diff options
Diffstat (limited to 'hack')
89 files changed, 22854 insertions, 0 deletions
diff --git a/hack/COPYRIGHT b/hack/COPYRIGHT new file mode 100644 index 0000000..71a9449 --- /dev/null +++ b/hack/COPYRIGHT @@ -0,0 +1,6 @@ +This entire subtree is copyright the Stichting Mathematisch Centrum. +The following copyright notice applies to all files found here. None of +these files contain AT&T proprietary source code. +_____________________________________________________________________________ + +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ diff --git a/hack/Makefile b/hack/Makefile new file mode 100644 index 0000000..53b4975 --- /dev/null +++ b/hack/Makefile @@ -0,0 +1,52 @@ +# @(#)Makefile 8.1 (Berkeley) 5/31/93 +# $FreeBSD: src/games/hack/Makefile,v 1.20.2.4 2002/08/07 16:31:41 ru Exp $ +# $DragonFly: src/games/hack/Makefile,v 1.6 2006/10/08 16:22:35 pavalos Exp $ + +# 20150209: hackery with makedefs and hack.onames.h to +# make Slackware's old pmake do this right. + +PROG= hack +SRCS= alloc.c hack.Decl.c hack.apply.c hack.bones.c hack.c hack.cmd.c \ + hack.do.c hack.do_name.c hack.do_wear.c hack.dog.c hack.eat.c \ + hack.end.c hack.engrave.c hack.fight.c hack.invent.c hack.ioctl.c \ + hack.lev.c hack.main.c hack.makemon.c hack.mhitu.c hack.mklev.c \ + hack.mkmaze.c hack.mkobj.c hack.mkshop.c hack.mon.c hack.monst.c \ + hack.o_init.c hack.objnam.c hack.options.c hack.pager.c hack.potion.c \ + hack.pri.c hack.read.c hack.rip.c hack.rumors.c hack.save.c \ + hack.search.c hack.shk.c hack.shknam.c hack.steal.c hack.termcap.c \ + hack.timeout.c hack.topl.c hack.track.c hack.trap.c hack.tty.c \ + hack.u_init.c hack.unix.c hack.vault.c hack.version.c hack.wield.c \ + hack.wizard.c hack.worm.c hack.worn.c hack.zap.c rnd.c \ + hack.onames.h +MAN= hack.6 +DPADD= ${LIBTERMCAP} +LDADD= -ltermcap -lbsd +CFLAGS+= -I${.CURDIR} -I. +FILES= rumors help hh data +FILESMODE_rumors= 440 +FILESGRP= ${BINGRP} +FILESDIR= /var/games/hackdir +HIDEGAME=hidegame +CLEANFILES=hack.onames.h makedefs makedefs.no + +build-tools: makedefs + +alloc.c: hack.onames.h + +hack.onames.h: makedefs def.objects.h + ./makedefs ${.CURDIR}/def.objects.h > hack.onames.h + +makedefs: makedefs.c + +beforeinstall: + mkdir -p ${DESTDIR}/var/games/hackdir + ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \ + ${DESTDIR}/var/games/hackdir/perm +.if !exists(${DESTDIR}/var/games/hackdir/record) + ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m 460 /dev/null \ + ${DESTDIR}/var/games/hackdir/record +.endif +# rm -f ${DESTDIR}/var/games/hackdir/bones* \ +# ${DESTDIR}/var/games/hackdir/save/* + +.include <bsd.prog.mk> diff --git a/hack/Makequest b/hack/Makequest new file mode 100644 index 0000000..9271c28 --- /dev/null +++ b/hack/Makequest @@ -0,0 +1,196 @@ +# Hack or Quest Makefile. + +# on some systems the termcap library is in -ltermcap +TERMLIB = -ltermlib + + +# make hack +GAME = quest +GAMEDIR = /usr/games/lib/questdir +CFLAGS = -g -DQUEST +HACKCSRC = hack.Decl.c\ + hack.apply.c hack.bones.c hack.c hack.cmd.c hack.do.c\ + hack.do_name.c hack.do_wear.c hack.dog.c hack.eat.c hack.end.c\ + hack.engrave.c hack.fight.c hack.invent.c hack.ioctl.c\ + hack.lev.c hack.main.c hack.makemon.c hack.mhitu.c\ + hack.mklev.c hack.mkmaze.c hack.mkobj.c hack.mkshop.c\ + hack.mon.c hack.monst.c hack.o_init.c hack.objnam.c\ + hack.options.c hack.pager.c hack.potion.c hack.pri.c\ + hack.read.c hack.rip.c hack.rumors.c hack.save.c\ + hack.search.c hack.shk.c hack.shknam.c hack.steal.c\ + hack.termcap.c hack.timeout.c hack.topl.c\ + hack.track.c hack.trap.c hack.tty.c hack.unix.c\ + hack.u_init.c hack.vault.c\ + hack.wield.c hack.wizard.c hack.worm.c hack.worn.c hack.zap.c\ + hack.version.c rnd.c alloc.c + +CSOURCES = $(HACKCSRC) makedefs.c + +HSOURCES = hack.h hack.mfndpos.h config.h\ + def.edog.h def.eshk.h def.flag.h def.func_tab.h def.gold.h\ + def.mkroom.h\ + def.monst.h def.obj.h def.objclass.h def.objects.h\ + def.permonst.h def.rm.h def.trap.h def.wseg.h + +SOURCES = $(CSOURCES) $(HSOURCES) + +AUX = data help hh rumors hack.6 hack.sh + +DISTR = $(SOURCES) $(AUX) READ_ME Makefile date.h hack.onames.h + +HOBJ = hack.Decl.o hack.apply.o hack.bones.o hack.o hack.cmd.o hack.do.o\ + hack.do_name.o hack.do_wear.o hack.dog.o hack.eat.o hack.end.o\ + hack.engrave.o hack.fight.o hack.invent.o hack.ioctl.o\ + hack.lev.o hack.main.o hack.makemon.o hack.mhitu.o hack.mklev.o\ + hack.mkmaze.o hack.mkobj.o hack.mkshop.o hack.mon.o\ + hack.monst.o hack.o_init.o hack.objnam.o hack.options.o\ + hack.pager.o hack.potion.o hack.pri.o\ + hack.read.o hack.rip.o hack.rumors.o hack.save.o\ + hack.search.o hack.shk.o hack.shknam.o hack.steal.o\ + hack.termcap.o hack.timeout.o hack.topl.o\ + hack.track.o hack.trap.o\ + hack.tty.o hack.unix.o hack.u_init.o hack.vault.o hack.wield.o\ + hack.wizard.o hack.worm.o hack.worn.o hack.zap.o\ + hack.version.o rnd.o alloc.o + +$(GAME): $(HOBJ) Makefile + @echo "Loading ..." + @ld -X -o $(GAME) /lib/crt0.o $(HOBJ) $(TERMLIB) -lc + +all: $(GAME) lint + @echo "Done." + +makedefs: makedefs.c + cc -o makedefs makedefs.c + + +hack.onames.h: makedefs def.objects.h + makedefs > hack.onames.h + +lint: +# lint cannot have -p here because (i) capitals are meaningful: +# [Ww]izard, (ii) identifiers may coincide in the first six places: +# doweararm() versus dowearring(). +# _flsbuf comes from <stdio.h>, a bug in the system libraries. + @echo lint -axbh -DLINT ... + @lint -axbh -DLINT $(HACKCSRC) | sed '/_flsbuf/d' + + +diff: + @- for i in $(SOURCES) $(AUX) ; do \ + cmp -s $$i $D/$$i || \ + ( echo diff $D/$$i $$i ; diff $D/$$i $$i ; echo ) ; done + +distribution: Makefile + @- for i in READ_ME $(SOURCES) $(AUX) Makefile date.h hack.onames.h\ + ; do \ + cmp -s $$i $D/$$i || \ + ( echo cp $$i $D ; cp $$i $D ) ; done +# the distribution directory also contains the empty files perm and record. + + +install: + rm -f $(GAMEDIR)/$(GAME) + cp $(GAME) $(GAMEDIR)/$(GAME) + chmod 04511 $(GAMEDIR)/$(GAME) + rm -f $(GAMEDIR)/bones* +# cp hack.6 /usr/man/man6 + +clean: + rm -f *.o + + +depend: +# For the moment we are lazy and disregard /usr/include files because +# the sources contain them conditionally. Perhaps we should use cpp. +# ( /bin/grep '^#[ ]*include' $$i | sed -n \ +# -e 's,<\(.*\)>,"/usr/include/\1",' \ +# + for i in ${CSOURCES}; do \ + ( /bin/grep '^#[ ]*include[ ]*"' $$i | sed -n \ + -e 's/[^"]*"\([^"]*\)".*/\1/' \ + -e H -e '$$g' -e '$$s/\n/ /g' \ + -e '$$s/.*/'$$i': &/' -e '$$s/\.c:/.o:/p' \ + >> makedep); done + for i in ${HSOURCES}; do \ + ( /bin/grep '^#[ ]*include[ ]*"' $$i | sed -n \ + -e 's/[^"]*"\([^"]*\)".*/\1/' \ + -e H -e '$$g' -e '$$s/\n/ /g' \ + -e '$$s/.*/'$$i': &\ + touch '$$i/p \ + >> makedep); done + @echo '/^# DO NOT DELETE THIS LINE/+2,$$d' >eddep + @echo '$$r makedep' >>eddep + @echo 'w' >>eddep + @cp Makefile Makefile.bak + ed - Makefile < eddep + @rm -f eddep makedep + @echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile + @echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile + @echo '# see make depend above' >> Makefile + - diff Makefile Makefile.bak + @rm -f Makefile.bak + +# DO NOT DELETE THIS LINE + +hack.Decl.o: hack.h def.mkroom.h +hack.apply.o: hack.h def.edog.h def.mkroom.h +hack.bones.o: hack.h +hack.o: hack.h +hack.cmd.o: hack.h def.func_tab.h +hack.do.o: hack.h +hack.do_name.o: hack.h +hack.do_wear.o: hack.h +hack.dog.o: hack.h hack.mfndpos.h def.edog.h def.mkroom.h +hack.eat.o: hack.h +hack.end.o: hack.h +hack.engrave.o: hack.h +hack.fight.o: hack.h +hack.invent.o: hack.h def.wseg.h +hack.ioctl.o: config.h +hack.lev.o: hack.h def.mkroom.h def.wseg.h +hack.main.o: hack.h +hack.makemon.o: hack.h +hack.mhitu.o: hack.h +hack.mklev.o: hack.h def.mkroom.h +hack.mkmaze.o: hack.h def.mkroom.h +hack.mkobj.o: hack.h +hack.mkshop.o: hack.h def.mkroom.h def.eshk.h +hack.mon.o: hack.h hack.mfndpos.h +hack.monst.o: hack.h def.eshk.h +hack.o_init.o: config.h def.objects.h hack.onames.h +hack.objnam.o: hack.h +hack.options.o: config.h hack.h +hack.pager.o: hack.h +hack.potion.o: hack.h +hack.pri.o: hack.h def.wseg.h +hack.read.o: hack.h +hack.rip.o: hack.h +hack.rumors.o: hack.h +hack.save.o: hack.h +hack.search.o: hack.h +hack.shk.o: hack.h hack.mfndpos.h def.mkroom.h def.eshk.h +hack.shknam.o: hack.h +hack.steal.o: hack.h +hack.termcap.o: config.h def.flag.h +hack.timeout.o: hack.h +hack.topl.o: hack.h +hack.track.o: hack.h +hack.trap.o: hack.h def.mkroom.h +hack.tty.o: hack.h +hack.unix.o: hack.h def.mkroom.h +hack.u_init.o: hack.h +hack.vault.o: hack.h def.mkroom.h +hack.wield.o: hack.h +hack.wizard.o: hack.h +hack.worm.o: hack.h def.wseg.h +hack.worn.o: hack.h +hack.zap.o: hack.h +hack.version.o: date.h +hack.h: config.h def.objclass.h def.monst.h def.gold.h def.trap.h def.obj.h def.flag.h def.rm.h def.permonst.h hack.onames.h + touch hack.h +def.objects.h: config.h def.objclass.h + touch def.objects.h +# DEPENDENCIES MUST END AT END OF FILE +# IF YOU PUT STUFF HERE IT WILL GO AWAY +# see make depend above diff --git a/hack/OWNER b/hack/OWNER new file mode 100644 index 0000000..be2d1e5 --- /dev/null +++ b/hack/OWNER @@ -0,0 +1,2 @@ +Andries Brouwer +mcvax!aeb diff --git a/hack/Original_READ_ME b/hack/Original_READ_ME new file mode 100644 index 0000000..81ca07a --- /dev/null +++ b/hack/Original_READ_ME @@ -0,0 +1,61 @@ +This is export hack, my first semester programming project. + +To set it up for your system, you will have to do the following: + 1: create a hack uid, to own the top ten list, etc. + 2: create a hack directory "/usr/lib/game/hack" is the default. + 2.5: make the directory 700 mode. /* sav files go in there...*/ + 3: modify hack.main.c to use the new directory. + 4: modify hack.main.c so it uses the new hack gid. Gid accounts can +go into magic mode without the password, can get cores with ^G, etc. +(make sure gid isn't checked anywhere else...) + 5: recompile hack. + 6: put it in games after making it set-uid hack. + 8: fix the bugs I undoubtedly left in it. + 9: tell me what you think of it. + + Hack uses the UCB file /etc/termcap to get your terminal escape codes. +If you don't use it, you will have to make extensive changes to hack.pri.c + +If you find any bugs (That you think I don't know about), or have any +awesome new changes (Like a better save (One that works!)), or have ANY +questions, write me + Jay Fenlason + 29 East St. + Sudbury Mass. + 01776 + +or call me at (617) 443-5036. Since I have both a modem and a teen-age +sister, Good Luck. + + +Hack is split (roughly) into several source files that do different things. +I have tried to fit all the procedures having to do with a certain segment +of the game into a single file, but the job is not the best in the world. +The rough splits are: + +hack.c General random stuff and things I never got around to moving. +hack.main.c main() and other random procedures, also the lock file stuff. +hack.mon.c Monsters, moving, attacking, etc. +hack.do.c drink, eat, read, wield, save, etc. +hack.do1.c zap, wear, remove, etc... +hack.pri.c stuff having to do with the screen, most of the terminal + independent stuff is in here. +hack.lev.c temp files and calling of mklev. + +Because of the peculiar restraints on our system, I make mklev (create +a level) a separate procedure execd by hack when needed. The source for +mklev is (Naturaly) mklev.c. You may want to put mklev back into hack. +Good luck. + +Most of hack was written by me, with help from + Kenny Woodland (KW) (general random things including + the original BUZZ()) + Mike Thome (MT) (The original chamelian) + and Jon Payne (JP) (The original lock file kludge and + the massive CURS()) + +This entire program would not have been possible without the SFSU Logo +Workshop. I am eternally grateful to all of our students (Especially K.L.), +without whom I would never have seen Rogue. I am especially grateful to +Mike Clancy, without whose generous help I would never have gotten to play +ROGUE. diff --git a/hack/READ_ME b/hack/READ_ME new file mode 100644 index 0000000..cfe6ca2 --- /dev/null +++ b/hack/READ_ME @@ -0,0 +1,92 @@ +Hack is a display oriented dungeons & dragons - like game. +Both display and command structure resemble rogue. +(For a game with the same structure but entirely different display - +a real cave instead of dull rectangles - try Quest) + +Hack was originally written by Jay Fenlason (at lincolnsudbury: + 29 East St., Sudbury Mass., 01776) with help from + Kenny Woodland, Mike Thome and Jon Payne. +Basically it was an implementation of Rogue, however, with 52+ instead of 26 + monster types. +The current version is more than thrice as large (with such new features as + the dog, the long worms, the shops, etc.) and almost entirely rewritten + (only the display routines are the original ones - I must rewrite these + too one day; especially when you are blind strange things still happen). + +Files for hack: + hack The actual game + record Top 100 list (just start with an empty file) + news Tells about recent changes in hack, or bugs found ... + (Just start with no news file.) + data Auxiliary file used by hack to give you the names + and sometimes some more information on the + objects and monsters. + help Introductory information (no doubt outdated). + hh Compactified version of help. + perm An empty file used for locking purposes. + rumors Texts for fortune cookies. + (Some of these contain information on the game, + others are just plain stupid. Additional rumors + are appreciated.) + hack.sh A shell script. + (We have hack.sh in /usr/games/hack and + hack in /usr/games/lib/hackdir/hack and all the other + hack stuff in /usr/games/lib/hackdir - perhaps this + will make the script clear. + There is no need for you to use it.) + READ_ME This file. + Original_READ_ME Jay Fenlason's READ_ME + +System files used: + /etc/termcap Used in conjunction with the environment variable + $TERM. + /bin/cat + /usr/ucb/more + /bin/sh Used when $SHELL is undefined. + +How to install hack: +0. Compile the sources. Perhaps you should first look at the file config.h + and define BSD if you are on a BSDtype system, + define STUPID if your C-compiler chokes on complicated expressions. + Make sure schar and uchar represent signed and unsigned types. + If your C compiler doesnt allow initialization of bit fields + change Bitfield. When config.h looks reasonable, say 'make'. + (Perhaps you have to change TERMLIB in the makefile.) +1. If it didnt exist already, introduce a loginname `play' . +2. The program hack resides in a directory so that it is executable + for everybody and is suid play: + ---s--s--x 1 play 206848 Apr 3 00:17 hack + Perhaps you wish to restrict playing to certain hours, or have games + running under nice; in that case you might write a program play.c + such that the program play is suid play and executable for everybody + while all the games in /usr/games are readable or executable for + play only; all the program play does is asking for the name of a game, + checking that time-of-day and system load do not forbid playing, + and then executing the game. Thus: + -r-sr-sr-x 1 play 13312 May 24 12:52 play + ---x------ 1 play 206848 Apr 3 00:17 hack + If you are worried about security you might let play do + chroot("/usr/games") so that no player can get access to the rest + of the system via shell escapes and the likes. + If you #define SECURE in config.h then hack will not setuid(getuid()) + before executing a chdir(). Hack will always do setuid(getuid()) with + a fork. If you do not define UNIX then hack will not fork. +3. The rest of the stuff belonging to hack sits in a subdirectory hackdir + (on our system /usr/games/lib/hackdir) with modes + drwx------ 3 play 1024 Aug 9 09:03 hackdir + Here all the temporary files will be created (with names like xlock.17 + or user.5). +4. If you are not really short on file space, creating a subdirectory + hackdir/save (modes again drwx------) will enable users to save their + unfinished games. + +The program hack is called +$ hack [-d hackdir] [maxnrofplayers] +(for playing) or +$ hack [-d hackdir] -s [listofusers | limit | all] +(for seeing part of the scorelist). +The shell file hack (in this kit called hack.sh) takes care of +calling hack with the right arguments. + +Send complaints, bug reports, suggestions for improvements to +mcvax!aeb - in real life Andries Brouwer. diff --git a/hack/alloc.c b/hack/alloc.c new file mode 100644 index 0000000..5d72997 --- /dev/null +++ b/hack/alloc.c @@ -0,0 +1,38 @@ +/* alloc.c - version 1.0.2 */ +/* $FreeBSD: src/games/hack/alloc.c,v 1.4 1999/11/16 02:57:01 billf Exp $ */ +/* $DragonFly: src/games/hack/alloc.c,v 1.4 2006/08/21 19:45:32 pavalos Exp $ */ + +#include "hack.h" + +#ifdef LINT + +/* + a ridiculous definition, suppressing + "possible pointer alignment problem" for (long *) malloc() + "enlarg defined but never used" + "ftell defined (in <stdio.h>) but never used" + from lint +*/ +long * +alloc(size_t n) +{ + long dummy = ftell(stderr); + + if (n) + dummy = 0; /* make sure arg is used */ + return (&dummy); +} + +#else + +void * +alloc(size_t lth) +{ + void *ptr; + + if ((ptr = malloc(lth)) == NULL) + panic("Cannot get %zd bytes", lth); + return (ptr); +} + +#endif /* LINT */ diff --git a/hack/config.h b/hack/config.h new file mode 100644 index 0000000..2173dcd --- /dev/null +++ b/hack/config.h @@ -0,0 +1,124 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* config.h - version 1.0.3 */ +/* $DragonFly: src/games/hack/config.h,v 1.3 2006/08/21 19:45:32 pavalos Exp $ */ + +#include <stdbool.h> +#include "pathnames.h" + +#ifndef CONFIG /* make sure the compiler doesnt see the typedefs twice */ + +#define CONFIG +#define UNIX /* delete if no fork(), exec() available */ +#define CHDIR /* delete if no chdir() available */ + +/* #define STUPID */ /* avoid some complicated expressions if + your C compiler chokes on them */ +/* #define PYRAMID_BUG */ /* avoid a bug on the Pyramid */ +/* #define NOWAITINCLUDE */ /* neither <wait.h> nor <sys/wait.h> exists */ + +#define WIZARD "bruno" /* the person allowed to use the -D option */ +#define RECORD "record"/* the file containing the list of topscorers */ +#define NEWS "news" /* the file containing the latest hack news */ +#define HELP "help" /* the file containing a description of the commands */ +#define SHELP "hh" /* abbreviated form of the same */ +#define RUMORFILE "rumors" /* a file with fortune cookies */ +#define DATAFILE "data" /* a file giving the meaning of symbols used */ +#define FMASK 0660 /* file creation mask */ +#define HLOCK "perm" /* an empty file used for locking purposes */ +#define LLOCK "safelock" /* link to previous */ + +#ifdef UNIX +/* + * Define DEF_PAGER as your default pager, e.g. "/bin/cat" or "/usr/ucb/more" + * If defined, it can be overridden by the environment variable PAGER. + * Hack will use its internal pager if DEF_PAGER is not defined. + * (This might be preferable for security reasons.) + * #define DEF_PAGER ".../mydir/mypager" + */ + +/* + * If you define MAIL, then the player will be notified of new mail + * when it arrives. If you also define DEF_MAILREADER then this will + * be the default mail reader, and can be overridden by the environment + * variable MAILREADER; otherwise an internal pager will be used. + * A stat system call is done on the mailbox every MAILCKFREQ moves. + */ +/* #define MAIL */ +#define DEF_MAILREADER _PATH_MAIL /* or e.g. /bin/mail */ +#define MAILCKFREQ 100 + + +#define SHELL /* do not delete the '!' command */ +#define SUSPEND /* let ^Z suspend the game */ +#endif /* UNIX */ + +#ifdef CHDIR +/* + * If you define HACKDIR, then this will be the default playground; + * otherwise it will be the current directory. + */ +#ifdef QUEST +#define HACKDIR _PATH_QUEST +#else /* QUEST */ +#define HACKDIR _PATH_HACK +#endif /* QUEST */ + +/* + * Some system administrators are stupid enough to make Hack suid root + * or suid daemon, where daemon has other powers besides that of reading or + * writing Hack files. In such cases one should be careful with chdir's + * since the user might create files in a directory of his choice. + * Of course SECURE is meaningful only if HACKDIR is defined. + */ +#define SECURE /* do setuid(getuid()) after chdir() */ + +/* + * If it is desirable to limit the number of people that can play Hack + * simultaneously, define HACKDIR, SECURE and MAX_NR_OF_PLAYERS. + * #define MAX_NR_OF_PLAYERS 100 + */ +#endif /* CHDIR */ + +/* size of terminal screen is (at least) (ROWNO+2) by COLNO */ +#define COLNO 80 +#define ROWNO 22 + +/* + * small signed integers (8 bits suffice) + * typedef char schar; + * will do when you have signed characters; otherwise use + * typedef short int schar; + */ +typedef short schar; + +/* + * small unsigned integers (8 bits suffice - but 7 bits do not) + * - these are usually object types; be careful with inequalities! - + * typedef unsigned char uchar; + * will be satisfactory if you have an "unsigned char" type; otherwise use + * typedef unsigned short int uchar; + */ +typedef unsigned char uchar; + +/* + * small integers in the range 0 - 127, usually coordinates + * although they are nonnegative they must not be declared unsigned + * since otherwise comparisons with signed quantities are done incorrectly + */ +typedef schar xchar; +typedef bool boolean; /* 0 or 1 */ +#define TRUE 1 +#define FALSE 0 + +/* + * Declaration of bitfields in various structs; if your C compiler + * doesnt handle bitfields well, e.g., if it is unable to initialize + * structs containing bitfields, then you might use + * #define Bitfield(x,n) uchar x + * since the bitfields used never have more than 7 bits. (Most have 1 bit.) + */ +#define Bitfield(x,n) unsigned x:n + +#define SIZE(x) (int)(sizeof(x) / sizeof(x[0])) + +#endif /* CONFIG */ diff --git a/hack/data b/hack/data new file mode 100644 index 0000000..5faa384 --- /dev/null +++ b/hack/data @@ -0,0 +1,232 @@ + Hack & Quest data file - version 1.0.3 +@ human (or you) +- a wall +| a wall ++ a door +. the floor of a room + a dark part of a room +# a corridor +} water filled area +< the staircase to the previous level +> the staircase to the next level +^ a trap +$ a pile, pot or chest of gold +%% a piece of food +! a potion +* a gem +? a scroll += a ring +/ a wand +[ a suit of armor +) a weapon +( a useful item (camera, key, rope etc.) +0 an iron ball +_ an iron chain +` an enormous rock +" an amulet +, a trapper +: a chameleon +; a giant eel +' a lurker above +& a demon +A a giant ant +B a giant bat +C a centaur; + Of all the monsters put together by the Greek imagination + the Centaurs (Kentauroi) constituted a class in themselves. + Despite a strong streak of sensuality in their make-up, + their normal behaviour was moral, and they took a kindly + thought of man's welfare. The attempted outrage of Nessos on + Deianeira, and that of the whole tribe of Centaurs on the + Lapith women, are more than offset by the hospitality of + Pholos and by the wisdom of Cheiron, physician, prophet, + lyrist, and the instructor of Achilles. Further, the Cen- + taurs were peculiar in that their nature, which united the + body of a horse with the trunk and head of a man, involved + an unthinkable duplication of vital organs and important + members. So grotesque a combination seems almost un-Greek. + These strange creatures were said to live in the caves and + clefts of the mountains, myths associating them especially + with the hills of Thessaly and the range of Erymanthos. + [Mythology of all races, Vol. 1, pp. 270-271] +D a dragon; + In the West the dragon was the natural enemy of man. Although + preferring to live in bleak and desolate regions, whenever it was + seen among men it left in its wake a trail of destruction and + disease. Yet any attempt to slay this beast was a perilous under- + taking. For the dragon's assailant had to contend not only with + clouds of sulphurous fumes pouring from its fire-breathing nos- + trils, but also with the thrashings of its tail, the most deadly + part of its serpent-like body. + [From: Mythical Beasts by Deirdre Headon (The Leprechaun Library)] +E a floating eye +F a freezing sphere +G a gnome; + ... And then a gnome came by, carrying a bundle, an old fellow + three times as large as an imp and wearing clothes of a sort, + especially a hat. And he was clearly just as frightened as the + imps though he could not go so fast. Ramon Alonzo saw that there + must be some great trouble that was vexing magical things; and, + since gnomes speak the language of men, and will answer if spoken + to gently, he raised his hat, and asked of the gnome his name. + The gnome did not stop his hasty shuffle a moment as he answered + 'Alaraba' and grabbed the rim of his hat but forgot to doff it. + 'What is the trouble, Alaraba?' said Ramon Alonzo. + 'White magic. Run!' said the gnome ... + [From: The Charwoman's Shadow, by Lord Dunsany.] +H a hobgoblin; + Hobgoblin. Used by the Puritans and in later times for + wicked goblin spirits, as in Bunyan's 'Hobgoblin nor foul + friend', but its more correct use is for the friendly spir- + its of the brownie type. In 'A midsummer night's dream' a + fairy says to Shakespeare's Puck: + Those that Hobgoblin call you, and sweet Puck, + You do their work, and they shall have good luck: + Are you not he? + and obviously Puck would not wish to be called a hobgoblin + if that was an ill-omened word. + Hobgoblins are on the whole, good-humoured and ready to be + helpful, but fond of practical joking, and like most of the + fairies rather nasty people to annoy. Boggarts hover on the + verge of hobgoblindom. Bogles are just over the edge. + One Hob mentioned by Henderson, was Hob Headless who haunted + the road between Hurworth and Neasham, but could not cross + the little river Kent, which flowed into the Tess. He was + exorcised and laid under a large stone by the roadside for + ninety-nine years and a day. If anyone was so unwary as to + sit on that stone, he would be unable to quit it for ever. + The ninety-nine years is nearly up, so trouble may soon be + heard of on the road between Hurworth and Neasham. + [Katharine Briggs, A dictionary of Fairies] +I an invisible stalker +J a jackal +K a kobold +L a leprechaun; + The Irish Leprechaun is the Faeries' shoemaker and is known + under various names in different parts of Ireland: Cluri- + caune in Cork, Lurican in Kerry, Lurikeen in Kildare and Lu- + rigadaun in Tipperary. Although he works for the Faeries, + the Leprechaun is not of the same species. He is small, has + dark skin and wears strange clothes. His nature has some- + thing of the manic-depressive about it: first he is quite + happy, whistling merrily as he nails a sole on to a shoe; a + few minutes later, he is sullen and morose, drunk on his + home-made heather ale. The Leprechaun's two great loves are + tobacco and whiskey, and he is a first-rate con-man, impos- + sible to out-fox. No one, no matter how clever, has ever + managed to cheat him out of his hidden pot of gold or his + magic shilling. At the last minute he always thinks of some + way to divert his captor's attention and vanishes in the + twinkling of an eye. + [From: A Field Guide to the Little People + by Nancy Arrowsmith & George Moorse. ] +M a mimic +N a nymph +O an orc +P a purple worm +Q a quasit +R a rust monster +S a snake +T a troll +U an umber hulk +V a vampire +W a wraith +X a xorn +Y a yeti +Z a zombie +a an acid blob +b a giant beetle +c a cockatrice; + Once in a great while, when the positions of the stars are + just right, a seven-year-old rooster will lay an egg. Then, + along will come a snake, to coil around the egg, or a toad, + to squat upon the egg, keeping it warm and helping it to + hatch. When it hatches, out comes a creature called basil- + isk, or cockatrice, the most deadly of all creatures. A sin- + gle glance from its yellow, piercing toad's eyes will kill + both man and beast. Its power of destruction is said to be + so great that sometimes simply to hear its hiss can prove + fatal. Its breath is so venomous that it causes all vege- + tation to wither. + There is, however, one creature which can withstand the + basilisk's deadly gaze, and this is the weasel. No one knows + why this is so, but although the fierce weasel can slay the + basilisk, it will itself be killed in the struggle. Perhaps + the weasel knows the basilisk's fatal weakness: if it ever + sees its own reflection in a mirror it will perish instant- + ly. But even a dead basilisk is dangerous, for it is said + that merely touching its lifeless body can cause a person to + sicken and die. + [From: Mythical Beasts by Deirdre Headon (The Leprechaun + Library) and other sources. ] +d a dog +e an ettin +f a fog cloud +g a gelatinous cube +h a homunculus +i an imp; + ... imps ... little creatures of two feet high that could + gambol and jump prodigiously; ... + [From: The Charwoman's Shadow, by Lord Dunsany.] + + An 'imp' is an off-shoot or cutting. Thus an 'ymp tree' was + a grafted tree, or one grown from a cutting, not from seed. + 'Imp' properly means a small devil, an off-shoot of Satan, + but the distinction between goblins or bogles and imps from + hell is hard to make, and many in the Celtic countries as + well as the English Puritans regarded all fairies as devils. + The fairies of tradition often hover uneasily between the + ghostly and the diabolic state. + [Katharine Briggs, A dictionary of Fairies] +j a jaguar +k a killer bee +l a leocrotta +m a minotaur +n a nurse +o an owlbear +p a piercer +q a quivering blob +r a giant rat +s a scorpion +t a tengu; + The tengu was the most troublesome creature of Japanese + legend. Part bird and part man, with red beak for a nose + and flashing eyes, the tengu was notorious for stirring up + feuds and prolonging enmity between families. Indeed, the + belligerent tengus were supposed to have been man's first + instructors in the use of arms. + [From: Mythical Beasts by Deirdre Headon + (The Leprechaun Library). ] +u a unicorn; + Men have always sought the elusive unicorn, for the single + twisted horn which projected from its forehead was thought + to be a powerful talisman. It was said that the unicorn had + simply to dip the tip of its horn in a muddy pool for the + water to become pure. Men also believed that to drink from + this horn was a protection against all sickness, and that if + the horn was ground to a powder it would act as an antidote + to all poisons. Less than 200 years ago in France, the horn + of a unicorn was used in a ceremony to test the royal food + for poison. + Although only the size of a small horse, the unicorn is a + very fierce beast, capable of killing an elephant with a + single thrust from its horn. Its fleetness of foot also + makes this solitary creature difficult to capture. However, + it can be tamed and captured by a maiden. Made gentle by the + sight of a virgin, the unicorn can be lured to lay its head + in her lap, and in this docile mood, the maiden may secure + it with a golden rope. + [From: Mythical Beasts by Deirdre Headon + (The Leprechaun Library). ] +v a violet fungi +w a long worm; + From its teeth the crysknife can be manufactured. +~ the tail of a long worm +x a xan; + The xan were animals sent to prick the legs of the Lords of Xibalba. +y a yellow light +z a zruty; + The zruty are wild and gigantic beings, living in the wildernesses + of the Tatra mountains. +1 The wizard of Yendor +2 The mail daemon diff --git a/hack/date.h b/hack/date.h new file mode 100644 index 0000000..9a7ef76 --- /dev/null +++ b/hack/date.h @@ -0,0 +1,2 @@ + +char datestring[] = "Tue Jul 23 1985"; diff --git a/hack/def.edog.h b/hack/def.edog.h new file mode 100644 index 0000000..a5c2b46 --- /dev/null +++ b/hack/def.edog.h @@ -0,0 +1,12 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* def.edog.h - version 1.0.2 */ + +struct edog { + long hungrytime; /* at this time dog gets hungry */ + long eattime; /* dog is eating */ + long droptime; /* moment dog dropped object */ + unsigned dropdist; /* dist of drpped obj from @ */ + unsigned apport; /* amount of training */ + long whistletime; /* last time he whistled */ +}; +#define EDOG(mp) ((struct edog *)(&(mp->mextra[0]))) diff --git a/hack/def.eshk.h b/hack/def.eshk.h new file mode 100644 index 0000000..2ebf280 --- /dev/null +++ b/hack/def.eshk.h @@ -0,0 +1,24 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* def.eshk.h - version 1.0.2 : added 'following' */ + +#define BILLSZ 200 +struct bill_x { + unsigned bo_id; + unsigned useup:1; + unsigned bquan:7; + unsigned price; /* price per unit */ +}; + +struct eshk { + long int robbed; /* amount stolen by most recent customer */ + boolean following; /* following customer since he owes us sth */ + schar shoproom; /* index in rooms; set by inshop() */ + coord shk; /* usual position shopkeeper */ + coord shd; /* position shop door */ + int shoplevel; /* level of his shop */ + int billct; + struct bill_x bill[BILLSZ]; + int visitct; /* nr of visits by most recent customer */ + char customer[PL_NSIZ]; /* most recent customer */ + char shknam[PL_NSIZ]; +}; diff --git a/hack/def.flag.h b/hack/def.flag.h new file mode 100644 index 0000000..221f33d --- /dev/null +++ b/hack/def.flag.h @@ -0,0 +1,42 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* def.flag.h - version 1.0.3 */ + +struct flag { + unsigned ident; /* social security number for each monster */ + unsigned debug:1; /* in debugging mode */ +#define wizard flags.debug + unsigned toplin:2; /* a top line (message) has been printed */ + /* 0: top line empty; 2: no --More-- reqd. */ + unsigned cbreak:1; /* in cbreak mode, rogue format */ + unsigned standout:1; /* use standout for --More-- */ + unsigned nonull:1; /* avoid sending nulls to the terminal */ + unsigned time:1; /* display elapsed 'time' */ + unsigned nonews:1; /* suppress news printing */ + unsigned notombstone:1; + unsigned end_top, end_around; /* describe desired score list */ + unsigned end_own:1; /* idem (list all own scores) */ + unsigned no_rest_on_space:1; /* spaces are ignored */ + unsigned beginner:1; + unsigned female:1; + unsigned invlet_constant:1; /* let objects keep their + inventory symbol */ + unsigned move:1; + unsigned mv:1; + unsigned run:3; /* 0: h (etc), 1: H (etc), 2: fh (etc) */ + /* 3: FH, 4: ff+, 5: ff-, 6: FF+, 7: FF- */ + unsigned nopick:1; /* do not pickup objects */ + unsigned echo:1; /* 1 to echo characters */ + unsigned botl:1; /* partially redo status line */ + unsigned botlx:1; /* print an entirely new bottom line */ + unsigned nscrinh:1; /* inhibit nscr() in pline(); */ + unsigned made_amulet:1; + unsigned no_of_wizards:2;/* 0, 1 or 2 (wizard and his shadow) */ + /* reset from 2 to 1, but never to 0 */ + unsigned moonphase:3; +#define NEW_MOON 0 +#define FULL_MOON 4 + +}; + +extern struct flag flags; + diff --git a/hack/def.func_tab.h b/hack/def.func_tab.h new file mode 100644 index 0000000..ceb952c --- /dev/null +++ b/hack/def.func_tab.h @@ -0,0 +1,13 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* def.func_tab.h - version 1.0.2 */ +/* $DragonFly: src/games/hack/def.func_tab.h,v 1.3 2006/08/21 19:45:32 pavalos Exp $ */ + +struct func_tab { + char f_char; + int (*f_funct)(void); +}; + +struct ext_func_tab { + const char *ef_txt; + int (*ef_funct)(void); +}; diff --git a/hack/def.gold.h b/hack/def.gold.h new file mode 100644 index 0000000..c279a45 --- /dev/null +++ b/hack/def.gold.h @@ -0,0 +1,12 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* def.gold.h - version 1.0.2 */ +/* $DragonFly: src/games/hack/def.gold.h,v 1.2 2006/08/21 19:45:32 pavalos Exp $ */ + +struct gold { + struct gold *ngold; + xchar gx,gy; + long amount; +}; + +extern struct gold *fgold; +#define newgold() alloc(sizeof(struct gold)) diff --git a/hack/def.mkroom.h b/hack/def.mkroom.h new file mode 100644 index 0000000..f57f34c --- /dev/null +++ b/hack/def.mkroom.h @@ -0,0 +1,26 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* def.mkroom.h - version 1.0.3 */ + +struct mkroom { + schar lx, hx, ly, hy; /* usually xchar, but hx may be -1 */ + schar rtype, rlit, doorct, fdoor; +}; + +#define MAXNROFROOMS 15 +extern struct mkroom rooms[MAXNROFROOMS + 1]; + +#define DOORMAX 100 +extern coord doors[DOORMAX]; + +/* various values of rtype */ +/* 0: ordinary room; 8-15: various shops */ +/* Note: some code assumes that >= 8 means shop, so be careful when adding + new roomtypes */ +#define SWAMP 3 +#define VAULT 4 +#define BEEHIVE 5 +#define MORGUE 6 +#define ZOO 7 +#define SHOPBASE 8 +#define WANDSHOP 9 +#define GENERAL 15 diff --git a/hack/def.monst.h b/hack/def.monst.h new file mode 100644 index 0000000..8424396 --- /dev/null +++ b/hack/def.monst.h @@ -0,0 +1,60 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* def.monst.h - version 1.0.2 */ +/* $DragonFly: src/games/hack/def.monst.h,v 1.3 2006/08/21 19:45:32 pavalos Exp $ */ + +struct monst { + struct monst *nmon; + struct permonst *data; + unsigned m_id; + xchar mx, my; + xchar mdx, mdy; /* if mdispl then pos where last displayed */ +#define MTSZ 4 + coord mtrack[MTSZ]; /* monster track */ + schar mhp, mhpmax; + char mappearance; /* nonzero for undetected 'M's and for '1's */ + Bitfield(mimic, 1); /* undetected mimic */ + Bitfield(mdispl, 1); /* mdx, mdy valid */ + Bitfield(minvis, 1); /* invisible */ + Bitfield(cham, 1); /* shape-changer */ + Bitfield(mhide, 1); /* hides beneath objects */ + Bitfield(mundetected, 1); /* not seen in present hiding place */ + Bitfield(mspeed, 2); + Bitfield(msleep, 1); + Bitfield(mfroz, 1); + Bitfield(mconf, 1); + Bitfield(mflee, 1); /* fleeing */ + Bitfield(mfleetim, 7); /* timeout for mflee */ + Bitfield(mcan, 1); /* has been cancelled */ + Bitfield(mtame, 1); /* implies peaceful */ + Bitfield(mpeaceful, 1); /* does not attack unprovoked */ + Bitfield(isshk, 1); /* is shopkeeper */ + Bitfield(isgd, 1); /* is guard */ + Bitfield(mcansee, 1); /* cansee 1, temp.blinded 0, blind 0 */ + Bitfield(mblinded, 7); /* cansee 0, temp.blinded n, blind 0 */ + Bitfield(mtrapped, 1); /* trapped in a pit or bear trap */ + Bitfield(mnamelth, 6); /* length of name (following mxlth) */ +#ifndef NOWORM + Bitfield(wormno, 5); /* at most 31 worms on any level */ +#endif /* NOWORM */ + unsigned mtrapseen; /* bitmap of traps we've been trapped in */ + long mlstmv; /* prevent two moves at once */ + struct obj *minvent; + long mgold; + unsigned mxlth; /* length of following data */ + /* in order to prevent alignment problems mextra should + be (or follow) a long int */ + long mextra[1]; /* monster dependent info */ +}; + +#define newmonst(xl) alloc((unsigned)(xl) + sizeof(struct monst)) + +extern struct monst *fmon; +extern struct monst *fallen_down; + +/* these are in mspeed */ +#define MSLOW 1 /* slow monster */ +#define MFAST 2 /* speeded monster */ + +#define NAME(mtmp) (((char *)mtmp->mextra) + mtmp->mxlth) +#define MREGEN "TVi1" +#define UNDEAD "ZVW " diff --git a/hack/def.obj.h b/hack/def.obj.h new file mode 100644 index 0000000..cbdf091 --- /dev/null +++ b/hack/def.obj.h @@ -0,0 +1,48 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* def.obj.h - version 1.0.3 */ + +struct obj { + struct obj *nobj; + unsigned o_id; + unsigned o_cnt_id; /* id of container object is in */ + xchar ox ,oy; + xchar odx, ody; + uchar otyp; + uchar owt; + uchar quan; /* use oextra for tmp gold objects */ + schar spe; /* quality of weapon, armor or ring (+ or -) + number of charges for wand ( >= -1 ) + special for uball and amulet %% BAH */ + char olet; + char invlet; + Bitfield(oinvis, 1); /* not yet implemented */ + Bitfield(odispl, 1); + Bitfield(known, 1); /* exact nature known */ + Bitfield(dknown, 1); /* color or text known */ + Bitfield(cursed, 1); + Bitfield(unpaid, 1); /* on some bill */ + Bitfield(rustfree, 1); + Bitfield(onamelth, 6); + long age; /* creation date */ + long owornmask; +#define W_ARM 01L +#define W_ARM2 02L +#define W_ARMH 04L +#define W_ARMS 010L +#define W_ARMG 020L +#define W_ARMOR (W_ARM | W_ARM2 | W_ARMH | W_ARMS | W_ARMG) +#define W_RINGL 010000L /* make W_RINGL = RING_LEFT (see uprop) */ +#define W_RINGR 020000L +#define W_RING (W_RINGL | W_RINGR) +#define W_WEP 01000L +#define W_BALL 02000L +#define W_CHAIN 04000L + long oextra[1]; /* used for name of ordinary objects - length + is flexible; amount for tmp gold objects */ +}; + +extern struct obj *fobj; + +#define newobj(xl) alloc((unsigned)(xl) + sizeof(struct obj)) +#define ONAME(otmp) ((char *)otmp->oextra) +#define OGOLD(otmp) (otmp->oextra[0]) diff --git a/hack/def.objclass.h b/hack/def.objclass.h new file mode 100644 index 0000000..e388554 --- /dev/null +++ b/hack/def.objclass.h @@ -0,0 +1,62 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* def.objclass.h - version 1.0.3 */ +/* $DragonFly: src/games/hack/def.objclass.h,v 1.3 2006/08/21 19:45:32 pavalos Exp $ */ + +#ifndef _DEF_OBJCLASS_H_ +#define _DEF_OBJCLASS_H_ +/* definition of a class of objects */ + +struct objclass { + const char *oc_name; /* actual name */ + const char *oc_descr; /* description when name unknown */ + char *oc_uname; /* called by user */ + Bitfield(oc_name_known, 1); + Bitfield(oc_merge, 1); /* merge otherwise equal objects */ + char oc_olet; + schar oc_prob; /* probability for mkobj() */ + schar oc_delay; /* delay when using such an object */ + uchar oc_weight; + schar oc_oc1, oc_oc2; + int oc_oi; +#define nutrition oc_oi /* for foods */ +#define a_ac oc_oc1 /* for armors - only used in ARM_BONUS */ +#define ARM_BONUS(obj) ((10 - objects[obj->otyp].a_ac) + obj->spe) +#define a_can oc_oc2 /* for armors */ +#define bits oc_oc1 /* for wands and rings */ + /* wands */ +#define NODIR 1 +#define IMMEDIATE 2 +#define RAY 4 + /* rings */ +#define SPEC 1 /* +n is meaningful */ +#define wldam oc_oc1 /* for weapons and PICK_AXE */ +#define wsdam oc_oc2 /* for weapons and PICK_AXE */ +#define g_val oc_oi /* for gems: value on exit */ +}; + +extern struct objclass objects[]; + +/* definitions of all object-symbols */ + +#define ILLOBJ_SYM '\\' +#define AMULET_SYM '"' +#define FOOD_SYM '%' +#define WEAPON_SYM ')' +#define TOOL_SYM '(' +#define BALL_SYM '0' +#define CHAIN_SYM '_' +#define ROCK_SYM '`' +#define ARMOR_SYM '[' +#define POTION_SYM '!' +#define SCROLL_SYM '?' +#define WAND_SYM '/' +#define RING_SYM '=' +#define GEM_SYM '*' +/* Other places with explicit knowledge of object symbols: + * hack.mklev.c: "=/)%?![<>"[rn2(9)]; + * hack.mkobj.c: char mkobjstr[] = "))[[!!!!????%%%%/=**))[[!!!!????%%%%/=**(%"; + * hack.apply.c: otmp = getobj("0#%", "put in"); + * hack.eat.c: otmp = getobj("%", "eat"); + * hack.invent.c: else if (strchr("!%?[()= /\"0", sym)) { + */ +#endif /* _DEF_OBJCLASS_H_ */ diff --git a/hack/def.objects.h b/hack/def.objects.h new file mode 100644 index 0000000..a2416fb --- /dev/null +++ b/hack/def.objects.h @@ -0,0 +1,290 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* def.objects.h - version 1.0.3 */ +/* $DragonFly: src/games/hack/def.objects.h,v 1.2 2006/08/21 19:45:32 pavalos Exp $ */ + +/* objects have letter " % ) ( 0 _ ` [ ! ? / = * */ +#include <string.h> +#include "config.h" +#include "def.objclass.h" + +struct objclass objects[] = { + + { "strange object", NULL, NULL, 1, 0, + ILLOBJ_SYM, 0, 0, 0, 0, 0, 0 }, + { "amulet of Yendor", NULL, NULL, 1, 0, + AMULET_SYM, 100, 0, 2, 0, 0, 0 }, + +#define FOOD(name,prob,delay,weight,nutrition) { name, NULL, NULL, 1, 1,\ + FOOD_SYM, prob, delay, weight, 0, 0, nutrition } + +/* dog eats foods 0-4 but prefers 1 above 0,2,3,4 */ +/* food 4 can be read */ +/* food 5 improves your vision */ +/* food 6 makes you stronger (like Popeye) */ +/* foods CORPSE up to CORPSE+52 are cadavers */ + + FOOD("food ration", 50, 5, 4, 800), + FOOD("tripe ration", 20, 1, 2, 200), + FOOD("pancake", 3, 1, 1, 200), + FOOD("dead lizard", 3, 0, 1, 40), + FOOD("fortune cookie", 7, 0, 1, 40), + FOOD("carrot", 2, 0, 1, 50), + FOOD("tin", 7, 0, 1, 0), + FOOD("orange", 1, 0, 1, 80), + FOOD("apple", 1, 0, 1, 50), + FOOD("pear", 1, 0, 1, 50), + FOOD("melon", 1, 0, 1, 100), + FOOD("banana", 1, 0, 1, 80), + FOOD("candy bar", 1, 0, 1, 100), + FOOD("egg", 1, 0, 1, 80), + FOOD("clove of garlic", 1, 0, 1, 40), + FOOD("lump of royal jelly", 0, 0, 1, 200), + + FOOD("dead human", 0, 4, 40, 400), + FOOD("dead giant ant", 0, 1, 3, 30), + FOOD("dead giant bat", 0, 1, 3, 30), + FOOD("dead centaur", 0, 5, 50, 500), + FOOD("dead dragon", 0, 15, 150, 1500), + FOOD("dead floating eye", 0, 1, 1, 10), + FOOD("dead freezing sphere", 0, 1, 1, 10), + FOOD("dead gnome", 0, 1, 10, 100), + FOOD("dead hobgoblin", 0, 2, 20, 200), + FOOD("dead stalker", 0, 4, 40, 400), + FOOD("dead jackal", 0, 1, 10, 100), + FOOD("dead kobold", 0, 1, 10, 100), + FOOD("dead leprechaun", 0, 4, 40, 400), + FOOD("dead mimic", 0, 4, 40, 400), + FOOD("dead nymph", 0, 4, 40, 400), + FOOD("dead orc", 0, 2, 20, 200), + FOOD("dead purple worm", 0, 7, 70, 700), + FOOD("dead quasit", 0, 2, 20, 200), + FOOD("dead rust monster", 0, 5, 50, 500), + FOOD("dead snake", 0, 1, 10, 100), + FOOD("dead troll", 0, 4, 40, 400), + FOOD("dead umber hulk", 0, 5, 50, 500), + FOOD("dead vampire", 0, 4, 40, 400), + FOOD("dead wraith", 0, 1, 1, 10), + FOOD("dead xorn", 0, 7, 70, 700), + FOOD("dead yeti", 0, 7, 70, 700), + FOOD("dead zombie", 0, 1, 3, 30), + FOOD("dead acid blob", 0, 1, 3, 30), + FOOD("dead giant beetle", 0, 1, 1, 10), + FOOD("dead cockatrice", 0, 1, 3, 30), + FOOD("dead dog", 0, 2, 20, 200), + FOOD("dead ettin", 0, 1, 3, 30), + FOOD("dead fog cloud", 0, 1, 1, 10), + FOOD("dead gelatinous cube", 0, 1, 10, 100), + FOOD("dead homunculus", 0, 2, 20, 200), + FOOD("dead imp", 0, 1, 1, 10), + FOOD("dead jaguar", 0, 3, 30, 300), + FOOD("dead killer bee", 0, 1, 1, 10), + FOOD("dead leocrotta", 0, 5, 50, 500), + FOOD("dead minotaur", 0, 7, 70, 700), + FOOD("dead nurse", 0, 4, 40, 400), + FOOD("dead owlbear", 0, 7, 70, 700), + FOOD("dead piercer", 0, 2, 20, 200), + FOOD("dead quivering blob", 0, 1, 10, 100), + FOOD("dead giant rat", 0, 1, 3, 30), + FOOD("dead giant scorpion", 0, 1, 10, 100), + FOOD("dead tengu", 0, 3, 30, 300), + FOOD("dead unicorn", 0, 3, 30, 300), + FOOD("dead violet fungi", 0, 1, 10, 100), + FOOD("dead long worm", 0, 5, 50, 500), +/* %% wt of long worm should be proportional to its length */ + FOOD("dead xan", 0, 3, 30, 300), + FOOD("dead yellow light", 0, 1, 1, 10), + FOOD("dead zruty", 0, 6, 60, 600), + +/* weapons ... - ROCK come several at a time */ +/* weapons ... - (ROCK-1) are shot using idem+(BOW-ARROW) */ +/* weapons AXE, SWORD, THSWORD are good for worm-cutting */ +/* weapons (PICK-)AXE, DAGGER, CRYSKNIFE are good for tin-opening */ +#define WEAPON(name,prob,wt,ldam,sdam) { name, NULL, NULL, 1, 0 /*%%*/,\ + WEAPON_SYM, prob, 0, wt, ldam, sdam, 0 } + + WEAPON("arrow", 7, 0, 6, 6), + WEAPON("sling bullet", 7, 0, 4, 6), + WEAPON("crossbow bolt", 7, 0, 4, 6), + WEAPON("dart", 7, 0, 3, 2), + WEAPON("rock", 6, 1, 3, 3), + WEAPON("boomerang", 2, 3, 9, 9), + WEAPON("mace", 9, 3, 6, 7), + WEAPON("axe", 6, 3, 6, 4), + WEAPON("flail", 6, 3, 6, 5), + WEAPON("long sword", 8, 3, 8, 12), + WEAPON("two handed sword", 6, 4, 12, 6), + WEAPON("dagger", 6, 3, 4, 3), + WEAPON("worm tooth", 0, 4, 2, 2), + WEAPON("crysknife", 0, 3, 10, 10), + WEAPON("spear", 6, 3, 6, 8), + WEAPON("bow", 6, 3, 4, 6), + WEAPON("sling", 5, 3, 6, 6), + WEAPON("crossbow", 6, 3, 4, 6), + + { "whistle", "whistle", NULL, 0, 0, + TOOL_SYM, 90, 0, 2, 0, 0, 0 }, + { "magic whistle", "whistle", NULL, 0, 0, + TOOL_SYM, 10, 0, 2, 0, 0, 0 }, + { "expensive camera", NULL, NULL, 1, 1, + TOOL_SYM, 0, 0, 3, 0, 0, 0 }, + { "ice box", "large box", NULL, 0, 0, + TOOL_SYM, 0, 0, 40, 0, 0, 0 }, + { "pick-axe", NULL, NULL, 1, 1, + TOOL_SYM, 0, 0, 5, 6, 3, 0 }, + { "can opener", NULL, NULL, 1, 1, + TOOL_SYM, 0, 0, 1, 0, 0, 0 }, + { "heavy iron ball", NULL, NULL, 1, 0, + BALL_SYM, 100, 0, 20, 0, 0, 0 }, + { "iron chain", NULL, NULL, 1, 0, + CHAIN_SYM, 100, 0, 20, 0, 0, 0 }, + { "enormous rock", NULL, NULL, 1, 0, + ROCK_SYM, 100, 0, 200 /* > MAX_CARR_CAP */, 0, 0, 0 }, + +#define ARMOR(name,prob,delay,ac,can) { name, NULL, NULL, 1, 0,\ + ARMOR_SYM, prob, delay, 8, ac, can, 0 } + ARMOR("helmet", 3, 1, 9, 0), + ARMOR("plate mail", 5, 5, 3, 2), + ARMOR("splint mail", 8, 5, 4, 1), + ARMOR("banded mail", 10, 5, 4, 0), + ARMOR("chain mail", 10, 5, 5, 1), + ARMOR("scale mail", 10, 5, 6, 0), + ARMOR("ring mail", 15, 5, 7, 0), + /* the armors below do not rust */ + ARMOR("studded leather armor", 13, 3, 7, 1), + ARMOR("leather armor", 17, 3, 8, 0), + ARMOR("elven cloak", 5, 0, 9, 3), + ARMOR("shield", 3, 0, 9, 0), + ARMOR("pair of gloves", 1, 1, 9, 0), + +#define POTION(name,color) { name, color, NULL, 0, 1,\ + POTION_SYM, 0, 0, 2, 0, 0, 0 } + + POTION("restore strength", "orange"), + POTION("booze", "bubbly"), + POTION("invisibility", "glowing"), + POTION("fruit juice", "smoky"), + POTION("healing", "pink"), + POTION("paralysis", "puce"), + POTION("monster detection", "purple"), + POTION("object detection", "yellow"), + POTION("sickness", "white"), + POTION("confusion", "swirly"), + POTION("gain strength", "purple-red"), + POTION("speed", "ruby"), + POTION("blindness", "dark green"), + POTION("gain level", "emerald"), + POTION("extra healing", "sky blue"), + POTION("levitation", "brown"), + POTION(NULL, "brilliant blue"), + POTION(NULL, "clear"), + POTION(NULL, "magenta"), + POTION(NULL, "ebony"), + +#define SCROLL(name,text,prob) { name, text, NULL, 0, 1,\ + SCROLL_SYM, prob, 0, 3, 0, 0, 0 } + SCROLL("mail", "KIRJE", 0), + SCROLL("enchant armor", "ZELGO MER", 6), + SCROLL("destroy armor", "JUYED AWK YACC", 5), + SCROLL("confuse monster", "NR 9", 5), + SCROLL("scare monster", "XIXAXA XOXAXA XUXAXA", 4), + SCROLL("blank paper", "READ ME", 3), + SCROLL("remove curse", "PRATYAVAYAH", 6), + SCROLL("enchant weapon", "DAIYEN FOOELS", 6), + SCROLL("damage weapon", "HACKEM MUCHE", 5), + SCROLL("create monster", "LEP GEX VEN ZEA", 5), + SCROLL("taming", "PRIRUTSENIE", 1), + SCROLL("genocide", "ELBIB YLOH",2), + SCROLL("light", "VERR YED HORRE", 10), + SCROLL("teleportation", "VENZAR BORGAVVE", 5), + SCROLL("gold detection", "THARR", 4), + SCROLL("food detection", "YUM YUM", 1), + SCROLL("identify", "KERNOD WEL", 18), + SCROLL("magic mapping", "ELAM EBOW", 5), + SCROLL("amnesia", "DUAM XNAHT", 3), + SCROLL("fire", "ANDOVA BEGARIN", 5), + SCROLL("punishment", "VE FORBRYDERNE", 1), + SCROLL(NULL, "VELOX NEB", 0), + SCROLL(NULL, "FOOBIE BLETCH", 0), + SCROLL(NULL, "TEMOV", 0), + SCROLL(NULL, "GARVEN DEH", 0), + +#define WAND(name,metal,prob,flags) { name, metal, NULL, 0, 0,\ + WAND_SYM, prob, 0, 3, flags, 0, 0 } + + WAND("light", "iridium", 10, NODIR), + WAND("secret door detection", "tin", 5, NODIR), + WAND("create monster", "platinum", 5, NODIR), + WAND("wishing", "glass", 1, NODIR), + WAND("striking", "zinc", 9, IMMEDIATE), + WAND("slow monster", "balsa", 5, IMMEDIATE), + WAND("speed monster", "copper", 5, IMMEDIATE), + WAND("undead turning", "silver", 5, IMMEDIATE), + WAND("polymorph", "brass", 5, IMMEDIATE), + WAND("cancellation", "maple", 5, IMMEDIATE), + WAND("teleportation", "pine", 5, IMMEDIATE), + WAND("make invisible", "marble", 9, IMMEDIATE), + WAND("digging", "iron", 5, RAY), + WAND("magic missile", "aluminium", 10, RAY), + WAND("fire", "steel", 5, RAY), + WAND("sleep", "curved", 5, RAY), + WAND("cold", "short", 5, RAY), + WAND("death", "long", 1, RAY), + WAND(NULL, "oak", 0, 0), + WAND(NULL, "ebony", 0, 0), + WAND(NULL, "runed", 0, 0), + +#define RING(name,stone,spec) { name, stone, NULL, 0, 0,\ + RING_SYM, 0, 0, 1, spec, 0, 0 } + + RING("adornment", "engagement", 0), + RING("teleportation", "wooden", 0), + RING("regeneration", "black onyx", 0), + RING("searching", "topaz", 0), + RING("see invisible", "pearl", 0), + RING("stealth", "sapphire", 0), + RING("levitation", "moonstone", 0), + RING("poison resistance", "agate", 0), + RING("aggravate monster", "tiger eye", 0), + RING("hunger", "shining", 0), + RING("fire resistance", "gold", 0), + RING("cold resistance", "copper", 0), + RING("protection from shape changers", "diamond", 0), + RING("conflict", "jade", 0), + RING("gain strength", "ruby", SPEC), + RING("increase damage", "silver", SPEC), + RING("protection", "granite", SPEC), + RING("warning", "wire", 0), + RING("teleport control", "iron", 0), + RING(NULL, "ivory", 0), + RING(NULL, "blackened", 0), + +/* gems ************************************************************/ +#define GEM(name,color,prob,gval) { name, color, NULL, 0, 1,\ + GEM_SYM, prob, 0, 1, 0, 0, gval } + GEM("diamond", "blue", 1, 4000), + GEM("ruby", "red", 1, 3500), + GEM("sapphire", "blue", 1, 3000), + GEM("emerald", "green", 1, 2500), + GEM("turquoise", "green", 1, 2000), + GEM("aquamarine", "blue", 1, 1500), + GEM("tourmaline", "green", 1, 1000), + GEM("topaz", "yellow", 1, 900), + GEM("opal", "yellow", 1, 800), + GEM("garnet", "dark", 1, 700), + GEM("amethyst", "violet", 2, 650), + GEM("agate", "green", 2, 600), + GEM("onyx", "white", 2, 550), + GEM("jasper", "yellowish brown", 2, 500), + GEM("jade", "green", 2, 450), + GEM("worthless piece of blue glass", "blue", 20, 0), + GEM("worthless piece of red glass", "red", 20, 0), + GEM("worthless piece of yellow glass", "yellow", 20, 0), + GEM("worthless piece of green glass", "green", 20, 0), + { NULL, NULL, NULL, 0, 0, ILLOBJ_SYM, 0, 0, 0, 0, 0, 0 } +}; + +char obj_symbols[] = { + ILLOBJ_SYM, AMULET_SYM, FOOD_SYM, WEAPON_SYM, TOOL_SYM, + BALL_SYM, CHAIN_SYM, ROCK_SYM, ARMOR_SYM, POTION_SYM, SCROLL_SYM, + WAND_SYM, RING_SYM, GEM_SYM, 0 }; +int bases[sizeof(obj_symbols)]; diff --git a/hack/def.permonst.h b/hack/def.permonst.h new file mode 100644 index 0000000..591a6c6 --- /dev/null +++ b/hack/def.permonst.h @@ -0,0 +1,27 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* def.permonst.h - version 1.0.2 */ +/* $DragonFly: src/games/hack/def.permonst.h,v 1.2 2005/05/22 03:37:05 y0netan1 Exp $ */ + +struct permonst { + const char *mname; + char mlet; + schar mlevel, mmove, ac, damn, damd; + unsigned pxlth; +}; + +extern struct permonst mons[]; +#define PM_ACID_BLOB &mons[7] +#define PM_ZOMBIE &mons[13] +#define PM_PIERCER &mons[17] +#define PM_KILLER_BEE &mons[26] +#define PM_WRAITH &mons[33] +#define PM_MIMIC &mons[37] +#define PM_VAMPIRE &mons[43] +#define PM_CHAMELEON &mons[47] +#define PM_DEMON &mons[54] +#define PM_MINOTAUR &mons[55] /* last in mons array */ +#define PM_SHK &mons[56] /* very last */ +#define PM_GHOST &pm_ghost +#define PM_EEL &pm_eel +#define PM_WIZARD &pm_wizard +#define CMNUM 55 /* number of common monsters */ diff --git a/hack/def.rm.h b/hack/def.rm.h new file mode 100644 index 0000000..46f3975 --- /dev/null +++ b/hack/def.rm.h @@ -0,0 +1,53 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* def.rm.h - version 1.0.2 */ +/* $DragonFly: src/games/hack/def.rm.h,v 1.2 2004/11/06 12:29:17 eirikn Exp $ */ + +/* Level location types */ +#define HWALL 1 +#define VWALL 2 +#define SDOOR 3 +#define SCORR 4 +#define LDOOR 5 +#define POOL 6 /* not yet fully implemented */ + /* this should in fact be a bit like lit */ +#define DOOR 7 +#define CORR 8 +#define ROOM 9 +#define STAIRS 10 + +/* + * Avoid using the level types in inequalities: + * these types are subject to change. + * Instead, use one of the macros below. + */ +#define IS_WALL(typ) ((typ) <= VWALL) +#define IS_ROCK(typ) ((typ) < POOL) /* absolutely nonaccessible */ +#define ACCESSIBLE(typ) ((typ) >= DOOR) /* good position */ +#define IS_ROOM(typ) ((typ) >= ROOM) /* ROOM or STAIRS */ +#define ZAP_POS(typ) ((typ) > DOOR) + +/* + * A few of the associated symbols are not hardwired. + */ +#ifdef QUEST +#define CORR_SYM ':' +#else +#define CORR_SYM '#' +#endif /* QUEST */ +#define POOL_SYM '}' + +#define ERRCHAR '{' + +/* + * The structure describing a coordinate position. + * Before adding fields, remember that this will significantly affect + * the size of temporary files and save files. + */ +struct rm { + char scrsym; + unsigned typ:5; + unsigned new:1; + unsigned seen:1; + unsigned lit:1; +}; +extern struct rm levl[COLNO][ROWNO]; diff --git a/hack/def.trap.h b/hack/def.trap.h new file mode 100644 index 0000000..2d25ec1 --- /dev/null +++ b/hack/def.trap.h @@ -0,0 +1,27 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* def.trap.h - version 1.0.2 */ +/* $DragonFly: src/games/hack/def.trap.h,v 1.2 2006/08/21 19:45:32 pavalos Exp $ */ + +struct trap { + struct trap *ntrap; + xchar tx, ty; + unsigned ttyp:5; + unsigned tseen:1; + unsigned once:1; +}; + +extern struct trap *ftrap; +#define newtrap() alloc(sizeof(struct trap)) + +/* various kinds of traps */ +#define BEAR_TRAP 0 +#define ARROW_TRAP 1 +#define DART_TRAP 2 +#define TRAPDOOR 3 +#define TELEP_TRAP 4 +#define PIT 5 +#define SLP_GAS_TRAP 6 +#define PIERC 7 +#define MIMIC 8 /* used only in mklev.c */ +#define TRAPNUM 9 /* if not less than 32, change sizeof(ttyp) */ + /* see also mtrapseen (bit map) */ diff --git a/hack/def.wseg.h b/hack/def.wseg.h new file mode 100644 index 0000000..161ead9 --- /dev/null +++ b/hack/def.wseg.h @@ -0,0 +1,14 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* def.wseg.h - version 1.0.2 */ +/* $DragonFly: src/games/hack/def.wseg.h,v 1.2 2004/11/06 12:29:17 eirikn Exp $ */ + +#ifndef NOWORM +/* worm structure */ +struct wseg { + struct wseg *nseg; + xchar wx, wy; + unsigned wdispl:1; +}; + +#define newseg() alloc(sizeof(struct wseg)) +#endif /* NOWORM */ diff --git a/hack/hack.6 b/hack/hack.6 new file mode 100644 index 0000000..239a4ce --- /dev/null +++ b/hack/hack.6 @@ -0,0 +1,160 @@ +.\" $FreeBSD: src/games/hack/hack.6,v 1.2.8.1 2001/07/22 11:01:22 dd Exp $ +.\" $DragonFly: src/games/hack/hack.6,v 1.5 2007/10/23 07:51:09 swildner Exp $ +.Dd March 31, 1985 +.Dt HACK 6 +.Os +.Sh NAME +.Nm hack +.Nd exploring The Dungeons of Doom +.Sh SYNOPSIS +.Nm +.Op Fl d Ar directory +.Op Fl n +.Op Fl u Ar playername +.Nm +.Op Fl d Ar directory +.Op Fl s +.Op Fl X +.Op Ar playername ... +.Sh DESCRIPTION +.Nm +is a display oriented dungeons \*[Am] dragons-like game. +Both display and command structure resemble rogue. +(For a game with the same structure but entirely different display - +a real cave instead of dull rectangles - try Quest.) +.Pp +To get started you really only need to know two commands. +The command +.Ic \&? +will give you a list of the available commands and the command +.Ic / +will identify the things you see on the screen. +.Pp +To win the game (as opposed to merely playing to beat other people's high +scores) you must locate the Amulet of Yendor which is somewhere below +the 20th level of the dungeon and get it out. +Nobody has achieved this yet and if somebody does, he will probably go +down in history as a hero among heroes. +.Pp +When the game ends, either by your death, when you quit, or if you escape +from the caves, +.Nm +will give you (a fragment of) the list of top scorers. +The scoring is based on many aspects of your behavior but a rough estimate +is obtained by taking the amount of gold you've found in the cave plus four +times your (real) experience. +Precious stones may be worth a lot of gold when brought to the exit. +There is a 10% penalty for getting yourself killed. +.Pp +The administration of the game is kept in the directory specified with the +.Fl d +option, or, if no such option is given, in the directory specified by +the environment variable +.Ev HACKDIR , +or, if no such variable exists, in the current directory. +This same directory contains several auxiliary files such as lockfiles and +the list of topscorers and a subdirectory +.Pa save +where games are saved. +The game administrator may however choose to install +.Nm +with a fixed playing ground, usually +.Pa /var/games/hackdir . +.Pp +The +.Fl n +option suppresses printing of the news. +.Pp +The +.Fl u Ar playername +option supplies the answer to the question "Who are you?". +When +.Ar playername +has as suffix one of +.Em -T , +.Em -S , +.Em -K , +.Em -F , +.Em -C , +or +.Em -W , +then this supplies the answer to the question "What kind of character ... ?". +.Pp +The +.Fl s +option will print out the list of your scores. +It may be followed by arguments +.Fl X +where X is one of the letters C, F, K, S, T, W to print the scores of +Cavemen, Fighters, Knights, Speleologists, Tourists or Wizards. +It may also be followed by one or more player names to print the scores of the +players mentioned. +.Sh ENVIRONMENT +.Bl -tag -width 24n -compact +.It Ev USER No or Ev LOGNAME +Your login name. +.It Ev HOME +Your home directory. +.It Ev SHELL +Your shell. +.It Ev TERM +The type of your terminal. +.It Ev HACKPAGER, PAGER +Pager used instead of default pager. +.It Ev MAIL +Mailbox file. +.It Ev MAILREADER +Reader used instead of default (probably +.Pa /usr/bin/mail ) . +.It Ev HACKDIR +Playground. +.It Ev HACKOPTIONS +String predefining several +.Nm +options (see help file). +.El +.Pp +Several other environment variables are used in debugging (wizard) mode, +like +.Ev GENOCIDED , +.Ev INVENT , +.Ev MAGIC +and +.Ev SHOPTYPE . +.Sh FILES +.Bl -tag -width 24n -compact +.It Pa hack +The +.Nm +program. +.It Pa data, rumors +Data files used by +.Nm . +.It Pa help, hh +Help data files. +.It Pa record +The list of topscorers. +.It Pa save +A subdirectory containing the saved games. +.It Pa bones_dd +Descriptions of the ghost and belongings of a deceased adventurer. +.It Pa xlock.dd +Description of a dungeon level. +.It Pa safelock +Lock file for xlock. +.It Pa record_lock +Lock file for record. +.El +.Sh AUTHORS +Jay Fenlason (+ Kenny Woodland, Mike Thome and Jon Payne) wrote the +original +.Nm , +very much like +.Xr rogue 6 +(but full of bugs). +.Pp +Andries Brouwer continuously deformed their sources into the current +version - in fact an entirely different game. +.Sh BUGS +Probably infinite. +Mail complaints to mcvax!aeb . diff --git a/hack/hack.Decl.c b/hack/hack.Decl.c new file mode 100644 index 0000000..15f807e --- /dev/null +++ b/hack/hack.Decl.c @@ -0,0 +1,42 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.Decl.c - version 1.0.3 */ + +#include "hack.h" +char nul[40]; /* contains zeros */ +char plname[PL_NSIZ]; /* player name */ +char lock[PL_NSIZ + 4] = "1lock"; /* long enough for login name .99 */ + +boolean in_mklev, restoring; + +struct rm levl[COLNO][ROWNO]; /* level map */ +#ifndef QUEST +struct mkroom rooms[MAXNROFROOMS + 1]; +coord doors[DOORMAX]; +#endif /* QUEST */ +struct monst *fmon = NULL; +struct trap *ftrap = NULL; +struct gold *fgold = NULL; +struct obj *fobj = NULL, *fcobj = NULL, *invent = NULL, *uwep = NULL, *uarm = NULL, + *uarm2 = NULL, *uarmh = NULL, *uarms = NULL, *uarmg = NULL, *uright = NULL, + *uleft = NULL, *uchain = NULL, *uball = NULL; +struct flag flags; +struct you u; +struct monst youmonst; /* dummy; used as return value for boomhit */ + +xchar dlevel = 1; +xchar xupstair, yupstair, xdnstair, ydnstair; +const char *save_cm, *killer, *nomovemsg; + +long moves = 1; +long wailmsg = 0; + +int multi = 0; +char genocided[60]; +char fut_geno[60]; + +xchar curx, cury; +xchar seelx, seehx, seely, seehy; /* corners of lit room */ + +coord bhitpos; + +char quitchars[] = " \r\n\033"; diff --git a/hack/hack.apply.c b/hack/hack.apply.c new file mode 100644 index 0000000..7944991 --- /dev/null +++ b/hack/hack.apply.c @@ -0,0 +1,467 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.apply.c - version 1.0.3 */ +/* $FreeBSD: src/games/hack/hack.apply.c,v 1.4.2.1 2001/02/18 02:20:07 kris Exp $ */ +/* $DragonFly: src/games/hack/hack.apply.c,v 1.4 2006/08/21 19:45:32 pavalos Exp $ */ + +#include "hack.h" +#include "def.edog.h" +extern char quitchars[]; + +static void use_camera(struct obj *); +static bool in_ice_box(struct obj *); +static bool ck_ice_box(struct obj *); +static int out_ice_box(struct obj *); +static void use_ice_box(struct obj *); +static struct monst *bchit(int, int, int, char); +static void use_whistle(struct obj *); +static void use_magic_whistle(struct obj *); +static bool dig(void); +static int use_pick_axe(struct obj *); + +int +doapply(void) +{ + struct obj *obj; + int res = 1; + + obj = getobj("(", "use or apply"); + if (!obj) + return (0); + + switch (obj->otyp) { + case EXPENSIVE_CAMERA: + use_camera(obj); + break; + case ICE_BOX: + use_ice_box(obj); + break; + case PICK_AXE: + res = use_pick_axe(obj); + break; + + case MAGIC_WHISTLE: + if (pl_character[0] == 'W' || u.ulevel > 9) { + use_magic_whistle(obj); + break; + } + /* fall into next case */ + case WHISTLE: + use_whistle(obj); + break; + + case CAN_OPENER: + if (!carrying(TIN)) { + pline("You have no can to open."); + goto xit; + } + pline("You cannot open a tin without eating its contents."); + pline("In order to eat, use the 'e' command."); + if (obj != uwep) + pline("Opening the tin will be much easier if you wield the can-opener."); + goto xit; + + default: + pline("Sorry, I don't know how to use that."); +xit: + nomul(0); + return (0); + } + nomul(0); + return (res); +} + +static void +use_camera(struct obj *obj __attribute__((unused))) +{ + struct monst *mtmp; + + if (!getdir(1)) { /* ask: in what direction? */ + flags.move = multi = 0; + return; + } + if (u.uswallow) { + pline("You take a picture of %s's stomach.", monnam(u.ustuck)); + return; + } + if (u.dz) { + pline("You take a picture of the %s.", + (u.dz > 0) ? "floor" : "ceiling"); + return; + } + if ((mtmp = bchit(u.dx, u.dy, COLNO, '!')) != NULL) { + if (mtmp->msleep) { + mtmp->msleep = 0; + pline("The flash awakens %s.", monnam(mtmp)); /* a3 */ + } else if (mtmp->data->mlet != 'y') + if (mtmp->mcansee || mtmp->mblinded) { + int tmp = dist(mtmp->mx, mtmp->my); + int tmp2; + if (cansee(mtmp->mx, mtmp->my)) + pline("%s is blinded by the flash!", Monnam(mtmp)); + setmangry(mtmp); + if (tmp < 9 && !mtmp->isshk && rn2(4)) { + mtmp->mflee = 1; + if (rn2(4)) + mtmp->mfleetim = rnd(100); + } + if (tmp < 3) + mtmp->mcansee = mtmp->mblinded = 0; + else { + tmp2 = mtmp->mblinded; + tmp2 += rnd(1 + 50 / tmp); + if (tmp2 > 127) + tmp2 = 127; + mtmp->mblinded = tmp2; + mtmp->mcansee = 0; + } + } + } +} + +static +struct obj *current_ice_box; /* a local variable of use_ice_box, to be + used by its local procedures in/ck_ice_box */ +static bool +in_ice_box(struct obj *obj) +{ + if (obj == current_ice_box || + (Punished && (obj == uball || obj == uchain))) { + pline("You must be kidding."); + return (0); + } + if (obj->owornmask & (W_ARMOR | W_RING)) { + pline("You cannot refrigerate something you are wearing."); + return (0); + } + if (obj->owt + current_ice_box->owt > 70) { + pline("It won't fit."); + return (1); /* be careful! */ + } + if (obj == uwep) { + if (uwep->cursed) { + pline("Your weapon is welded to your hand!"); + return (0); + } + setuwep(NULL); + } + current_ice_box->owt += obj->owt; + freeinv(obj); + obj->o_cnt_id = current_ice_box->o_id; + obj->nobj = fcobj; + fcobj = obj; + obj->age = moves - obj->age; /* actual age */ + return (1); +} + +static bool +ck_ice_box(struct obj *obj) +{ + return (obj->o_cnt_id == current_ice_box->o_id); +} + +static int +out_ice_box(struct obj *obj) +{ + struct obj *otmp; + + if (obj == fcobj) + fcobj = fcobj->nobj; + else { + for (otmp = fcobj; otmp->nobj != obj; otmp = otmp->nobj) + if (!otmp->nobj) + panic("out_ice_box"); + otmp->nobj = obj->nobj; + } + current_ice_box->owt -= obj->owt; + obj->age = moves - obj->age; /* simulated point of time */ + addinv(obj); + return (0); +} + +static void +use_ice_box(struct obj *obj) +{ + int cnt = 0; + struct obj *otmp; + + current_ice_box = obj; /* for use by in/out_ice_box */ + for (otmp = fcobj; otmp; otmp = otmp->nobj) + if (otmp->o_cnt_id == obj->o_id) + cnt++; + if (!cnt) + pline("Your ice-box is empty."); + else { + pline("Do you want to take something out of the ice-box? [yn] "); + if (readchar() == 'y') + if (askchain(fcobj, NULL, 0, out_ice_box, ck_ice_box, 0)) + return; + pline("That was all. Do you wish to put something in? [yn] "); + if (readchar() != 'y') + return; + } + /* call getobj: 0: allow cnt; #: allow all types; %: expect food */ + otmp = getobj("0#%", "put in"); + if (!otmp || !in_ice_box(otmp)) + flags.move = multi = 0; +} + +static +struct monst * +bchit(int ddx, int ddy, int range, char sym) +{ + struct monst *mtmp = NULL; + int bchx = u.ux, bchy = u.uy; + + if (sym) + Tmp_at(-1, sym); /* open call */ + while (range--) { + bchx += ddx; + bchy += ddy; + if ((mtmp = m_at(bchx, bchy))) + break; + if (!ZAP_POS(levl[bchx][bchy].typ)) { + bchx -= ddx; + bchy -= ddy; + break; + } + if (sym) + Tmp_at(bchx, bchy); + } + if (sym) + Tmp_at(-1, -1); + return (mtmp); +} + +static void +use_whistle(struct obj *obj __attribute__((unused))) +{ + struct monst *mtmp = fmon; + + pline("You produce a high whistling sound."); + while (mtmp) { + if (dist(mtmp->mx, mtmp->my) < u.ulevel * 20) { + if (mtmp->msleep) + mtmp->msleep = 0; + if (mtmp->mtame) + EDOG(mtmp)->whistletime = moves; + } + mtmp = mtmp->nmon; + } +} + +static void +use_magic_whistle(struct obj *obj __attribute__((unused))) +{ + struct monst *mtmp = fmon; + + pline("You produce a strange whistling sound."); + while (mtmp) { + if (mtmp->mtame) + mnexto(mtmp); + mtmp = mtmp->nmon; + } +} + +static int dig_effort; /* effort expended on current pos */ +static uchar dig_level; +static coord dig_pos; +static boolean dig_down; + +static +bool +dig(void) +{ + struct rm *lev; + int dpx = dig_pos.x, dpy = dig_pos.y; + + /* perhaps a nymph stole his pick-axe while he was busy digging */ + /* or perhaps he teleported away */ + if (u.uswallow || !uwep || uwep->otyp != PICK_AXE || + dig_level != dlevel || + ((dig_down && (dpx != u.ux || dpy != u.uy)) || + (!dig_down && dist(dpx, dpy) > 2))) + return (0); + + dig_effort += 10 + abon() + uwep->spe + rn2(5); + if (dig_down) { + if (!xdnstair) { + pline("The floor here seems too hard to dig in."); + return (0); + } + if (dig_effort > 250) { + dighole(); + return (0); /* done with digging */ + } + if (dig_effort > 50) { + struct trap *ttmp = t_at(dpx, dpy); + + if (!ttmp) { + ttmp = maketrap(dpx, dpy, PIT); + ttmp->tseen = 1; + pline("You have dug a pit."); + u.utrap = rn1(4, 2); + u.utraptype = TT_PIT; + return (0); + } + } + } else if (dig_effort > 100) { + const char *digtxt; + struct obj *obj; + + lev = &levl[dpx][dpy]; + if ((obj = sobj_at(ENORMOUS_ROCK, dpx, dpy)) != NULL) { + fracture_rock(obj); + digtxt = "The rock falls apart."; + } else if (!lev->typ || lev->typ == SCORR) { + lev->typ = CORR; + digtxt = "You succeeded in cutting away some rock."; + } else if (lev->typ == HWALL || lev->typ == VWALL + || lev->typ == SDOOR) { + lev->typ = xdnstair ? DOOR : ROOM; + digtxt = "You just made an opening in the wall."; + } else + digtxt = "Now what exactly was it that you were digging in?"; + mnewsym(dpx, dpy); + prl(dpx, dpy); + pline("%s", digtxt); /* after mnewsym & prl */ + return (0); + } else { + if (IS_WALL(levl[dpx][dpy].typ)) { + int rno = inroom(dpx, dpy); + + if (rno >= 0 && rooms[rno].rtype >= 8) { + pline("This wall seems too hard to dig into."); + return (0); + } + } + pline("You hit the rock with all your might."); + } + return (1); +} + +/* When will hole be finished? Very rough indication used by shopkeeper. */ +int +holetime(void) +{ + return ((occupation == dig) ? (250 - dig_effort) / 20 : -1); +} + +void +dighole(void) +{ + struct trap *ttmp = t_at(u.ux, u.uy); + + if (!xdnstair) { + pline("The floor here seems too hard to dig in."); + } else { + if (ttmp) + ttmp->ttyp = TRAPDOOR; + else + ttmp = maketrap(u.ux, u.uy, TRAPDOOR); + ttmp->tseen = 1; + pline("You've made a hole in the floor."); + if (!u.ustuck) { + if (inshop()) + shopdig(1); + pline("You fall through ..."); + if (u.utraptype == TT_PIT) { + u.utrap = 0; + u.utraptype = 0; + } + goto_level(dlevel + 1, FALSE); + } + } +} + +static int +use_pick_axe(struct obj *obj) +{ + char dirsyms[12]; + char *dsp = dirsyms, *sdp = sdir; + struct monst *mtmp; + struct rm *lev; + int rx, ry, res = 0; + + if (obj != uwep) { + if (uwep && uwep->cursed) { + /* Andreas Bormann - ihnp4!decvax!mcvax!unido!ab */ + pline("Since your weapon is welded to your hand,"); + pline("you cannot use that pick-axe."); + return (0); + } + pline("You now wield %s.", doname(obj)); + setuwep(obj); + res = 1; + } + while (*sdp) { + movecmd(*sdp); /* sets u.dx and u.dy and u.dz */ + rx = u.ux + u.dx; + ry = u.uy + u.dy; + if (u.dz > 0 || (u.dz == 0 && isok(rx, ry) && + (IS_ROCK(levl[rx][ry].typ) + || sobj_at(ENORMOUS_ROCK, rx, ry)))) + *dsp++ = *sdp; + sdp++; + } + *dsp = 0; + pline("In what direction do you want to dig? [%s] ", dirsyms); + if (!getdir(0)) /* no txt */ + return (res); + if (u.uswallow && attack(u.ustuck)) /* return(1) */ + ; + else if (u.dz < 0) + pline("You cannot reach the ceiling."); + else if (u.dz == 0) { + if (Confusion) + confdir(); + rx = u.ux + u.dx; + ry = u.uy + u.dy; + if ((mtmp = m_at(rx, ry)) && attack(mtmp)) + return (1); + if (!isok(rx, ry)) { + pline("Clash!"); + return (1); + } + lev = &levl[rx][ry]; + if (lev->typ == DOOR) + pline("Your %s against the door.", + aobjnam(obj, "clang")); + else if (!IS_ROCK(lev->typ) + && !sobj_at(ENORMOUS_ROCK, rx, ry)) { + /* ACCESSIBLE or POOL */ + pline("You swing your %s through thin air.", + aobjnam(obj, NULL)); + } else { + if (dig_pos.x != rx || dig_pos.y != ry + || dig_level != dlevel || dig_down) { + dig_down = FALSE; + dig_pos.x = rx; + dig_pos.y = ry; + dig_level = dlevel; + dig_effort = 0; + pline("You start digging."); + } else + pline("You continue digging."); + occupation = dig; + occtxt = "digging"; + } + } else if (Levitation) { + pline("You cannot reach the floor."); + } else { + if (dig_pos.x != u.ux || dig_pos.y != u.uy + || dig_level != dlevel || !dig_down) { + dig_down = TRUE; + dig_pos.x = u.ux; + dig_pos.y = u.uy; + dig_level = dlevel; + dig_effort = 0; + pline("You start digging in the floor."); + if (inshop()) + shopdig(0); + } else + pline("You continue digging in the floor."); + occupation = dig; + occtxt = "digging"; + } + return (1); +} diff --git a/hack/hack.bones.c b/hack/hack.bones.c new file mode 100644 index 0000000..97ecc0f --- /dev/null +++ b/hack/hack.bones.c @@ -0,0 +1,108 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.bones.c - version 1.0.3 */ +/* $FreeBSD: src/games/hack/hack.bones.c,v 1.4 1999/11/16 10:26:35 marcel Exp $ */ +/* $DragonFly: src/games/hack/hack.bones.c,v 1.4 2006/08/21 19:45:32 pavalos Exp $ */ + +#include "hack.h" + +char bones[] = "bones_xx"; + +/* save bones and possessions of a deceased adventurer */ +void +savebones(void) +{ + int fd; + struct obj *otmp; + struct trap *ttmp; + struct monst *mtmp; + + if (dlevel <= 0 || dlevel > MAXLEVEL) + return; + if (!rn2(1 + dlevel / 2)) /* not so many ghosts on low levels */ + return; + bones[6] = '0' + (dlevel / 10); + bones[7] = '0' + (dlevel % 10); + if ((fd = open(bones, O_RDONLY)) >= 0) { + close(fd); + return; + } + /* drop everything; the corpse's possessions are usually cursed */ + otmp = invent; + while (otmp) { + otmp->ox = u.ux; + otmp->oy = u.uy; + otmp->age = 0; /* very long ago */ + otmp->owornmask = 0; + if (rn2(5)) + otmp->cursed = 1; + if (!otmp->nobj) { + otmp->nobj = fobj; + fobj = invent; + invent = 0; /* superfluous */ + break; + } + otmp = otmp->nobj; + } + if (!(mtmp = makemon(PM_GHOST, u.ux, u.uy))) + return; + mtmp->mx = u.ux; + mtmp->my = u.uy; + mtmp->msleep = 1; + strcpy((char *)mtmp->mextra, plname); + mkgold(somegold() + d(dlevel, 30), u.ux, u.uy); + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { + mtmp->m_id = 0; + if (mtmp->mtame) { + mtmp->mtame = 0; + mtmp->mpeaceful = 0; + } + mtmp->mlstmv = 0; + if (mtmp->mdispl) + unpmon(mtmp); + } + for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap) + ttmp->tseen = 0; + for (otmp = fobj; otmp; otmp = otmp->nobj) { + otmp->o_id = 0; + /* otmp->o_cnt_id = 0; - superfluous */ + otmp->onamelth = 0; + otmp->known = 0; + otmp->invlet = 0; + if (otmp->olet == AMULET_SYM && !otmp->spe) { + otmp->spe = -1; /* no longer the actual amulet */ + otmp->cursed = 1; /* flag as gotten from a ghost */ + } + } + if ((fd = creat(bones, FMASK)) < 0) + return; + savelev(fd, dlevel); + close(fd); +} + +int +getbones(void) +{ + int fd, x, y, ok; + + if (rn2(3)) /* only once in three times do we find bones */ + return (0); + bones[6] = '0' + dlevel / 10; + bones[7] = '0' + dlevel % 10; + if ((fd = open(bones, O_RDONLY)) < 0) + return (0); + if ((ok = uptodate(fd)) != 0) { + getlev(fd, 0, dlevel); + for (x = 0; x < COLNO; x++) + for (y = 0; y < ROWNO; y++) + levl[x][y].seen = levl[x][y].new = 0; + } + close(fd); +#ifdef WIZARD + if (!wizard) /* duvel!frans: don't remove bones while debugging */ +#endif /* WiZARD */ + if (unlink(bones) < 0) { + pline("Cannot unlink %s .", bones); + return (0); + } + return (ok); +} diff --git a/hack/hack.c b/hack/hack.c new file mode 100644 index 0000000..56aa11f --- /dev/null +++ b/hack/hack.c @@ -0,0 +1,904 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.c - version 1.0.3 */ +/* $FreeBSD: src/games/hack/hack.c,v 1.4 1999/11/16 10:26:35 marcel Exp $ */ + +#include "hack.h" + +static void movobj(struct obj *, int, int); +#ifdef QUEST +static bool rroom(int, int); +#endif +static int inv_cnt(void); + +/* called on movement: + * 1. when throwing ball+chain far away + * 2. when teleporting + * 3. when walking out of a lit room + */ +void +unsee(void) +{ + int x, y; + struct rm *lev; + +#ifndef QUEST + if (seehx) + seehx = 0; + else +#endif /* QUEST */ + for (x = u.ux - 1; x < u.ux + 2; x++) + for (y = u.uy - 1; y < u.uy + 2; y++) { + if (!isok(x, y)) + continue; + lev = &levl[x][y]; + if (!lev->lit && lev->scrsym == '.') { + lev->scrsym = ' '; + lev->new = 1; + on_scr(x, y); + } + } +} + +/* called: + * in hack.eat.c: seeoff(0) - blind after eating rotten food + * in hack.mon.c: seeoff(0) - blinded by a yellow light + * in hack.mon.c: seeoff(1) - swallowed + * in hack.do.c: seeoff(0) - blind after drinking potion + * in hack.do.c: seeoff(1) - go up or down the stairs + * in hack.trap.c:seeoff(1) - fall through trapdoor + * mode: + * 1 to redo @, 0 to leave them *//* 1 means + * misc movement, 0 means blindness + */ +void +seeoff(bool mode) +{ + int x, y; + struct rm *lev; + + if (u.udispl && mode) { + u.udispl = 0; + levl[u.udisx][u.udisy].scrsym = news0(u.udisx, u.udisy); + } +#ifndef QUEST + if (seehx) + seehx = 0; + else +#endif /* QUEST */ + if (!mode) { + for (x = u.ux - 1; x < u.ux + 2; x++) + for (y = u.uy - 1; y < u.uy + 2; y++) { + if (!isok(x, y)) + continue; + lev = &levl[x][y]; + if (!lev->lit && lev->scrsym == '.') + lev->seen = 0; + } + } +} + +void +domove(void) +{ + xchar oldx, oldy; + struct monst *mtmp = NULL; + struct rm *tmpr, *ust; + struct trap *trap = NULL; + struct obj *otmp; + + u_wipe_engr(rnd(5)); + + if (inv_weight() > 0) { + pline("You collapse under your load."); + nomul(0); + return; + } + if (u.uswallow) { + u.dx = u.dy = 0; + u.ux = u.ustuck->mx; + u.uy = u.ustuck->my; + } else { + if (Confusion) { + do { + confdir(); + } while (!isok(u.ux + u.dx, u.uy + u.dy) || + IS_ROCK(levl[u.ux + u.dx][u.uy + u.dy].typ)); + } + if (!isok(u.ux + u.dx, u.uy + u.dy)) { + nomul(0); + return; + } + } + + ust = &levl[u.ux][u.uy]; + oldx = u.ux; + oldy = u.uy; + if (!u.uswallow && + (trap = t_at(u.ux + u.dx, u.uy + u.dy)) && trap->tseen) + nomul(0); + if (u.ustuck && !u.uswallow && (u.ux + u.dx != u.ustuck->mx || + u.uy + u.dy != u.ustuck->my)) { + if (dist(u.ustuck->mx, u.ustuck->my) > 2) { + /* perhaps it fled (or was teleported or ... ) */ + u.ustuck = 0; + } else { + if (Blind) + pline("You cannot escape from it!"); + else + pline("You cannot escape from %s!", + monnam(u.ustuck)); + nomul(0); + return; + } + } + if (u.uswallow || (mtmp = m_at(u.ux + u.dx, u.uy + u.dy))) { + /* attack monster */ + + nomul(0); + gethungry(); + if (multi < 0) /* we just fainted */ + return; + + /* try to attack; note that it might evade */ + if (attack(u.uswallow ? u.ustuck : mtmp)) + return; + } + /* not attacking an animal, so we try to move */ + if (u.utrap) { + if (u.utraptype == TT_PIT) { + pline("You are still in a pit."); + u.utrap--; + } else { + pline("You are caught in a beartrap."); + if ((u.dx && u.dy) || !rn2(5)) + u.utrap--; + } + return; + } + tmpr = &levl[u.ux + u.dx][u.uy + u.dy]; + if (IS_ROCK(tmpr->typ) || + (u.dx && u.dy && (tmpr->typ == DOOR || ust->typ == DOOR))) { + flags.move = 0; + nomul(0); + return; + } + while ((otmp = sobj_at(ENORMOUS_ROCK, u.ux + u.dx, u.uy + u.dy)) != NULL) { + struct trap *ttmp; + xchar rx = u.ux + 2 * u.dx, ry = u.uy + 2 * u.dy; + nomul(0); + if (isok(rx, ry) && !IS_ROCK(levl[rx][ry].typ) && + (levl[rx][ry].typ != DOOR || !(u.dx && u.dy)) && + !sobj_at(ENORMOUS_ROCK, rx, ry)) { + if (m_at(rx, ry)) { + pline("You hear a monster behind the rock."); + pline("Perhaps that's why you cannot move it."); + goto cannot_push; + } + if ((ttmp = t_at(rx, ry)) != NULL) + switch (ttmp->ttyp) { + case PIT: + pline("You push the rock into a pit!"); + deltrap(ttmp); + delobj(otmp); + pline("It completely fills the pit!"); + continue; + case TELEP_TRAP: + pline("You push the rock and suddenly it disappears!"); + delobj(otmp); + continue; + } + if (levl[rx][ry].typ == POOL) { + levl[rx][ry].typ = ROOM; + mnewsym(rx, ry); + prl(rx, ry); + pline("You push the rock into the water."); + pline("Now you can cross the water!"); + delobj(otmp); + continue; + } + otmp->ox = rx; + otmp->oy = ry; + if (cansee(rx, ry)) + atl(rx, ry, otmp->olet); + if (Invisible) + newsym(u.ux + u.dx, u.uy + u.dy); + + { + static long lastmovetime; + /* note: this var contains garbage initially and + * after a restore */ + if (moves > lastmovetime + 2 || moves < lastmovetime) + pline("With great effort you move the enormous rock."); + lastmovetime = moves; + } + } else { + pline("You try to move the enormous rock, but in vain."); +cannot_push: + if ((!invent || inv_weight() + 90 <= 0) && + (!u.dx || !u.dy || + (IS_ROCK(levl[u.ux][u.uy + u.dy].typ) + && IS_ROCK(levl[u.ux + u.dx][u.uy].typ)))) { + pline("However, you can squeeze yourself into a small opening."); + break; + } else + return; + } + } + if (u.dx && u.dy && IS_ROCK(levl[u.ux][u.uy + u.dy].typ) && + IS_ROCK(levl[u.ux + u.dx][u.uy].typ) && + invent && inv_weight() + 40 > 0) { + pline("You are carrying too much to get through."); + nomul(0); + return; + } + if (Punished && + DIST(u.ux + u.dx, u.uy + u.dy, uchain->ox, uchain->oy) > 2) { + if (carried(uball)) { + movobj(uchain, u.ux, u.uy); + goto nodrag; + } + + if (DIST(u.ux + u.dx, u.uy + u.dy, uball->ox, uball->oy) < 3) { + /* leave ball, move chain under/over ball */ + movobj(uchain, uball->ox, uball->oy); + goto nodrag; + } + + if (inv_weight() + (int)uball->owt / 2 > 0) { + pline("You cannot %sdrag the heavy iron ball.", + invent ? "carry all that and also " : ""); + nomul(0); + return; + } + + movobj(uball, uchain->ox, uchain->oy); + unpobj(uball); /* BAH %% */ + uchain->ox = u.ux; + uchain->oy = u.uy; + nomul(-2); + nomovemsg = ""; +nodrag: ; + } + u.ux += u.dx; + u.uy += u.dy; + if (flags.run) { + if (tmpr->typ == DOOR || + (xupstair == u.ux && yupstair == u.uy) || + (xdnstair == u.ux && ydnstair == u.uy)) + nomul(0); + } + + if (tmpr->typ == POOL && !Levitation) + drown(); /* not necessarily fatal */ + + if (!Blind) { +#ifdef QUEST + setsee(); +#else + if (ust->lit) { + if (tmpr->lit) { + if (tmpr->typ == DOOR) + prl1(u.ux + u.dx, u.uy + u.dy); + else if (ust->typ == DOOR) + nose1(oldx - u.dx, oldy - u.dy); + } else { + unsee(); + prl1(u.ux + u.dx, u.uy + u.dy); + } + } else { + if (tmpr->lit) + setsee(); + else { + prl1(u.ux + u.dx, u.uy + u.dy); + if (tmpr->typ == DOOR) { + if (u.dy) { + prl(u.ux - 1, u.uy); + prl(u.ux + 1, u.uy); + } else { + prl(u.ux, u.uy - 1); + prl(u.ux, u.uy + 1); + } + } + } + nose1(oldx - u.dx, oldy - u.dy); + } +#endif /* QUEST */ + } else + pru(); + if (!flags.nopick) + pickup(1); + if (trap) + dotrap(trap); /* fall into pit, arrow trap, etc. */ + inshop(); + if (!Blind) + read_engr_at(u.ux, u.uy); +} + +static void +movobj(struct obj *obj, int ox, int oy) +{ + /* Some dirty programming to get display right */ + freeobj(obj); + unpobj(obj); + obj->nobj = fobj; + fobj = obj; + obj->ox = ox; + obj->oy = oy; +} + +int +dopickup(void) +{ + if (!g_at(u.ux, u.uy) && !o_at(u.ux, u.uy)) { + pline("There is nothing here to pick up."); + return (0); + } + if (Levitation) { + pline("You cannot reach the floor."); + return (1); + } + pickup(0); + return (1); +} + +void +pickup(int all) +{ + struct gold *gold; + struct obj *obj, *obj2; + int wt; + + if (Levitation) + return; + while ((gold = g_at(u.ux, u.uy))) { + pline("%ld gold piece%s.", gold->amount, plur(gold->amount)); + u.ugold += gold->amount; + flags.botl = 1; + freegold(gold); + if (flags.run) + nomul(0); + if (Invisible) + newsym(u.ux, u.uy); + } + + /* check for more than one object */ + if (!all) { + int ct = 0; + + for (obj = fobj; obj; obj = obj->nobj) + if (obj->ox == u.ux && obj->oy == u.uy) + if (!Punished || obj != uchain) + ct++; + if (ct < 2) + all++; + else + pline("There are several objects here."); + } + + for (obj = fobj; obj; obj = obj2) { + obj2 = obj->nobj; /* perhaps obj will be picked up */ + if (obj->ox == u.ux && obj->oy == u.uy) { + if (flags.run) + nomul(0); + + /* do not pick up uchain */ + if (Punished && obj == uchain) + continue; + + if (!all) { + char c; + + pline("Pick up %s ? [ynaq]", doname(obj)); + while (!strchr("ynaq ", (c = readchar()))) + bell(); + if (c == 'q') + return; + if (c == 'n') + continue; + if (c == 'a') + all = 1; + } + + if (obj->otyp == DEAD_COCKATRICE && !uarmg) { + pline("Touching the dead cockatrice is a fatal mistake."); + pline("You turn to stone."); + killer = "cockatrice cadaver"; + done("died"); + } + + if (obj->otyp == SCR_SCARE_MONSTER) { + if (!obj->spe) + obj->spe = 1; + else { + /* Note: perhaps the 1st pickup failed: you cannot + * carry anymore, and so we never dropped it - + * let's assume that treading on it twice also + * destroys the scroll */ + pline("The scroll turns to dust as you pick it up."); + delobj(obj); + continue; + } + } + + wt = inv_weight() + obj->owt; + if (wt > 0) { + if (obj->quan > 1) { + /* see how many we can lift */ + int savequan = obj->quan; + int iw = inv_weight(); + int qq; + for (qq = 1; qq < savequan; qq++) { + obj->quan = qq; + if (iw + weight(obj) > 0) + break; + } + obj->quan = savequan; + qq--; + /* we can carry qq of them */ + if (!qq) + goto too_heavy; + pline("You can only carry %s of the %s lying here.", + (qq == 1) ? "one" : "some", + doname(obj)); + splitobj(obj, qq); + /* note: obj2 is set already, so we'll never + * encounter the other half; if it should be + * otherwise then write + * obj2 = splitobj(obj, qq); + */ + goto lift_some; + } +too_heavy: + pline("There %s %s here, but %s.", + (obj->quan == 1) ? "is" : "are", + doname(obj), + !invent ? "it is too heavy for you to lift" + : "you cannot carry anymore"); + break; + } +lift_some: + if (inv_cnt() >= 52) { + pline("Your knapsack cannot accommodate anymore items."); + break; + } + if (wt > -5) + pline("You have a little trouble lifting"); + freeobj(obj); + if (Invisible) + newsym(u.ux, u.uy); + addtobill(obj); /* sets obj->unpaid if necessary */ + { + int pickquan = obj->quan; + int mergquan; + if (!Blind) /* this is done by prinv(), */ + obj->dknown = 1;/* but addinv() needs it */ + /* already for merging */ + obj = addinv(obj); /* might merge it with other objects */ + mergquan = obj->quan; + obj->quan = pickquan; /* to fool prinv() */ + prinv(obj); + obj->quan = mergquan; + } + } + } +} + +/* stop running if we see something interesting */ +/* turn around a corner if that is the only way we can proceed */ +/* do not turn left or right twice */ +void +lookaround(void) +{ + int x, y, i, x0, y0, m0, i0 = 9; + int corrct = 0, noturn = 0; + struct monst *mtmp; + + /* suppress "used before set" message */ + x0 = y0 = m0 = 0; + if (Blind || flags.run == 0) + return; + if (flags.run == 1 && levl[u.ux][u.uy].typ == ROOM) + return; +#ifdef QUEST + if (u.ux0 == u.ux + u.dx && u.uy0 == u.uy + u.dy) + goto stop; +#endif /* QUEST */ + for (x = u.ux - 1; x <= u.ux + 1; x++) + for (y = u.uy - 1; y <= u.uy + 1; y++) { + if (x == u.ux && y == u.uy) + continue; + if (!levl[x][y].typ) + continue; + if ((mtmp = m_at(x, y)) && !mtmp->mimic && + (!mtmp->minvis || See_invisible)) { + if (!mtmp->mtame || + (x == u.ux + u.dx && y == u.uy + u.dy)) + goto stop; + } else /* invisible M cannot influence us */ + mtmp = NULL; + if (x == u.ux - u.dx && y == u.uy - u.dy) + continue; + switch (levl[x][y].scrsym) { + case '|': + case '-': + case '.': + case ' ': + break; + case '+': + if (x != u.ux && y != u.uy) + break; + if (flags.run != 1) + goto stop; + /* fall into next case */ + case CORR_SYM: +corr: + if (flags.run == 1 || flags.run == 3) { + i = DIST(x, y, u.ux + u.dx, u.uy + u.dy); + if (i > 2) + break; + if (corrct == 1 && + DIST(x, y, x0, y0) != 1) + noturn = 1; + if (i < i0) { + i0 = i; + x0 = x; + y0 = y; + m0 = mtmp ? 1 : 0; + } + } + corrct++; + break; + case '^': + if (flags.run == 1) /* if you must */ + goto corr; + if (x == u.ux + u.dx && y == u.uy + u.dy) + goto stop; + break; + default: /* e.g. objects or trap or stairs */ + if (flags.run == 1) + goto corr; + if (mtmp) /* d */ + break; +stop: + nomul(0); + return; + } + } +#ifdef QUEST + if (corrct > 0 && (flags.run == 4 || flags.run == 5)) + goto stop; +#endif /* QUEST */ + if (corrct > 1 && flags.run == 2) + goto stop; + if ((flags.run == 1 || flags.run == 3) && !noturn && !m0 && i0 && + (corrct == 1 || (corrct == 2 && i0 == 1))) { + /* make sure that we do not turn too far */ + if (i0 == 2) { + if (u.dx == y0 - u.uy && u.dy == u.ux - x0) + i = 2; /* straight turn right */ + else + i = -2; /* straight turn left */ + } else if (u.dx && u.dy) { + if ((u.dx == u.dy && y0 == u.uy) || + (u.dx != u.dy && y0 != u.uy)) + i = -1; /* half turn left */ + else + i = 1; /* half turn right */ + } else { + if ((x0 - u.ux == y0 - u.uy && !u.dy) || + (x0 - u.ux != y0 - u.uy && u.dy)) + i = 1; /* half turn right */ + else + i = -1; /* half turn left */ + } + i += u.last_str_turn; + if (i <= 2 && i >= -2) { + u.last_str_turn = i; + u.dx = x0 - u.ux, u.dy = y0 - u.uy; + } + } +} + +/* something like lookaround, but we are not running */ +/* react only to monsters that might hit us */ +bool +monster_nearby(void) +{ + int x, y; + struct monst *mtmp; + + if (!Blind) + for (x = u.ux - 1; x <= u.ux + 1; x++) + for (y = u.uy - 1; y <= u.uy + 1; y++) { + if (x == u.ux && y == u.uy) + continue; + if ((mtmp = m_at(x, y)) && !mtmp->mimic && + !mtmp->mtame && + !mtmp->mpeaceful && + !strchr("Ea", mtmp->data->mlet) && + !mtmp->mfroz && !mtmp->msleep && /* aplvax!jcn */ + (!mtmp->minvis || See_invisible)) + return (1); + } + return (0); +} + +#ifdef QUEST +bool +cansee(xchar x, xchar y) +{ + int dx, dy, adx, ady, sdx, sdy, dmax, d; + + if (Blind) + return (0); + if (!isok(x, y)) + return (0); + d = dist(x, y); + if (d < 3) + return (1); + if (d > u.uhorizon * u.uhorizon) + return (0); + if (!levl[x][y].lit) + return (0); + dx = x - u.ux; + adx = abs(dx); + sdx = sgn(dx); + dy = y - u.uy; + ady = abs(dy); + sdy = sgn(dy); + if (dx == 0 || dy == 0 || adx == ady) { + dmax = (dx == 0) ? ady : adx; + for (d = 1; d <= dmax; d++) + if (!rroom(sdx * d, sdy * d)) + return (0); + return (1); + } else if (ady > adx) { + for (d = 1; d <= ady; d++) { + if (!rroom(sdx * ((d * adx) / ady), sdy * d) || + !rroom(sdx * ((d * adx - 1) / ady + 1), sdy * d)) + return (0); + } + return (1); + } else { + for (d = 1; d <= adx; d++) { + if (!rroom(sdx * d, sdy * ((d * ady) / adx)) || + !rroom(sdx * d, sdy * ((d * ady - 1) / adx + 1))) + return (0); + } + return (1); + } +} + +static bool +rroom(int x, int y) +{ + return (IS_ROOM(levl[u.ux + x][u.uy + y].typ)); +} + +#else + +bool +cansee(xchar x, xchar y) +{ + if (Blind || u.uswallow) + return (0); + if (dist(x, y) < 3) + return (1); + if (levl[x][y].lit && seelx <= x && x <= seehx && seely <= y && + y <= seehy) + return (1); + return (0); +} +#endif /* QUEST */ + +int +sgn(int a) +{ + return ((a > 0) ? 1 : (a == 0) ? 0 : -1); +} + +#ifdef QUEST +void +setsee(void) +{ + int x, y; + + if (Blind) { + pru(); + return; + } + for (y = u.uy - u.uhorizon; y <= u.uy + u.uhorizon; y++) + for (x = u.ux - u.uhorizon; x <= u.ux + u.uhorizon; x++) { + if (cansee(x, y)) + prl(x, y); + } +} + +#else + +void +setsee(void) +{ + int x, y; + + if (Blind) { + pru(); + return; + } + if (!levl[u.ux][u.uy].lit) { + seelx = u.ux - 1; + seehx = u.ux + 1; + seely = u.uy - 1; + seehy = u.uy + 1; + } else { + for (seelx = u.ux; levl[seelx - 1][u.uy].lit; seelx--) ; + for (seehx = u.ux; levl[seehx + 1][u.uy].lit; seehx++) ; + for (seely = u.uy; levl[u.ux][seely - 1].lit; seely--) ; + for (seehy = u.uy; levl[u.ux][seehy + 1].lit; seehy++) ; + } + for (y = seely; y <= seehy; y++) + for (x = seelx; x <= seehx; x++) + prl(x, y); + + if (!levl[u.ux][u.uy].lit) /* seems necessary elsewhere */ + seehx = 0; + else { + if (seely == u.uy) + for (x = u.ux - 1; x <= u.ux + 1; x++) + prl(x, seely - 1); + if (seehy == u.uy) + for (x = u.ux - 1; x <= u.ux + 1; x++) + prl(x, seehy + 1); + if (seelx == u.ux) + for (y = u.uy - 1; y <= u.uy + 1; y++) + prl(seelx - 1, y); + if (seehx == u.ux) + for (y = u.uy - 1; y <= u.uy + 1; y++) + prl(seehx + 1, y); + } +} +#endif /* QUEST */ + +void +nomul(int nval) +{ + if (multi < 0) + return; + multi = nval; + flags.mv = flags.run = 0; +} + +int +abon(void) +{ + if (u.ustr == 3) + return (-3); + else if (u.ustr < 6) + return (-2); + else if (u.ustr < 8) + return (-1); + else if (u.ustr < 17) + return (0); + else if (u.ustr < 69) /* up to 18/50 */ + return (1); + else if (u.ustr < 118) + return (2); + else + return (3); +} + +int +dbon(void) +{ + if (u.ustr < 6) + return (-1); + else if (u.ustr < 16) + return (0); + else if (u.ustr < 18) + return (1); + else if (u.ustr == 18) /* up to 18 */ + return (2); + else if (u.ustr < 94) /* up to 18/75 */ + return (3); + else if (u.ustr < 109) /* up to 18/90 */ + return (4); + else if (u.ustr < 118) /* up to 18/99 */ + return (5); + else + return (6); +} + +/* may kill you; cause may be poison or monster like 'A' */ +void +losestr(int num) +{ + u.ustr -= num; + while (u.ustr < 3) { + u.ustr++; + u.uhp -= 6; + u.uhpmax -= 6; + } + flags.botl = 1; +} + +void +losehp(int n, const char *knam) +{ + u.uhp -= n; + if (u.uhp > u.uhpmax) + u.uhpmax = u.uhp; /* perhaps n was negative */ + flags.botl = 1; + if (u.uhp < 1) { + killer = knam; /* the thing that killed you */ + done("died"); + } +} + +void +losehp_m(int n, struct monst *mtmp) +{ + u.uhp -= n; + flags.botl = 1; + if (u.uhp < 1) + done_in_by(mtmp); +} + +void +losexp(void) /* hit by V or W */ +{ + int num; + + if (u.ulevel > 1) + pline("Goodbye level %u.", u.ulevel--); + else + u.uhp = -1; + num = rnd(10); + u.uhp -= num; + u.uhpmax -= num; + u.uexp = newuexp(); + flags.botl = 1; +} + +int +inv_weight(void) +{ + struct obj *otmp = invent; + int wt = (u.ugold + 500) / 1000; + int carrcap; + + if (Levitation) /* pugh@cornell */ + carrcap = MAX_CARR_CAP; + else { + carrcap = 5 * (((u.ustr > 18) ? 20 : u.ustr) + u.ulevel); + if (carrcap > MAX_CARR_CAP) + carrcap = MAX_CARR_CAP; + if (Wounded_legs & LEFT_SIDE) + carrcap -= 10; + if (Wounded_legs & RIGHT_SIDE) + carrcap -= 10; + } + while (otmp) { + wt += otmp->owt; + otmp = otmp->nobj; + } + return (wt - carrcap); +} + +static int +inv_cnt(void) +{ + struct obj *otmp = invent; + int ct = 0; + + while (otmp) { + ct++; + otmp = otmp->nobj; + } + return (ct); +} + +long +newuexp(void) +{ + return (10 * (1L << (u.ulevel - 1))); +} diff --git a/hack/hack.cmd.c b/hack/hack.cmd.c new file mode 100644 index 0000000..d2aaed3 --- /dev/null +++ b/hack/hack.cmd.c @@ -0,0 +1,332 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.cmd.c - version 1.0.3 */ +/* $FreeBSD: src/games/hack/hack.cmd.c,v 1.4 1999/11/16 10:26:35 marcel Exp $ */ +/* $DragonFly: src/games/hack/hack.cmd.c,v 1.4 2006/08/21 19:45:32 pavalos Exp $ */ + +#include "hack.h" +#include "def.func_tab.h" + +static int doextcmd(void); +static char lowc(char); +static char unctrl(char); +#ifdef QUEST +static bool isroom(int, int); +#endif +static int done2(void); + +struct func_tab cmdlist[]={ + { '\020', doredotopl }, + { '\022', doredraw }, + { '\024', dotele }, +#ifdef SUSPEND + { '\032', dosuspend }, +#endif /* SUSPEND */ + { 'a', doapply }, + /*'A' : UNUSED */ + /*'b', 'B' : go sw */ + { 'c', ddocall }, + { 'C', do_mname }, + { 'd', dodrop }, + { 'D', doddrop }, + { 'e', doeat }, + { 'E', doengrave }, + /*'f', 'F' : multiple go (might become 'fight') */ + /*'g', 'G' : UNUSED */ + /*'h', 'H' : go west */ + { 'I', dotypeinv }, /* Robert Viduya */ + { 'i', ddoinv }, + /*'j', 'J', 'k', 'K', 'l', 'L', 'm', 'M', 'n', 'N' : move commands */ + /*'o', doopen, */ + { 'O', doset }, + { 'p', dopay }, + { 'P', dowearring }, + { 'q', dodrink }, + { 'Q', done2 }, + { 'r', doread }, + { 'R', doremring }, + { 's', dosearch }, + { 'S', dosave }, + { 't', dothrow }, + { 'T', doremarm }, + /*'u', 'U' : go ne */ + { 'v', doversion }, + /*'V' : UNUSED */ + { 'w', dowield }, + { 'W', doweararm }, + /*'x', 'X' : UNUSED */ + /*'y', 'Y' : go nw */ + { 'z', dozap }, + /*'Z' : UNUSED */ + { '<', doup }, + { '>', dodown }, + { '/', dowhatis }, + { '?', dohelp }, +#ifdef SHELL + { '!', dosh }, +#endif /* SHELL */ + { '.', donull }, + { ' ', donull }, + { ',', dopickup }, + { ':', dolook }, + { '^', doidtrap }, + { '\\', dodiscovered }, /* Robert Viduya */ + { WEAPON_SYM, doprwep }, + { ARMOR_SYM, doprarm }, + { RING_SYM, doprring }, + { '$', doprgold }, + { '#', doextcmd }, + { 0, 0 } +}; + +struct ext_func_tab extcmdlist[] = { + { "dip", dodip }, + { "pray", dopray }, + { NULL, donull } +}; + +extern char quitchars[]; + +void +rhack(const char *cmd) +{ + struct func_tab *tlist = cmdlist; + boolean firsttime = FALSE; + int res; + + if (!cmd) { + firsttime = TRUE; + flags.nopick = 0; + cmd = parse(); + } + if (!*cmd || (*cmd & 0377) == 0377 || + (flags.no_rest_on_space && *cmd == ' ')) { + bell(); + flags.move = 0; + return; /* probably we just had an interrupt */ + } + if (movecmd(*cmd)) { +walk: + if (multi) + flags.mv = 1; + domove(); + return; + } + if (movecmd(lowc(*cmd))) { + flags.run = 1; +rush: + if (firsttime) { + if (!multi) + multi = COLNO; + u.last_str_turn = 0; + } + flags.mv = 1; +#ifdef QUEST + if (flags.run >= 4) + finddir(); + if (firsttime) { + u.ux0 = u.ux + u.dx; + u.uy0 = u.uy + u.dy; + } +#endif /* QUEST */ + domove(); + return; + } + if ((*cmd == 'f' && movecmd(cmd[1])) || movecmd(unctrl(*cmd))) { + flags.run = 2; + goto rush; + } + if (*cmd == 'F' && movecmd(lowc(cmd[1]))) { + flags.run = 3; + goto rush; + } + if (*cmd == 'm' && movecmd(cmd[1])) { + flags.run = 0; + flags.nopick = 1; + goto walk; + } + if (*cmd == 'M' && movecmd(lowc(cmd[1]))) { + flags.run = 1; + flags.nopick = 1; + goto rush; + } +#ifdef QUEST + if (*cmd == cmd[1] && (*cmd == 'f' || *cmd == 'F')) { + flags.run = 4; + if (*cmd == 'F') + flags.run += 2; + if (cmd[2] == '-') + flags.run += 1; + goto rush; + } +#endif /* QUEST */ + while (tlist->f_char) { + if (*cmd == tlist->f_char) { + res = (*(tlist->f_funct))(); + if (!res) { + flags.move = 0; + multi = 0; + } + return; + } + tlist++; + } + { + char expcmd[10]; + char *cp = expcmd; + while (*cmd && cp - expcmd < (int)sizeof(expcmd) - 2) { + if (*cmd >= 040 && *cmd < 0177) + *cp++ = *cmd++; + else { + *cp++ = '^'; + *cp++ = *cmd++ ^ 0100; + } + } + *cp++ = 0; + pline("Unknown command '%s'.", expcmd); + } + multi = flags.move = 0; +} + +static int +doextcmd(void) /* here after # - now read a full-word command */ +{ + char buf[BUFSZ]; + struct ext_func_tab *efp = extcmdlist; + + pline("# "); + getlin(buf); + clrlin(); + if (buf[0] == '\033') + return (0); + while (efp->ef_txt) { + if (!strcmp(efp->ef_txt, buf)) + return ((*(efp->ef_funct))()); + efp++; + } + pline("%s: unknown command.", buf); + return (0); +} + +static char +lowc(char sym) +{ + return ((sym >= 'A' && sym <= 'Z') ? sym + 'a' - 'A' : sym); +} + +static char +unctrl(char sym) +{ + return ((sym >= ('A' & 037) && sym <= ('Z' & 037)) ? sym + 0140 : sym); +} + +/* 'rogue'-like direction commands */ +char sdir[] = "hykulnjb><"; +schar xdir[10] = { -1, -1, 0, 1, 1, 1, 0, -1, 0, 0 }; +schar ydir[10] = { 0, -1, -1, -1, 0, 1, 1, 1, 0, 0 }; +schar zdir[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, -1 }; + +bool +movecmd(char sym) /* also sets u.dz, but returns false for <> */ +{ + char *dp; + + u.dz = 0; + if (!(dp = strchr(sdir, sym))) + return (0); + u.dx = xdir[dp - sdir]; + u.dy = ydir[dp - sdir]; + u.dz = zdir[dp - sdir]; + return (!u.dz); +} + +bool +getdir(bool s) +{ + char dirsym; + + if (s) + pline("In what direction?"); + dirsym = readchar(); + if (!movecmd(dirsym) && !u.dz) { + if (!strchr(quitchars, dirsym)) + pline("What a strange direction!"); + return (0); + } + if (Confusion && !u.dz) + confdir(); + return (1); +} + +void +confdir(void) +{ + int x = rn2(8); + + u.dx = xdir[x]; + u.dy = ydir[x]; +} + +#ifdef QUEST +void +finddir(void) +{ + int i, ui = u.di; + + for (i = 0; i <= 8; i++) { + if (flags.run & 1) + ui++; + else + ui += 7; + ui %= 8; + if (i == 8) { + pline("Not near a wall."); + flags.move = multi = 0; + return; + } + if (!isroom(u.ux + xdir[ui], u.uy + ydir[ui])) + break; + } + for (i = 0; i <= 8; i++) { + if (flags.run & 1) + ui += 7; + else + ui++; + ui %= 8; + if (i == 8) { + pline("Not near a room."); + flags.move = multi = 0; + return; + } + if (isroom(u.ux + xdir[ui], u.uy + ydir[ui])) + break; + } + u.di = ui; + u.dx = xdir[ui]; + u.dy = ydir[ui]; +} + +static bool +isroom(int x, int y) +{ /* what about POOL? */ + return (isok(x, y) && (levl[x][y].typ == ROOM || + (levl[x][y].typ >= LDOOR && flags.run >= 6))); +} +#endif /* QUEST */ + +bool +isok(int x, int y) +{ + /* x corresponds to curx, so x==1 is the first column. Ach. %% */ + return (x >= 1 && x <= COLNO - 1 && y >= 0 && y <= ROWNO - 1); +} + +/* + * done2 is a function that fits into cmdlist[] (int func(void)) + * and calls done1 which discards its argument. + */ +static int +done2(void) +{ + done1(0); + return (0); +} diff --git a/hack/hack.do.c b/hack/hack.do.c new file mode 100644 index 0000000..8d6277d --- /dev/null +++ b/hack/hack.do.c @@ -0,0 +1,512 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.do.c - version 1.0.3 */ +/* $FreeBSD: src/games/hack/hack.do.c,v 1.4 1999/11/16 10:26:36 marcel Exp $ */ +/* $DragonFly: src/games/hack/hack.do.c,v 1.5 2006/08/21 19:45:32 pavalos Exp $ */ + +/* Contains code for 'd', 'D' (drop), '>', '<' (up, down) and 't' (throw) */ + +#include "hack.h" + +extern struct monst youmonst; + +static int drop(struct obj *); +static void dropy(struct obj *); + +int +dodrop(void) +{ + return (drop(getobj("0$#", "drop"))); +} + +static int +drop(struct obj *obj) +{ + if (!obj) + return (0); + if (obj->olet == '$') { /* pseudo object */ + long amount = OGOLD(obj); + + if (amount == 0) + pline("You didn't drop any gold pieces."); + else { + mkgold(amount, u.ux, u.uy); + pline("You dropped %ld gold piece%s.", + amount, plur(amount)); + if (Invisible) + newsym(u.ux, u.uy); + } + free(obj); + return (1); + } + if (obj->owornmask & (W_ARMOR | W_RING)) { + pline("You cannot drop something you are wearing."); + return (0); + } + if (obj == uwep) { + if (uwep->cursed) { + pline("Your weapon is welded to your hand!"); + return (0); + } + setuwep(NULL); + } + pline("You dropped %s.", doname(obj)); + dropx(obj); + return (1); +} + +/* Called in several places - should not produce texts */ +void +dropx(struct obj *obj) +{ + freeinv(obj); + dropy(obj); +} + +static void +dropy(struct obj *obj) +{ + if (obj->otyp == CRYSKNIFE) + obj->otyp = WORM_TOOTH; + obj->ox = u.ux; + obj->oy = u.uy; + obj->nobj = fobj; + fobj = obj; + if (Invisible) + newsym(u.ux, u.uy); + subfrombill(obj); + stackobj(obj); +} + +/* drop several things */ +int +doddrop(void) +{ + return (ggetobj("drop", drop, 0)); +} + +int +dodown(void) +{ + if (u.ux != xdnstair || u.uy != ydnstair) { + pline("You can't go down here."); + return (0); + } + if (u.ustuck) { + pline("You are being held, and cannot go down."); + return (1); + } + if (Levitation) { + pline("You're floating high above the stairs."); + return (0); + } + + goto_level(dlevel + 1, TRUE); + return (1); +} + +int +doup(void) +{ + if (u.ux != xupstair || u.uy != yupstair) { + pline("You can't go up here."); + return (0); + } + if (u.ustuck) { + pline("You are being held, and cannot go up."); + return (1); + } + if (!Levitation && inv_weight() + 5 > 0) { + pline("Your load is too heavy to climb the stairs."); + return (1); + } + + goto_level(dlevel - 1, TRUE); + return (1); +} + +void +goto_level(int newlevel, boolean at_stairs) +{ + int fd; + boolean up = (newlevel < dlevel); + + if (newlevel <= 0) /* in fact < 0 is impossible */ + done("escaped"); + if (newlevel > MAXLEVEL) /* strange ... */ + newlevel = MAXLEVEL; + if (newlevel == dlevel) /* this can happen */ + return; + + glo(dlevel); + fd = creat(lock, FMASK); + if (fd < 0) { + /* + * This is not quite impossible: e.g., we may have + * exceeded our quota. If that is the case then we + * cannot leave this level, and cannot save either. + * Another possibility is that the directory was not + * writable. + */ + pline("A mysterious force prevents you from going %s.", + up ? "up" : "down"); + return; + } + + if (Punished) + unplacebc(); + u.utrap = 0; /* needed in level_tele */ + u.ustuck = 0; /* idem */ + keepdogs(); + seeoff(1); + if (u.uswallow) /* idem */ + u.uswldtim = u.uswallow = 0; + flags.nscrinh = 1; + u.ux = FAR; /* hack */ + inshop(); /* probably was a trapdoor */ + + savelev(fd, dlevel); + close(fd); + + dlevel = newlevel; + if (maxdlevel < dlevel) + maxdlevel = dlevel; + glo(dlevel); + + if (!level_exists[dlevel]) + mklev(); + else { + if ((fd = open(lock, O_RDONLY)) < 0) { + pline("Cannot open %s .", lock); + pline("Probably someone removed it."); + done("tricked"); + } + getlev(fd, hackpid, dlevel); + close(fd); + } + + if (at_stairs) { + if (up) { + u.ux = xdnstair; + u.uy = ydnstair; + if (!u.ux) { /* entering a maze from below? */ + u.ux = xupstair; /* this will confuse the player! */ + u.uy = yupstair; + } + if (Punished && !Levitation) { + pline("With great effort you climb the stairs."); + placebc(1); + } + } else { + u.ux = xupstair; + u.uy = yupstair; + if (inv_weight() + 5 > 0 || Punished) { + pline("You fall down the stairs."); /* %% */ + losehp(rnd(3), "fall"); + if (Punished) { + if (uwep != uball && rn2(3)) { + pline("... and are hit by the iron ball."); + losehp(rnd(20), "iron ball"); + } + placebc(1); + } + selftouch("Falling, you"); + } + } + { + struct monst *mtmp = m_at(u.ux, u.uy); + if (mtmp) + mnexto(mtmp); + } + } else { /* trapdoor or level_tele */ + do { + u.ux = rnd(COLNO - 1); + u.uy = rn2(ROWNO); + } while (levl[u.ux][u.uy].typ != ROOM || + m_at(u.ux, u.uy)); + if (Punished) { + if (uwep != uball && !up /* %% */ && rn2(5)) { + pline("The iron ball falls on your head."); + losehp(rnd(25), "iron ball"); + } + placebc(1); + } + selftouch("Falling, you"); + } + inshop(); + initrack(); + + losedogs(); + { + struct monst *mtmp; + if ((mtmp = m_at(u.ux, u.uy))) /* riv05!a3 */ + mnexto(mtmp); + } + flags.nscrinh = 0; + setsee(); + seeobjs(); /* make old cadavers disappear - riv05!a3 */ + docrt(); + pickup(1); + read_engr_at(u.ux, u.uy); +} + +int +donull(void) +{ + return (1); /* Do nothing, but let other things happen */ +} + +int +dopray(void) +{ + nomovemsg = "You finished your prayer."; + nomul(-3); + return (1); +} + +int +dothrow(void) +{ + struct obj *obj; + struct monst *mon; + int tmp; + + obj = getobj("#)", "throw"); /* it is also possible to throw food */ + /* (or jewels, or iron balls ... ) */ + if (!obj || !getdir(1)) /* ask "in what direction?" */ + return (0); + if (obj->owornmask & (W_ARMOR | W_RING)) { + pline("You can't throw something you are wearing."); + return (0); + } + + u_wipe_engr(2); + + if (obj == uwep) { + if (obj->cursed) { + pline("Your weapon is welded to your hand."); + return (1); + } + if (obj->quan > 1) + setuwep(splitobj(obj, 1)); + else + setuwep(NULL); + } else if (obj->quan > 1) + splitobj(obj, 1); + freeinv(obj); + if (u.uswallow) { + mon = u.ustuck; + bhitpos.x = mon->mx; + bhitpos.y = mon->my; + } else if (u.dz) { + if (u.dz < 0) { + pline("%s hits the ceiling, then falls back on top of your head.", + Doname(obj)); /* note: obj->quan == 1 */ + if (obj->olet == POTION_SYM) + potionhit(&youmonst, obj); + else { + if (uarmh) + pline("Fortunately, you are wearing a helmet!"); + losehp(uarmh ? 1 : rnd((int)(obj->owt)), + "falling object"); + dropy(obj); + } + } else { + pline("%s hits the floor.", Doname(obj)); + if (obj->otyp == EXPENSIVE_CAMERA) { + pline("It is shattered in a thousand pieces!"); + obfree(obj, NULL); + } else if (obj->otyp == EGG) { + pline("\"Splash!\""); + obfree(obj, NULL); + } else if (obj->olet == POTION_SYM) { + pline("The flask breaks, and you smell a peculiar odor ..."); + potionbreathe(obj); + obfree(obj, NULL); + } else + dropy(obj); + } + return (1); + } else if (obj->otyp == BOOMERANG) { + mon = boomhit(u.dx, u.dy); + if (mon == &youmonst) { /* the thing was caught */ + addinv(obj); + return (1); + } + } else { + if (obj->otyp == PICK_AXE && shkcatch(obj)) + return (1); + + mon = bhit(u.dx, u.dy, (obj->otyp == ICE_BOX) ? 1 : + (!Punished || obj != uball) ? 8 : !u.ustuck ? 5 : 1, + obj->olet, (void (*)(struct monst *, struct obj *)) 0, + (bool (*)(struct obj *, struct obj *)) 0, obj); + } + if (mon) { + /* awake monster if sleeping */ + wakeup(mon); + + if (obj->olet == WEAPON_SYM) { + tmp = -1 + u.ulevel + mon->data->ac + abon(); + if (obj->otyp < ROCK) { + if (!uwep || + uwep->otyp != obj->otyp + (BOW - ARROW)) + tmp -= 4; + else { + tmp += uwep->spe; + } + } else if (obj->otyp == BOOMERANG) + tmp += 4; + tmp += obj->spe; + if (u.uswallow || tmp >= rnd(20)) { + if (hmon(mon, obj, 1) == TRUE) { + /* mon still alive */ +#ifndef NOWORM + cutworm(mon, bhitpos.x, bhitpos.y, obj->otyp); +#endif /* NOWORM */ + } else + mon = 0; + /* weapons thrown disappear sometimes */ + if (obj->otyp < BOOMERANG && rn2(3)) { + /* check bill; free */ + obfree(obj, NULL); + return (1); + } + } else + miss(objects[obj->otyp].oc_name, mon); + } else if (obj->otyp == HEAVY_IRON_BALL) { + tmp = -1 + u.ulevel + mon->data->ac + abon(); + if (!Punished || obj != uball) + tmp += 2; + if (u.utrap) + tmp -= 2; + if (u.uswallow || tmp >= rnd(20)) { + if (hmon(mon, obj, 1) == FALSE) + mon = 0; /* he died */ + } else + miss("iron ball", mon); + } else if (obj->olet == POTION_SYM && u.ulevel > rn2(15)) { + potionhit(mon, obj); + return (1); + } else { + if (cansee(bhitpos.x, bhitpos.y)) + pline("You miss %s.", monnam(mon)); + else + pline("You miss it."); + if (obj->olet == FOOD_SYM && mon->data->mlet == 'd') + if (tamedog(mon, obj)) + return (1); + if (obj->olet == GEM_SYM && mon->data->mlet == 'u' && + !mon->mtame) { + if (obj->dknown && objects[obj->otyp].oc_name_known) { + if (objects[obj->otyp].g_val > 0) { + u.uluck += 5; + goto valuable; + } else + pline("%s is not interested in your junk.", + Monnam(mon)); + } else { /* value unknown to @ */ + u.uluck++; +valuable: + if (u.uluck > LUCKMAX) /* dan@ut-ngp */ + u.uluck = LUCKMAX; + pline("%s graciously accepts your gift.", + Monnam(mon)); + mpickobj(mon, obj); + rloc(mon); + return (1); + } + } + } + } + /* the code following might become part of dropy() */ + if (obj->otyp == CRYSKNIFE) + obj->otyp = WORM_TOOTH; + obj->ox = bhitpos.x; + obj->oy = bhitpos.y; + obj->nobj = fobj; + fobj = obj; + /* prevent him from throwing articles to the exit and escaping */ + /* subfrombill(obj); */ + stackobj(obj); + if (Punished && obj == uball && + (bhitpos.x != u.ux || bhitpos.y != u.uy)) { + freeobj(uchain); + unpobj(uchain); + if (u.utrap) { + if (u.utraptype == TT_PIT) + pline("The ball pulls you out of the pit!"); + else { + long side = rn2(3) ? LEFT_SIDE : RIGHT_SIDE; + pline("The ball pulls you out of the bear trap."); + pline("Your %s leg is severely damaged.", + (side == LEFT_SIDE) ? "left" : "right"); + set_wounded_legs(side, 500 + rn2(1000)); + losehp(2, "thrown ball"); + } + u.utrap = 0; + } + unsee(); + uchain->nobj = fobj; + fobj = uchain; + u.ux = uchain->ox = bhitpos.x - u.dx; + u.uy = uchain->oy = bhitpos.y - u.dy; + setsee(); + inshop(); + } + if (cansee(bhitpos.x, bhitpos.y)) + prl(bhitpos.x, bhitpos.y); + return (1); +} + +/* split obj so that it gets size num */ +/* remainder is put in the object structure delivered by this call */ +struct obj * +splitobj(struct obj *obj, int num) +{ + struct obj *otmp; + + otmp = newobj(0); + *otmp = *obj; /* copies whole structure */ + otmp->o_id = flags.ident++; + otmp->onamelth = 0; + obj->quan = num; + obj->owt = weight(obj); + otmp->quan -= num; + otmp->owt = weight(otmp); /* -= obj->owt ? */ + obj->nobj = otmp; + if (obj->unpaid) + splitbill(obj, otmp); + return (otmp); +} + +void +more_experienced(int exp, int rexp) +{ + u.uexp += exp; + u.urexp += 4 * exp + rexp; + if (exp) + flags.botl = 1; + if (u.urexp >= ((pl_character[0] == 'W') ? 1000 : 2000)) + flags.beginner = 0; +} + +void +set_wounded_legs(long side, int timex) +{ + if (!Wounded_legs || (Wounded_legs & TIMEOUT)) + Wounded_legs |= side + timex; + else + Wounded_legs |= side; +} + +void +heal_legs(void) +{ + if (Wounded_legs) { + if ((Wounded_legs & BOTH_SIDES) == BOTH_SIDES) + pline("Your legs feel somewhat better."); + else + pline("Your leg feels somewhat better."); + Wounded_legs = 0; + } +} diff --git a/hack/hack.do_name.c b/hack/hack.do_name.c new file mode 100644 index 0000000..38ef837 --- /dev/null +++ b/hack/hack.do_name.c @@ -0,0 +1,316 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.do_name.c - version 1.0.3 */ +/* $FreeBSD: src/games/hack/hack.do_name.c,v 1.5 1999/11/16 10:26:36 marcel Exp $ */ +/* $DragonFly: src/games/hack/hack.do_name.c,v 1.4 2006/08/21 19:45:32 pavalos Exp $ */ + +#include "hack.h" + +static void do_oname(struct obj *); +static char *xmonnam(struct monst *, int); +static char *lmonnam(struct monst *); +static char *visctrl(char); + +coord +getpos(int force, const char *goal) +{ + int cx, cy, i, c; + coord cc; + + pline("(For instructions type a ?)"); + cx = u.ux; + cy = u.uy; + curs(cx, cy + 2); + while ((c = readchar()) != '.') { + for (i = 0; i < 8; i++) + if (sdir[i] == c) { + if (1 <= cx + xdir[i] && cx + xdir[i] <= COLNO) + cx += xdir[i]; + if (0 <= cy + ydir[i] && cy + ydir[i] <= ROWNO - 1) + cy += ydir[i]; + goto nxtc; + } + if (c == '?') { + pline("Use [hjkl] to move the cursor to %s.", goal); + pline("Type a . when you are at the right place."); + } else { + pline("Unknown direction: '%s' (%s).", + visctrl(c), + force ? "use hjkl or ." : "aborted"); + if (force) + goto nxtc; + cc.x = -1; + cc.y = 0; + return (cc); + } +nxtc: + curs(cx, cy + 2); + } + cc.x = cx; + cc.y = cy; + return (cc); +} + +int +do_mname(void) +{ + char buf[BUFSZ]; + coord cc; + int cx, cy, lth, i; + struct monst *mtmp, *mtmp2; + + cc = getpos(0, "the monster you want to name"); + cx = cc.x; + cy = cc.y; + if (cx < 0) + return (0); + mtmp = m_at(cx, cy); + if (!mtmp) { + if (cx == u.ux && cy == u.uy) + pline("This ugly monster is called %s and cannot be renamed.", + plname); + else + pline("There is no monster there."); + return (1); + } + if (mtmp->mimic) { + pline("I see no monster there."); + return (1); + } + if (!cansee(cx, cy)) { + pline("I cannot see a monster there."); + return (1); + } + pline("What do you want to call %s? ", lmonnam(mtmp)); + getlin(buf); + clrlin(); + if (!*buf || *buf == '\033') + return (1); + lth = strlen(buf) + 1; + if (lth > 63) { + buf[62] = 0; + lth = 63; + } + mtmp2 = newmonst(mtmp->mxlth + lth); + *mtmp2 = *mtmp; + for (i = 0; (unsigned)i < mtmp->mxlth; i++) + ((char *)mtmp2->mextra)[i] = ((char *)mtmp->mextra)[i]; + mtmp2->mnamelth = lth; + strcpy(NAME(mtmp2), buf); + replmon(mtmp, mtmp2); + return (1); +} + +/* + * This routine changes the address of obj . Be careful not to call it + * when there might be pointers around in unknown places. For now: only + * when obj is in the inventory. + */ +static void +do_oname(struct obj *obj) +{ + struct obj *otmp, *otmp2; + int lth; + char buf[BUFSZ]; + + pline("What do you want to name %s? ", doname(obj)); + getlin(buf); + clrlin(); + if (!*buf || *buf == '\033') + return; + lth = strlen(buf) + 1; + if (lth > 63) { + buf[62] = 0; + lth = 63; + } + otmp2 = newobj(lth); + *otmp2 = *obj; + otmp2->onamelth = lth; + strcpy(ONAME(otmp2), buf); + + setworn(NULL, obj->owornmask); + setworn(otmp2, otmp2->owornmask); + + /* + * do freeinv(obj); etc. by hand in order to preserve the position of + * this object in the inventory + */ + if (obj == invent) + invent = otmp2; + else + for (otmp = invent;; otmp = otmp->nobj) { + if (!otmp) + panic("Do_oname: cannot find obj."); + if (otmp->nobj == obj) { + otmp->nobj = otmp2; + break; + } + } + /*obfree(obj, otmp2);*/ /* now unnecessary: no pointers on bill */ + free(obj); /* let us hope nobody else saved a pointer */ +} + +int +ddocall(void) +{ + struct obj *obj; + + pline("Do you want to name an individual object? [ny] "); + switch (readchar()) { + case '\033': + break; + case 'y': + obj = getobj("#", "name"); + if (obj) + do_oname(obj); + break; + default: + obj = getobj("?!=/", "call"); + if (obj) + docall(obj); + } + return (0); +} + +void +docall(struct obj *obj) +{ + char buf[BUFSZ]; + struct obj otemp; + char **str1; + char *str; + + otemp = *obj; + otemp.quan = 1; + otemp.onamelth = 0; + str = xname(&otemp); + pline("Call %s %s: ", strchr(vowels, *str) ? "an" : "a", str); + getlin(buf); + clrlin(); + if (!*buf || *buf == '\033') + return; + str = newstring(strlen(buf) + 1); + strcpy(str, buf); + str1 = &(objects[obj->otyp].oc_uname); + if (*str1) + free(*str1); + *str1 = str; +} + +/* these names should have length < PL_NSIZ */ +const char *ghostnames[] = { + "adri", "andries", "andreas", "bert", "david", "dirk", "emile", + "frans", "fred", "greg", "hether", "jay", "john", "jon", "kay", + "kenny", "maud", "michiel", "mike", "peter", "robert", "ron", + "tom", "wilmar" +}; + +static char * +xmonnam(struct monst *mtmp, int vb) +{ + static char buf[BUFSZ]; /* %% */ + + if (mtmp->mnamelth && !vb) { + strcpy(buf, NAME(mtmp)); + return (buf); + } + switch (mtmp->data->mlet) { + case ' ': + { + const char *gn = (const char *)mtmp->mextra; + if (!*gn) { /* might also look in scorefile */ + gn = ghostnames[rn2(SIZE(ghostnames))]; + if (!rn2(2)) + strcpy((char *)mtmp->mextra, + !rn2(5) ? plname : gn); + } + sprintf(buf, "%s's ghost", gn); + } + break; + case '@': + if (mtmp->isshk) { + strcpy(buf, shkname(mtmp)); + break; + } + /* fall into next case */ + default: + sprintf(buf, "the %s%s", + mtmp->minvis ? "invisible " : "", + mtmp->data->mname); + } + if (vb && mtmp->mnamelth) { + strcat(buf, " called "); + strcat(buf, NAME(mtmp)); + } + return (buf); +} + +static char * +lmonnam(struct monst *mtmp) +{ + return (xmonnam(mtmp, 1)); +} + +char * +monnam(struct monst *mtmp) +{ + return (xmonnam(mtmp, 0)); +} + +char * +Monnam(struct monst *mtmp) +{ + char *bp = monnam(mtmp); + + if ('a' <= *bp && *bp <= 'z') + *bp += ('A' - 'a'); + return (bp); +} + +char * +amonnam(struct monst *mtmp, const char *adj) +{ + char *bp = monnam(mtmp); + static char buf[BUFSZ]; /* %% */ + + if (!strncmp(bp, "the ", 4)) + bp += 4; + sprintf(buf, "the %s %s", adj, bp); + return (buf); +} + +char * +Amonnam(struct monst *mtmp, const char *adj) +{ + char *bp = amonnam(mtmp, adj); + + *bp = 'T'; + return (bp); +} + +char * +Xmonnam(struct monst *mtmp) +{ + char *bp = Monnam(mtmp); + + if (!strncmp(bp, "The ", 4)) { + bp += 2; + *bp = 'A'; + } + return (bp); +} + +static char * +visctrl(char c) +{ + static char ccc[3]; + + if (c < 040) { + ccc[0] = '^'; + ccc[1] = c + 0100; + ccc[2] = 0; + } else { + ccc[0] = c; + ccc[1] = 0; + } + return (ccc); +} diff --git a/hack/hack.do_wear.c b/hack/hack.do_wear.c new file mode 100644 index 0000000..c7f7802 --- /dev/null +++ b/hack/hack.do_wear.c @@ -0,0 +1,395 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.do_wear.c - version 1.0.3 */ +/* $FreeBSD: src/games/hack/hack.do_wear.c,v 1.3 1999/11/16 02:57:03 billf Exp $ */ +/* $DragonFly: src/games/hack/hack.do_wear.c,v 1.6 2008/04/20 13:44:24 swildner Exp $ */ + +#include "hack.h" +extern char quitchars[]; + +static void off_msg(struct obj *); +static int dorr(struct obj *); +static bool cursed(struct obj *); + +static void +off_msg(struct obj *otmp) +{ + pline("You were wearing %s.", doname(otmp)); +} + +int +doremarm(void) +{ + struct obj *otmp; + + if (!uarm && !uarmh && !uarms && !uarmg) { + pline("Not wearing any armor."); + return (0); + } + otmp = (!uarmh && !uarms && !uarmg) ? uarm : + (!uarms && !uarm && !uarmg) ? uarmh : + (!uarmh && !uarm && !uarmg) ? uarms : + (!uarmh && !uarm && !uarms) ? uarmg : + getobj("[", "take off"); + if (!otmp) + return (0); + if (!(otmp->owornmask & (W_ARMOR - W_ARM2))) { + pline("You can't take that off."); + return (0); + } + if (otmp == uarmg && uwep && uwep->cursed) { /* myers@uwmacc */ + pline("You seem not able to take off the gloves while holding your weapon."); + return (0); + } + armoroff(otmp); + return (1); +} + +int +doremring(void) +{ + if (!uleft && !uright) { + pline("Not wearing any ring."); + return (0); + } + if (!uleft) + return (dorr(uright)); + if (!uright) + return (dorr(uleft)); + if (uleft && uright) + for (;;) { + char answer; + + pline("What ring, Right or Left? [ rl?]"); + if (strchr(quitchars, (answer = readchar()))) + return (0); + switch (answer) { + case 'l': + case 'L': + return (dorr(uleft)); + case 'r': + case 'R': + return (dorr(uright)); + case '?': + doprring(); + /* might look at morc here %% */ + } + } + /* NOTREACHED */ + return (0); +} + +static int +dorr(struct obj *otmp) +{ + if (cursed(otmp)) + return (0); + ringoff(otmp); + off_msg(otmp); + return (1); +} + +static bool +cursed(struct obj *otmp) +{ + if (otmp->cursed) { + pline("You can't. It appears to be cursed."); + return (1); + } + return (0); +} + +bool +armoroff(struct obj *otmp) +{ + int delay = -objects[otmp->otyp].oc_delay; + + if (cursed(otmp)) + return (0); + setworn(NULL, otmp->owornmask & W_ARMOR); + if (delay) { + nomul(delay); + switch (otmp->otyp) { + case HELMET: + nomovemsg = "You finished taking off your helmet."; + break; + case PAIR_OF_GLOVES: + nomovemsg = "You finished taking off your gloves"; + break; + default: + nomovemsg = "You finished taking off your suit."; + } + } else + off_msg(otmp); + return (1); +} + +int +doweararm(void) +{ + struct obj *otmp; + int delay; + int err = 0; + long mask = 0; + + otmp = getobj("[", "wear"); + if (!otmp) + return (0); + if (otmp->owornmask & W_ARMOR) { + pline("You are already wearing that!"); + return (0); + } + if (otmp->otyp == HELMET) { + if (uarmh) { + pline("You are already wearing a helmet."); + err++; + } else + mask = W_ARMH; + } else if (otmp->otyp == SHIELD) { + if (uarms) + pline("You are already wearing a shield."), err++; + if (uwep && uwep->otyp == TWO_HANDED_SWORD) { + pline("You cannot wear a shield and wield a two-handed sword."); + err++; + } + if (!err) + mask = W_ARMS; + } else if (otmp->otyp == PAIR_OF_GLOVES) { + if (uarmg) { + pline("You are already wearing gloves."); + err++; + } else if (uwep && uwep->cursed) { + pline("You cannot wear gloves over your weapon."); + err++; + } else + mask = W_ARMG; + } else { + if (uarm) { + if (otmp->otyp != ELVEN_CLOAK || uarm2) { + pline("You are already wearing some armor."); + err++; + } + } + if (!err) + mask = W_ARM; + } + if (otmp == uwep && uwep->cursed) { + if (!err++) + pline("%s is welded to your hand.", Doname(uwep)); + } + if (err) + return (0); + setworn(otmp, mask); + if (otmp == uwep) + setuwep(NULL); + delay = -objects[otmp->otyp].oc_delay; + if (delay) { + nomul(delay); + nomovemsg = "You finished your dressing manoeuvre."; + } + otmp->known = 1; + return (1); +} + +int +dowearring(void) +{ + struct obj *otmp; + long mask = 0; + long oldprop; + + if (uleft && uright) { + pline("There are no more ring-fingers to fill."); + return (0); + } + otmp = getobj("=", "wear"); + if (!otmp) + return (0); + if (otmp->owornmask & W_RING) { + pline("You are already wearing that!"); + return (0); + } + if (otmp == uleft || otmp == uright) { + pline("You are already wearing that."); + return (0); + } + if (otmp == uwep && uwep->cursed) { + pline("%s is welded to your hand.", Doname(uwep)); + return (0); + } + if (uleft) + mask = RIGHT_RING; + else if (uright) + mask = LEFT_RING; + else + do { + char answer; + + pline("What ring-finger, Right or Left? "); + if (strchr(quitchars, (answer = readchar()))) + return (0); + switch (answer) { + case 'l': + case 'L': + mask = LEFT_RING; + break; + case 'r': + case 'R': + mask = RIGHT_RING; + break; + } + } while (!mask); + setworn(otmp, mask); + if (otmp == uwep) + setuwep(NULL); + oldprop = u.uprops[PROP(otmp->otyp)].p_flgs; + u.uprops[PROP(otmp->otyp)].p_flgs |= mask; + switch (otmp->otyp) { + case RIN_LEVITATION: + if (!oldprop) + float_up(); + break; + case RIN_PROTECTION_FROM_SHAPE_CHANGERS: + rescham(); + break; + case RIN_GAIN_STRENGTH: + u.ustr += otmp->spe; + u.ustrmax += otmp->spe; + if (u.ustr > 118) + u.ustr = 118; + if (u.ustrmax > 118) + u.ustrmax = 118; + flags.botl = 1; + break; + case RIN_INCREASE_DAMAGE: + u.udaminc += otmp->spe; + break; + } + prinv(otmp); + return (1); +} + +void +ringoff(struct obj *obj) +{ + long mask; + + mask = obj->owornmask & W_RING; + setworn(NULL, obj->owornmask); + if (!(u.uprops[PROP(obj->otyp)].p_flgs & mask)) + impossible("Strange... I didn't know you had that ring."); + u.uprops[PROP(obj->otyp)].p_flgs &= ~mask; + switch (obj->otyp) { + case RIN_FIRE_RESISTANCE: + /* Bad luck if the player is in hell... --jgm */ + if (!Fire_resistance && dlevel >= 30) { + pline("The flames of Hell burn you to a crisp."); + killer = "stupidity in hell"; + done("burned"); + } + break; + case RIN_LEVITATION: + if (!Levitation) /* no longer floating */ + float_down(); + break; + case RIN_GAIN_STRENGTH: + u.ustr -= obj->spe; + u.ustrmax -= obj->spe; + if (u.ustr > 118) + u.ustr = 118; + if (u.ustrmax > 118) + u.ustrmax = 118; + flags.botl = 1; + break; + case RIN_INCREASE_DAMAGE: + u.udaminc -= obj->spe; + break; + } +} + +void +find_ac(void) +{ + int uac = 10; + + if (uarm) + uac -= ARM_BONUS(uarm); + if (uarm2) + uac -= ARM_BONUS(uarm2); + if (uarmh) + uac -= ARM_BONUS(uarmh); + if (uarms) + uac -= ARM_BONUS(uarms); + if (uarmg) + uac -= ARM_BONUS(uarmg); + if (uleft && uleft->otyp == RIN_PROTECTION) + uac -= uleft->spe; + if (uright && uright->otyp == RIN_PROTECTION) + uac -= uright->spe; + if (uac != u.uac) { + u.uac = uac; + flags.botl = 1; + } +} + +void +glibr(void) +{ + struct obj *otmp; + int xfl = 0; + + if (!uarmg) + if (uleft || uright) { + /* Note: at present also cursed rings fall off */ + pline("Your %s off your fingers.", + (uleft && uright) ? "rings slip" : "ring slips"); + xfl++; + if ((otmp = uleft) != NULL) { + ringoff(uleft); + dropx(otmp); + } + if ((otmp = uright) != NULL) { + ringoff(uright); + dropx(otmp); + } + } + if ((otmp = uwep) != NULL) { + /* Note: at present also cursed weapons fall */ + setuwep(NULL); + dropx(otmp); + pline("Your weapon %sslips from your hands.", + xfl ? "also " : ""); + } +} + +struct obj * +some_armor(void) +{ + struct obj *otmph = uarm; + + if (uarmh && (!otmph || !rn2(4))) + otmph = uarmh; + if (uarmg && (!otmph || !rn2(4))) + otmph = uarmg; + if (uarms && (!otmph || !rn2(4))) + otmph = uarms; + return (otmph); +} + +void +corrode_armor(void) +{ + struct obj *otmph = some_armor(); + + if (otmph) { + if (otmph->rustfree || + otmph->otyp == ELVEN_CLOAK || + otmph->otyp == LEATHER_ARMOR || + otmph->otyp == STUDDED_LEATHER_ARMOR) { + pline("Your %s not affected!", + aobjnam(otmph, "are")); + return; + } + pline("Your %s!", aobjnam(otmph, "corrode")); + otmph->spe--; + } +} diff --git a/hack/hack.dog.c b/hack/hack.dog.c new file mode 100644 index 0000000..531a3a4 --- /dev/null +++ b/hack/hack.dog.c @@ -0,0 +1,459 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.dog.c - version 1.0.3 */ +/* $FreeBSD: src/games/hack/hack.dog.c,v 1.3 1999/11/16 02:57:03 billf Exp $ */ + +#include "hack.h" +#include "hack.mfndpos.h" +#include "def.edog.h" + +struct permonst li_dog = + { "little dog", 'd', 2, 18, 6, 1, 6, sizeof(struct edog) }; +struct permonst dog = + { "dog", 'd', 4, 16, 5, 1, 6, sizeof(struct edog) }; +struct permonst la_dog = + { "large dog", 'd', 6, 15, 4, 2, 4, sizeof(struct edog) }; + +static void initedog(struct monst *); +static xchar dogfood(struct obj *); + +void +makedog(void) +{ + struct monst *mtmp = makemon(&li_dog, u.ux, u.uy); + + if (!mtmp) + return; /* dogs were genocided */ + initedog(mtmp); +} + +static void +initedog(struct monst *mtmp) +{ + mtmp->mtame = mtmp->mpeaceful = 1; + EDOG(mtmp)->hungrytime = 1000 + moves; + EDOG(mtmp)->eattime = 0; + EDOG(mtmp)->droptime = 0; + EDOG(mtmp)->dropdist = 10000; + EDOG(mtmp)->apport = 10; + EDOG(mtmp)->whistletime = 0; +} + +/* attach the monsters that went down (or up) together with @ */ +struct monst *mydogs = NULL; +struct monst *fallen_down = NULL; /* monsters that fell through a trapdoor */ +/* they will appear on the next level @ goes to, even if he goes up! */ + +void +losedogs(void) +{ + struct monst *mtmp; + + while ((mtmp = mydogs)) { + mydogs = mtmp->nmon; + mtmp->nmon = fmon; + fmon = mtmp; + mnexto(mtmp); + } + while ((mtmp = fallen_down)) { + fallen_down = mtmp->nmon; + mtmp->nmon = fmon; + fmon = mtmp; + rloc(mtmp); + } +} + +void +keepdogs(void) +{ + struct monst *mtmp; + + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) + if (dist(mtmp->mx, mtmp->my) < 3 && follower(mtmp) + && !mtmp->msleep && !mtmp->mfroz) { + relmon(mtmp); + mtmp->nmon = mydogs; + mydogs = mtmp; + unpmon(mtmp); + keepdogs(); /* we destroyed the link, so use recursion */ + return; /* (admittedly somewhat primitive) */ + } +} + +void +fall_down(struct monst *mtmp) +{ + relmon(mtmp); + mtmp->nmon = fallen_down; + fallen_down = mtmp; + unpmon(mtmp); + mtmp->mtame = 0; +} + +/* return quality of food; the lower the better */ +#define DOGFOOD 0 +#define CADAVER 1 +#define ACCFOOD 2 +#define MANFOOD 3 +#define APPORT 4 +#define POISON 5 +#define UNDEF 6 + +static xchar +dogfood(struct obj *obj) +{ + switch (obj->olet) { + case FOOD_SYM: + return ( + (obj->otyp == TRIPE_RATION) ? DOGFOOD : + (obj->otyp < CARROT) ? ACCFOOD : + (obj->otyp < CORPSE) ? MANFOOD : + (poisonous(obj) || obj->age + 50 <= moves || + obj->otyp == DEAD_COCKATRICE) + ? POISON : CADAVER + ); + default: + if (!obj->cursed) + return (APPORT); + /* fall into next case */ + case BALL_SYM: + case CHAIN_SYM: + case ROCK_SYM: + return (UNDEF); + } +} + +/* return 0 (no move), 1 (move) or 2 (dead) */ +int +dog_move(struct monst *mtmp, int after) +{ + int nx, ny, omx, omy, appr, nearer, j; + int udist, chi = 0, i, whappr; + struct monst *mtmp2; + struct permonst *mdat = mtmp->data; + struct edog *edog = EDOG(mtmp); + struct obj *obj; + struct trap *trap; + xchar cnt, chcnt, nix, niy; + schar dogroom, uroom; + xchar gx, gy, gtyp, otyp; /* current goal */ + coord poss[9]; + int info[9]; +#define GDIST(x, y) ((x - gx) * (x - gx) + (y - gy) * (y - gy)) +#define DDIST(x, y) ((x - omx) * (x - omx) + (y - omy) * (y - omy)) + + if (moves <= edog->eattime) /* dog is still eating */ + return (0); + omx = mtmp->mx; + omy = mtmp->my; + whappr = (moves - EDOG(mtmp)->whistletime < 5); + if (moves > edog->hungrytime + 500 && !mtmp->mconf) { + mtmp->mconf = 1; + mtmp->mhpmax /= 3; + if (mtmp->mhp > mtmp->mhpmax) + mtmp->mhp = mtmp->mhpmax; + if (cansee(omx, omy)) + pline("%s is confused from hunger.", Monnam(mtmp)); + else + pline("You feel worried about %s.", monnam(mtmp)); + } else if (moves > edog->hungrytime + 750 || mtmp->mhp < 1) { + if (cansee(omx, omy)) + pline("%s dies from hunger.", Monnam(mtmp)); + else + pline("You have a sad feeling for a moment, then it passes."); + mondied(mtmp); + return (2); + } + dogroom = inroom(omx, omy); + uroom = inroom(u.ux, u.uy); + udist = dist(omx, omy); + + /* maybe we tamed him while being swallowed --jgm */ + if (!udist) + return (0); + + /* if we are carrying sth then we drop it (perhaps near @) */ + /* Note: if apport == 1 then our behaviour is independent of udist */ + if (mtmp->minvent) { + if (!rn2(udist) || !rn2((int)edog->apport)) + if (rn2(10) < (int)edog->apport) { + relobj(mtmp, (int)mtmp->minvis); + if (edog->apport > 1) + edog->apport--; + edog->dropdist = udist; /* hpscdi!jon */ + edog->droptime = moves; + } + } else if ((obj = o_at(omx, omy))) { + if (!strchr("0_", obj->olet)) { + if ((otyp = dogfood(obj)) <= CADAVER) { + nix = omx; + niy = omy; + goto eatobj; + } + if (obj->owt < 10 * mtmp->data->mlevel) { + if (rn2(20) < (int)edog->apport + 3) { + if (rn2(udist) || + !rn2((int)edog->apport)) { + freeobj(obj); + unpobj(obj); + /* if (levl[omx][omy].scrsym == obj->olet) + * newsym(omx, omy); */ + mpickobj(mtmp, obj); + } + } + } + } + } + + /* first we look for food */ + gtyp = UNDEF; /* no goal as yet */ + gx = gy = 0; /* suppress 'used before set' message */ + for (obj = fobj; obj; obj = obj->nobj) { + otyp = dogfood(obj); + if (otyp > gtyp || otyp == UNDEF) + continue; + if (inroom(obj->ox, obj->oy) != dogroom) + continue; + if (otyp < MANFOOD && + (dogroom >= 0 || DDIST(obj->ox, obj->oy) < 10)) { + if (otyp < gtyp || (otyp == gtyp && + DDIST(obj->ox, obj->oy) < DDIST(gx, gy))) { + gx = obj->ox; + gy = obj->oy; + gtyp = otyp; + } + } else if (gtyp == UNDEF && dogroom >= 0 && + uroom == dogroom && + !mtmp->minvent && (int)edog->apport > rn2(8)) { + gx = obj->ox; + gy = obj->oy; + gtyp = APPORT; + } + } + if (gtyp == UNDEF || + (gtyp != DOGFOOD && gtyp != APPORT && moves < edog->hungrytime)) { + if (dogroom < 0 || dogroom == uroom) { + gx = u.ux; + gy = u.uy; +#ifndef QUEST + } else { + int tmp = rooms[dogroom].fdoor; + cnt = rooms[dogroom].doorct; + + gx = gy = FAR; /* random, far away */ + while (cnt--) { + if (dist(gx, gy) > + dist(doors[tmp].x, doors[tmp].y)) { + gx = doors[tmp].x; + gy = doors[tmp].y; + } + tmp++; + } + /* here gx == FAR e.g. when dog is in a vault */ + if (gx == FAR || (gx == omx && gy == omy)) { + gx = u.ux; + gy = u.uy; + } +#endif /* QUEST */ + } + appr = (udist >= 9) ? 1 : (mtmp->mflee) ? -1 : 0; + if (after && udist <= 4 && gx == u.ux && gy == u.uy) + return (0); + if (udist > 1) { + if (!IS_ROOM(levl[u.ux][u.uy].typ) || !rn2(4) || + whappr || + (mtmp->minvent && rn2((int)edog->apport))) + appr = 1; + } + /* if you have dog food he'll follow you more closely */ + if (appr == 0) { + obj = invent; + while (obj) { + if (obj->otyp == TRIPE_RATION) { + appr = 1; + break; + } + obj = obj->nobj; + } + } + } else /* gtyp != UNDEF */ + appr = 1; + if (mtmp->mconf) + appr = 0; + + if (gx == u.ux && gy == u.uy && (dogroom != uroom || dogroom < 0)) { + coord *cp; + cp = gettrack(omx, omy); + if (cp) { + gx = cp->x; + gy = cp->y; + } + } + + nix = omx; + niy = omy; + cnt = mfndpos(mtmp, poss, info, ALLOW_M | ALLOW_TRAPS); + chcnt = 0; + chi = -1; + for (i = 0; i < cnt; i++) { + nx = poss[i].x; + ny = poss[i].y; + if (info[i] & ALLOW_M) { + mtmp2 = m_at(nx, ny); + if (mtmp2->data->mlevel >= mdat->mlevel + 2 || + mtmp2->data->mlet == 'c') + continue; + if (after) /* hit only once each move */ + return (0); + + if (hitmm(mtmp, mtmp2) == 1 && rn2(4) && + mtmp2->mlstmv != moves && + hitmm(mtmp2, mtmp) == 2) + return (2); + return (0); + } + + /* dog avoids traps */ + /* but perhaps we have to pass a trap in order to follow @ */ + if ((info[i] & ALLOW_TRAPS) && (trap = t_at(nx, ny))) { + if (!trap->tseen && rn2(40)) + continue; + if (rn2(10)) + continue; + } + + /* dog eschewes cursed objects */ + /* but likes dog food */ + obj = fobj; + while (obj) { + if (obj->ox != nx || obj->oy != ny) + goto nextobj; + if (obj->cursed) + goto nxti; + if (obj->olet == FOOD_SYM && + (otyp = dogfood(obj)) < MANFOOD && + (otyp < ACCFOOD || edog->hungrytime <= moves)) { + /* + * Note: our dog likes the food so much that + * he might eat it even when it conceals a + * cursed object + */ + nix = nx; + niy = ny; + chi = i; +eatobj: + edog->eattime = + moves + obj->quan * + objects[obj->otyp].oc_delay; + if (edog->hungrytime < moves) + edog->hungrytime = moves; + edog->hungrytime += + 5 * obj->quan * + objects[obj->otyp].nutrition; + mtmp->mconf = 0; + if (cansee(nix, niy)) + pline("%s ate %s.", Monnam( + mtmp), doname(obj)); + /* perhaps this was a reward */ + if (otyp != CADAVER) + edog->apport += 200 / + (edog->dropdist + + moves - edog->droptime); + delobj(obj); + goto newdogpos; + } +nextobj: + obj = obj->nobj; + } + + for (j = 0; j < MTSZ && j < cnt - 1; j++) + if (nx == mtmp->mtrack[j].x && ny == mtmp->mtrack[j].y) + if (rn2(4 * (cnt - j))) + goto nxti; + +/* Some stupid C compilers cannot compute the whole expression at once. */ + nearer = GDIST(nx, ny); + nearer -= GDIST(nix, niy); + nearer *= appr; + if ((nearer == 0 && !rn2(++chcnt)) || nearer < 0 || + (nearer > 0 && !whappr && + ((omx == nix && omy == niy && !rn2(3)) + || !rn2(12)) + )) { + nix = nx; + niy = ny; + if (nearer < 0) + chcnt = 0; + chi = i; + } +nxti:; + } +newdogpos: + if (nix != omx || niy != omy) { + if (info[chi] & ALLOW_U) { + hitu(mtmp, d(mdat->damn, mdat->damd) + 1); + return (0); + } + mtmp->mx = nix; + mtmp->my = niy; + for (j = MTSZ - 1; j > 0; j--) + mtmp->mtrack[j] = mtmp->mtrack[j - 1]; + mtmp->mtrack[0].x = omx; + mtmp->mtrack[0].y = omy; + } + if (mintrap(mtmp) == 2) /* he died */ + return (2); + pmon(mtmp); + return (1); +} + +/* return roomnumber or -1 */ +int +inroom(xchar x, xchar y) +{ +#ifndef QUEST + struct mkroom *croom = &rooms[0]; + + while (croom->hx >= 0) { + if (croom->hx >= x - 1 && croom->lx <= x + 1 && + croom->hy >= y - 1 && croom->ly <= y + 1) + return (croom - rooms); + croom++; + } +#endif /* QUEST */ + return (-1); /* not in room or on door */ +} + +bool +tamedog(struct monst *mtmp, struct obj *obj) +{ + struct monst *mtmp2; + + if (flags.moonphase == FULL_MOON && night() && rn2(6)) + return (0); + + /* If we cannot tame him, at least he's no longer afraid. */ + mtmp->mflee = 0; + mtmp->mfleetim = 0; + if (mtmp->mtame || mtmp->mfroz || +#ifndef NOWORM + mtmp->wormno || +#endif /* NOWORM */ + mtmp->isshk || mtmp->isgd || strchr(" &@12", mtmp->data->mlet)) + return (0); /* no tame long worms? */ + if (obj) { + if (dogfood(obj) >= MANFOOD) + return (0); + if (cansee(mtmp->mx, mtmp->my)) + pline("%s devours the %s.", Monnam(mtmp), + objects[obj->otyp].oc_name); + obfree(obj, NULL); + } + mtmp2 = newmonst(sizeof(struct edog) + mtmp->mnamelth); + *mtmp2 = *mtmp; + mtmp2->mxlth = sizeof(struct edog); + if (mtmp->mnamelth) + strcpy(NAME(mtmp2), NAME(mtmp)); + initedog(mtmp2); + replmon(mtmp, mtmp2); + return (1); +} diff --git a/hack/hack.eat.c b/hack/hack.eat.c new file mode 100644 index 0000000..6373bc5 --- /dev/null +++ b/hack/hack.eat.c @@ -0,0 +1,492 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.eat.c - version 1.0.3 */ +/* $FreeBSD: src/games/hack/hack.eat.c,v 1.4 1999/11/16 10:26:36 marcel Exp $ */ + +#include "hack.h" +char POISONOUS[] = "ADKSVabhks"; + +static bool opentin(void); +static void Meatdone(void); +static void unfaint(void); +static void newuhs(bool); +static int eatcorpse(struct obj *); + +/* hunger texts used on bottom line (each 8 chars long) */ +#define SATIATED 0 +#define NOT_HUNGRY 1 +#define HUNGRY 2 +#define WEAK 3 +#define FAINTING 4 +#define FAINTED 5 +#define STARVED 6 + +const char *hu_stat[] = { + "Satiated", + " ", + "Hungry ", + "Weak ", + "Fainting", + "Fainted ", + "Starved " +}; + +void +init_uhunger(void) +{ + u.uhunger = 900; + u.uhs = NOT_HUNGRY; +} + +#define TTSZ SIZE(tintxts) +struct { const char *txt; int nut; } tintxts[] = { + { "It contains first quality peaches - what a surprise!", 40 }, + { "It contains salmon - not bad!", 60 }, + { "It contains apple juice - perhaps not what you hoped for.", 20 }, + { "It contains some nondescript substance, tasting awfully.", 500 }, + { "It contains rotten meat. You vomit.", -50 }, + { "It turns out to be empty.", 0 } +}; + +static struct { + struct obj *tin; + int usedtime, reqtime; +} tin; + +static bool +opentin(void) +{ + int r; + + if (!carried(tin.tin)) /* perhaps it was stolen? */ + return (0); /* %% probably we should use tinoid */ + if (tin.usedtime++ >= 50) { + pline("You give up your attempt to open the tin."); + return (0); + } + if (tin.usedtime < tin.reqtime) + return (1); /* still busy */ + + pline("You succeed in opening the tin."); + useup(tin.tin); + r = rn2(2 * TTSZ); + if (r < TTSZ) { + pline("%s", tintxts[r].txt); + lesshungry(tintxts[r].nut); + if (r == 1) { /* SALMON */ + Glib = rnd(15); + pline("Eating salmon made your fingers very slippery."); + } + } else { + pline("It contains spinach - this makes you feel like Popeye!"); + lesshungry(600); + if (u.ustr < 118) + u.ustr += rnd(((u.ustr < 17) ? 19 : 118) - u.ustr); + if (u.ustr > u.ustrmax) + u.ustrmax = u.ustr; + flags.botl = 1; + } + return (0); +} + +static void +Meatdone(void) +{ + u.usym = '@'; + prme(); +} + +int +doeat(void) +{ + struct obj *otmp; + struct objclass *ftmp; + int tmp; + + /* Is there some food (probably a heavy corpse) here on the ground? */ + if (!Levitation) + for (otmp = fobj; otmp; otmp = otmp->nobj) { + if (otmp->ox == u.ux && otmp->oy == u.uy && + otmp->olet == FOOD_SYM) { + pline("There %s %s here; eat %s? [ny] ", + (otmp->quan == 1) ? "is" : "are", + doname(otmp), + (otmp->quan == 1) ? "it" : "one"); + if (readchar() == 'y') { + if (otmp->quan != 1) + splitobj(otmp, 1); + freeobj(otmp); + otmp = addinv(otmp); + addtobill(otmp); + goto gotit; + } + } + } + + otmp = getobj("%", "eat"); + if (!otmp) + return (0); +gotit: + if (otmp->otyp == TIN) { + if (uwep) { + switch (uwep->otyp) { + case CAN_OPENER: + tmp = 1; + break; + case DAGGER: + case CRYSKNIFE: + tmp = 3; + break; + case PICK_AXE: + case AXE: + tmp = 6; + break; + default: + goto no_opener; + } + pline("Using your %s you try to open the tin.", + aobjnam(uwep, NULL)); + } else { +no_opener: + pline("It is not so easy to open this tin."); + if (Glib) { + pline("The tin slips out of your hands."); + if (otmp->quan > 1) { + struct obj *obj; + + obj = splitobj(otmp, 1); + if (otmp == uwep) + setuwep(obj); + } + dropx(otmp); + return (1); + } + tmp = 10 + rn2(1 + 500 / ((int)(u.ulevel + u.ustr))); + } + tin.reqtime = tmp; + tin.usedtime = 0; + tin.tin = otmp; + occupation = opentin; + occtxt = "opening the tin"; + return (1); + } + ftmp = &objects[otmp->otyp]; + multi = -ftmp->oc_delay; + if (otmp->otyp >= CORPSE && eatcorpse(otmp)) + goto eatx; + if (!rn2(7) && otmp->otyp != FORTUNE_COOKIE) { + pline("Blecch! Rotten food!"); + if (!rn2(4)) { + pline("You feel rather light headed."); + Confusion += d(2, 4); + } else if (!rn2(4) && !Blind) { + pline("Everything suddenly goes dark."); + Blind = d(2, 10); + seeoff(0); + } else if (!rn2(3)) { + if (Blind) + pline("The world spins and you slap against the floor."); + else + pline("The world spins and goes dark."); + nomul(-rnd(10)); + nomovemsg = "You are conscious again."; + } + lesshungry(ftmp->nutrition / 4); + } else { + if (u.uhunger >= 1500) { + pline("You choke over your food."); + pline("You die..."); + killer = ftmp->oc_name; + done("choked"); + } + switch (otmp->otyp) { + case FOOD_RATION: + if (u.uhunger <= 200) + pline("That food really hit the spot!"); + else if (u.uhunger <= 700) + pline("That satiated your stomach!"); + else { + pline("You're having a hard time getting all that food down."); + multi -= 2; + } + lesshungry(ftmp->nutrition); + if (multi < 0) + nomovemsg = "You finished your meal."; + break; + case TRIPE_RATION: + pline("Yak - dog food!"); + more_experienced(1, 0); + flags.botl = 1; + if (rn2(2)) { + pline("You vomit."); + morehungry(20); + if (Sick) { + Sick = 0; /* David Neves */ + pline("What a relief!"); + } + } else + lesshungry(ftmp->nutrition); + break; + default: + if (otmp->otyp >= CORPSE) + pline("That %s tasted terrible!", ftmp->oc_name); + else + pline("That %s was delicious!", ftmp->oc_name); + lesshungry(ftmp->nutrition); + if (otmp->otyp == DEAD_LIZARD && (Confusion > 2)) + Confusion = 2; + else +#ifdef QUEST + if (otmp->otyp == CARROT && !Blind) { + u.uhorizon++; + setsee(); + pline("Your vision improves."); + } else +#endif /* QUEST */ + if (otmp->otyp == FORTUNE_COOKIE) { + if (Blind) { + pline("This cookie has a scrap of paper inside!"); + pline("What a pity, that you cannot read it!"); + } else + outrumor(); + } else if (otmp->otyp == LUMP_OF_ROYAL_JELLY) { + /* This stuff seems to be VERY healthy! */ + if (u.ustrmax < 118) + u.ustrmax++; + if (u.ustr < u.ustrmax) + u.ustr++; + u.uhp += rnd(20); + if (u.uhp > u.uhpmax) { + if (!rn2(17)) + u.uhpmax++; + u.uhp = u.uhpmax; + } + heal_legs(); + } + break; + } + } +eatx: + if (multi < 0 && !nomovemsg) { + static char msgbuf[BUFSZ]; + sprintf(msgbuf, "You finished eating the %s.", ftmp->oc_name); + nomovemsg = msgbuf; + } + useup(otmp); + return (1); +} + +/* called in hack.main.c */ +void +gethungry(void) +{ + --u.uhunger; + if (moves % 2) { + if (Regeneration) + u.uhunger--; + if (Hunger) + u.uhunger--; + /* a3: if (Hunger & LEFT_RING) u.uhunger--; + * if (Hunger & RIGHT_RING) u.uhunger--; + * etc. + */ + } + if (moves % 20 == 0) { /* jimt@asgb */ + if (uleft) + u.uhunger--; + if (uright) + u.uhunger--; + } + newuhs(TRUE); +} + +/* called after vomiting and after performing feats of magic */ +void +morehungry(int num) +{ + u.uhunger -= num; + newuhs(TRUE); +} + +/* called after eating something (and after drinking fruit juice) */ +void +lesshungry(int num) +{ + u.uhunger += num; + newuhs(FALSE); +} + +static void +unfaint(void) +{ + u.uhs = FAINTING; + flags.botl = 1; +} + +static void +newuhs(bool incr) +{ + int newhs, h = u.uhunger; + + newhs = (h > 1000) ? SATIATED : + (h > 150) ? NOT_HUNGRY : + (h > 50) ? HUNGRY : + (h > 0) ? WEAK : FAINTING; + + if (newhs == FAINTING) { + if (u.uhs == FAINTED) + newhs = FAINTED; + if (u.uhs <= WEAK || rn2(20 - u.uhunger / 10) >= 19) { + if (u.uhs != FAINTED && multi >= 0 /* %% */) { + pline("You faint from lack of food."); + nomul(-10 + (u.uhunger / 10)); + nomovemsg = "You regain consciousness."; + afternmv = unfaint; + newhs = FAINTED; + } + } else if (u.uhunger < -(int)(200 + 25 * u.ulevel)) { + u.uhs = STARVED; + flags.botl = 1; + bot(); + pline("You die from starvation."); + done("starved"); + } + } + + if (newhs != u.uhs) { + if (newhs >= WEAK && u.uhs < WEAK) + losestr(1); /* this may kill you -- see below */ + else if (newhs < WEAK && u.uhs >= WEAK && u.ustr < u.ustrmax) + losestr(-1); + switch (newhs) { + case HUNGRY: + pline((!incr) ? "You only feel hungry now." : + (u.uhunger < 145) ? "You feel hungry." : + "You are beginning to feel hungry."); + break; + case WEAK: + pline((!incr) ? "You feel weak now." : + (u.uhunger < 45) ? "You feel weak." : + "You are beginning to feel weak."); + break; + } + u.uhs = newhs; + flags.botl = 1; + if (u.uhp < 1) { + pline("You die from hunger and exhaustion."); + killer = "exhaustion"; + done("starved"); + } + } +} + +#define CORPSE_I_TO_C(otyp) (char)((otyp >= DEAD_ACID_BLOB)\ + ? 'a' + (otyp - DEAD_ACID_BLOB)\ + : '@' + (otyp - DEAD_HUMAN)) +bool +poisonous(struct obj *otmp) +{ + return (strchr(POISONOUS, CORPSE_I_TO_C(otmp->otyp)) != 0); +} + +/* returns 1 if some text was printed */ +static int +eatcorpse(struct obj *otmp) +{ + char let = CORPSE_I_TO_C(otmp->otyp); + int tp = 0; + + if (let != 'a' && moves > otmp->age + 50 + rn2(100)) { + tp++; + pline("Ulch -- that meat was tainted!"); + pline("You get very sick."); + Sick = 10 + rn2(10); + u.usick_cause = objects[otmp->otyp].oc_name; + } else if (strchr(POISONOUS, let) && rn2(5)) { + tp++; + pline("Ecch -- that must have been poisonous!"); + if (!Poison_resistance) { + losestr(rnd(4)); + losehp(rnd(15), "poisonous corpse"); + } else + pline("You don't seem affected by the poison."); + } else if (strchr("ELNOPQRUuxz", let) && rn2(5)) { + tp++; + pline("You feel sick."); + losehp(rnd(8), "cadaver"); + } + switch (let) { + case 'L': + case 'N': + case 't': + Teleportation |= INTRINSIC; + break; + case 'W': + pluslvl(); + break; + case 'n': + u.uhp = u.uhpmax; + flags.botl = 1; + /* fall into next case */ + case '@': + pline("You cannibal! You will be sorry for this!"); + /* not tp++; */ + /* fall into next case */ + case 'd': + Aggravate_monster |= INTRINSIC; + break; + case 'I': + if (!Invis) { + Invis = 50 + rn2(100); + if (!See_invisible) + newsym(u.ux, u.uy); + } else { + Invis |= INTRINSIC; + See_invisible |= INTRINSIC; + } + /* fall into next case */ + case 'y': +#ifdef QUEST + u.uhorizon++; +#endif /* QUEST */ + /* fall into next case */ + case 'B': + Confusion = 50; + break; + case 'D': + Fire_resistance |= INTRINSIC; + break; + case 'E': + Telepat |= INTRINSIC; + break; + case 'F': + case 'Y': + Cold_resistance |= INTRINSIC; + break; + case 'k': + case 's': + Poison_resistance |= INTRINSIC; + break; + case 'c': + pline("You turn to stone."); + killer = "dead cockatrice"; + done("died"); + /* NOTREACHED */ + case 'a': + if (Stoned) { + pline("What a pity - you just destroyed a future piece of art!"); + tp++; + Stoned = 0; + } + break; + case 'M': + pline("You cannot resist the temptation to mimic a treasure chest."); + tp++; + nomul(-30); + afternmv = Meatdone; + nomovemsg = "You now again prefer mimicking a human."; + u.usym = '$'; + prme(); + break; + } + return (tp); +} diff --git a/hack/hack.end.c b/hack/hack.end.c new file mode 100644 index 0000000..46b3002 --- /dev/null +++ b/hack/hack.end.c @@ -0,0 +1,725 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.end.c - version 1.0.3 */ +/* $FreeBSD: src/games/hack/hack.end.c,v 1.4 1999/11/16 10:26:36 marcel Exp $ */ + +#include "hack.h" +#define Sprintf (void) sprintf + +#define newttentry() alloc(sizeof(struct toptenentry)) +#define NAMSZ 8 +#define DTHSZ 40 +#define PERSMAX 1 +#define POINTSMIN 1 /* must be > 0 */ +#define ENTRYMAX 100 /* must be >= 10 */ +#define PERS_IS_UID /* delete for PERSMAX per name; now per uid */ +struct toptenentry { + struct toptenentry *tt_next; + long int points; + int level,maxlvl,hp,maxhp; + int uid; + char plchar; + char sex; + char name[NAMSZ+1]; + char death[DTHSZ+1]; + char date[7]; /* yymmdd */ +} *tt_head; + +static void done_intr(int); +static void done_hangup(int); +static void topten(void); +static void outheader(void); +static int outentry(int, struct toptenentry *, int); +static char *itoa(int); +static const char *ordin(int); + +xchar maxdlevel = 1; + +void +done1(int unused __attribute__((unused))) +{ + signal(SIGINT, SIG_IGN); + pline("Really quit?"); + if (readchar() != 'y') { + signal(SIGINT, done1); + clrlin(); + fflush(stdout); + if (multi > 0) + nomul(0); + return; + } + done("quit"); + /* NOTREACHED */ +} + +int done_stopprint; +int done_hup; + +static void +done_intr(int unused __attribute__((unused))) +{ + done_stopprint++; + signal(SIGINT, SIG_IGN); + signal(SIGQUIT, SIG_IGN); +} + +static void +done_hangup(int unused __attribute__((unused))) +{ + done_hup++; + signal(SIGHUP, SIG_IGN); + done_intr(0); +} + +void +done_in_by(struct monst *mtmp) +{ + static char buf[BUFSZ]; + + pline("You die ..."); + if (mtmp->data->mlet == ' ') { + Sprintf(buf, "the ghost of %s", (char *)mtmp->mextra); + killer = buf; + } else if (mtmp->mnamelth) { + Sprintf(buf, "%s called %s", + mtmp->data->mname, NAME(mtmp)); + killer = buf; + } else if (mtmp->minvis) { + Sprintf(buf, "invisible %s", mtmp->data->mname); + killer = buf; + } else + killer = mtmp->data->mname; + done("died"); +} + +/* + * called with arg "died", "drowned", "escaped", "quit", "choked", + * "panicked", "burned", "starved" or "tricked" + */ +/* Be careful not to call panic from here! */ +void +done(const char *st1) +{ +#ifdef WIZARD + if (wizard && *st1 == 'd') { + u.uswldtim = 0; + if (u.uhpmax < 0) /* arbitrary */ + u.uhpmax = 100; + u.uhp = u.uhpmax; + pline("For some reason you are still alive."); + flags.move = 0; + if (multi > 0) + multi = 0; + else + multi = -1; + flags.botl = 1; + return; + } +#endif /* WIZARD */ + signal(SIGINT, done_intr); + signal(SIGQUIT, done_intr); + signal(SIGHUP, done_hangup); + if (*st1 == 'q' && u.uhp < 1) { + st1 = "died"; + killer = "quit while already on Charon's boat"; + } + if (*st1 == 's') + killer = "starvation"; + else if (*st1 == 'd' && st1[1] == 'r') + killer = "drowning"; + else if (*st1 == 'p') + killer = "panic"; + else if (*st1 == 't') + killer = "trickery"; + else if (!strchr("bcd", *st1)) + killer = st1; + paybill(); + clearlocks(); + if (flags.toplin == 1) + more(); + if (strchr("bcds", *st1)) { +#ifdef WIZARD + if (!wizard) +#endif /* WIZARD */ + savebones(); + if (!flags.notombstone) + outrip(); + } + if (*st1 == 'c') /* after outrip() */ + killer = st1; + settty(NULL); /* does a clear_screen() */ + if (!done_stopprint) + printf("Goodbye %s %s...\n\n", pl_character, plname); + { + long int tmp; + tmp = u.ugold - u.ugold0; + if (tmp < 0) + tmp = 0; + if (*st1 == 'd' || *st1 == 'b') + tmp -= tmp / 10; + u.urexp += tmp; + u.urexp += 50 * maxdlevel; + if (maxdlevel > 20) + u.urexp += 1000 * ((maxdlevel > 30) ? 10 : maxdlevel - 20); + } + if (*st1 == 'e') { + struct monst *mtmp; + struct obj *otmp; + int i; + unsigned worthlessct = 0; + boolean has_amulet = FALSE; + + killer = st1; + keepdogs(); + mtmp = mydogs; + if (mtmp) { + if (!done_stopprint) + printf("You"); + while (mtmp) { + if (!done_stopprint) + printf(" and %s", monnam(mtmp)); + if (mtmp->mtame) + u.urexp += mtmp->mhp; + mtmp = mtmp->nmon; + } + if (!done_stopprint) + printf("\nescaped from the dungeon with %ld points,\n", + u.urexp); + } else if (!done_stopprint) + printf("You escaped from the dungeon with %ld points,\n", + u.urexp); + for (otmp = invent; otmp; otmp = otmp->nobj) { + if (otmp->olet == GEM_SYM) { + objects[otmp->otyp].oc_name_known = 1; + i = otmp->quan * objects[otmp->otyp].g_val; + if (i == 0) { + worthlessct += otmp->quan; + continue; + } + u.urexp += i; + if (!done_stopprint) + printf("\t%s (worth %d Zorkmids),\n", + doname(otmp), i); + } else if (otmp->olet == AMULET_SYM) { + otmp->known = 1; + i = (otmp->spe < 0) ? 2 : 5000; + u.urexp += i; + if (!done_stopprint) + printf("\t%s (worth %d Zorkmids),\n", + doname(otmp), i); + if (otmp->spe >= 0) { + has_amulet = TRUE; + killer = "escaped (with amulet)"; + } + } + } + if (worthlessct) + if (!done_stopprint) + printf("\t%u worthless piece%s of coloured glass,\n", + worthlessct, plur(worthlessct)); + if (has_amulet) + u.urexp *= 2; + } else if (!done_stopprint) + printf("You %s on dungeon level %d with %ld points,\n", + st1, dlevel, u.urexp); + if (!done_stopprint) + printf("and %ld piece%s of gold, after %ld move%s.\n", + u.ugold, plur(u.ugold), moves, plur(moves)); + if (!done_stopprint) + printf("You were level %u with a maximum of %d hit points when you %s.\n", + u.ulevel, u.uhpmax, st1); + if (*st1 == 'e' && !done_stopprint) { + getret(); /* all those pieces of coloured glass ... */ + cls(); + } +#ifdef WIZARD + if (!wizard) +#endif /* WIZARD */ + topten(); + if (done_stopprint) + printf("\n\n"); + exit(0); +} + +static void +topten(void) +{ + int uid = getuid(); + int rank, rank0 = -1, rank1 = 0; + int occ_cnt = PERSMAX; + struct toptenentry *t0, *t1, *tprev; + const char *recfile = RECORD; + const char *reclock = "record_lock"; + int sleepct = 300; + FILE *rfile; + int flg = 0; + +#define HUP if (!done_hup) + while (link(recfile, reclock) == -1) { + HUP perror(reclock); + if (!sleepct--) { + HUP puts("I give up. Sorry."); + HUP puts("Perhaps there is an old record_lock around?"); + return; + } + HUP printf("Waiting for access to record file. (%d)\n", + sleepct); + HUP fflush(stdout); + sleep(1); + } + if (!(rfile = fopen(recfile, "r"))) { + HUP puts("Cannot open record file!"); + goto unlock; + } + HUP putchar('\n'); + + /* create a new 'topten' entry */ + t0 = newttentry(); + t0->level = dlevel; + t0->maxlvl = maxdlevel; + t0->hp = u.uhp; + t0->maxhp = u.uhpmax; + t0->points = u.urexp; + t0->plchar = pl_character[0]; + t0->sex = (flags.female ? 'F' : 'M'); + t0->uid = uid; + strncpy(t0->name, plname, NAMSZ); + (t0->name)[NAMSZ] = 0; + strncpy(t0->death, killer, DTHSZ); + (t0->death)[DTHSZ] = 0; + strcpy(t0->date, getdate()); + + /* assure minimum number of points */ + if (t0->points < POINTSMIN) + t0->points = 0; + + t1 = tt_head = newttentry(); + tprev = NULL; + /* rank0: -1 undefined, 0 not_on_list, n n_th on list */ + for (rank = 1;;) { + if (fscanf(rfile, "%6s %d %d %d %d %d %ld %c%c %[^,],%[^\n]", + t1->date, &t1->uid, + &t1->level, &t1->maxlvl, + &t1->hp, &t1->maxhp, &t1->points, + &t1->plchar, &t1->sex, t1->name, t1->death) != 11 + || t1->points < POINTSMIN) + t1->points = 0; + if (rank0 < 0 && t1->points < t0->points) { + rank0 = rank++; + if (tprev == NULL) + tt_head = t0; + else + tprev->tt_next = t0; + t0->tt_next = t1; + occ_cnt--; + flg++; /* ask for a rewrite */ + } else + tprev = t1; + if (t1->points == 0) + break; + if ( +#ifdef PERS_IS_UID + t1->uid == t0->uid && +#else + strncmp(t1->name, t0->name, NAMSZ) == 0 && +#endif /* PERS_IS_UID */ + t1->plchar == t0->plchar && --occ_cnt <= 0) { + if (rank0 < 0) { + rank0 = 0; + rank1 = rank; + HUP printf("You didn't beat your previous score of %ld points.\n\n", + t1->points); + } + if (occ_cnt < 0) { + flg++; + continue; + } + } + if (rank <= ENTRYMAX) { + t1 = t1->tt_next = newttentry(); + rank++; + } + if (rank > ENTRYMAX) { + t1->points = 0; + break; + } + } + if (flg) { /* rewrite record file */ + fclose(rfile); + if (!(rfile = fopen(recfile, "w"))) { + HUP puts("Cannot write record file\n"); + goto unlock; + } + + if (!done_stopprint) + if (rank0 > 0) { + if (rank0 <= 10) + puts("You made the top ten list!\n"); + else + printf("You reached the %d%s place on the top %d list.\n\n", + rank0, ordin(rank0), ENTRYMAX); + } + } + if (rank0 == 0) + rank0 = rank1; + if (rank0 <= 0) + rank0 = rank; + if (!done_stopprint) + outheader(); + t1 = tt_head; + for (rank = 1; t1->points != 0; rank++, t1 = t1->tt_next) { + if (flg) + fprintf(rfile, "%6s %d %d %d %d %d %ld %c%c %s,%s\n", + t1->date, t1->uid, + t1->level, t1->maxlvl, + t1->hp, t1->maxhp, t1->points, + t1->plchar, t1->sex, t1->name, t1->death); + if (done_stopprint) + continue; + if (rank > (int)flags.end_top && + (rank < rank0 - (int)flags.end_around || rank > rank0 + + (int)flags.end_around) + && (!flags.end_own || +#ifdef PERS_IS_UID + t1->uid != t0->uid)) +#else + strncmp(t1->name, t0->name, NAMSZ))) +#endif /* PERS_IS_UID */ + continue; + if (rank == rank0 - (int)flags.end_around && + rank0 > (int)flags.end_top + (int)flags.end_around + 1 && + !flags.end_own) + putchar('\n'); + if (rank != rank0) + outentry(rank, t1, 0); + else if (!rank1) + outentry(rank, t1, 1); + else { + int t0lth = outentry(0, t0, -1); + int t1lth = outentry(rank, t1, t0lth); + if (t1lth > t0lth) + t0lth = t1lth; + outentry(0, t0, t0lth); + } + } + if (rank0 >= rank) + if (!done_stopprint) + outentry(0, t0, 1); + fclose(rfile); +unlock: + unlink(reclock); +} + +static void +outheader(void) +{ + char linebuf[BUFSZ]; + char *bp; + + strcpy(linebuf, "Number Points Name"); + bp = eos(linebuf); + while (bp < linebuf + COLNO - 9) + *bp++ = ' '; + strcpy(bp, "Hp [max]"); + puts(linebuf); +} + +/* so>0: standout line; so=0: ordinary line; so<0: no output, return lth */ +static int +outentry(int rank, struct toptenentry *t1, int so) +{ + boolean quit = FALSE, dead = FALSE, starv = FALSE; + char linebuf[BUFSZ]; + + linebuf[0] = 0; + if (rank) + Sprintf(eos(linebuf), "%3d", rank); + else + Sprintf(eos(linebuf), " "); + Sprintf(eos(linebuf), " %6ld %8s", t1->points, t1->name); + if (t1->plchar == 'X') + Sprintf(eos(linebuf), " "); + else + Sprintf(eos(linebuf), "-%c ", t1->plchar); + if (!strncmp("escaped", t1->death, 7)) { + if (!strcmp(" (with amulet)", t1->death + 7)) + Sprintf(eos(linebuf), "escaped the dungeon with amulet"); + else + Sprintf(eos(linebuf), "escaped the dungeon [max level %d]", + t1->maxlvl); + } else { + if (!strncmp(t1->death, "quit", 4)) { + quit = TRUE; + if (t1->maxhp < 3 * t1->hp && t1->maxlvl < 4) + Sprintf(eos(linebuf), "cravenly gave up"); + else + Sprintf(eos(linebuf), "quit"); + } else if (!strcmp(t1->death, "choked")) { + Sprintf(eos(linebuf), "choked on %s food", + (t1->sex == 'F') ? "her" : "his"); + } else if (!strncmp(t1->death, "starv", 5)) { + Sprintf(eos(linebuf), "starved to death"); + starv = TRUE; + } else { + Sprintf(eos(linebuf), "was killed"); + dead = TRUE; + } + Sprintf(eos(linebuf), " on%s level %d", + (dead || starv) ? "" : " dungeon", t1->level); + if (t1->maxlvl != t1->level) + Sprintf(eos(linebuf), " [max %d]", t1->maxlvl); + if (quit && t1->death[4]) + Sprintf(eos(linebuf), "%s", t1->death + 4); + } + if (dead) { + Sprintf(eos(linebuf), " by %s%s", + (!strncmp(t1->death, "trick", 5) || + !strncmp(t1->death, "the ", 4)) + ? "" : + strchr(vowels, *t1->death) ? "an " : "a ", + t1->death); + } + Sprintf(eos(linebuf), "."); + if (t1->maxhp) { + char *bp = eos(linebuf); + char hpbuf[10]; + int hppos; + + Sprintf(hpbuf, "%s", (t1->hp > 0) ? itoa(t1->hp) : "-"); + hppos = COLNO - 7 - strlen(hpbuf); + if (bp <= linebuf + hppos) { + while (bp < linebuf + hppos) + *bp++ = ' '; + strcpy(bp, hpbuf); + Sprintf(eos(bp), " [%d]", t1->maxhp); + } + } + if (so == 0) + puts(linebuf); + else if (so > 0) { + char *bp = eos(linebuf); + if (so >= COLNO) + so = COLNO - 1; + while (bp < linebuf + so) + *bp++ = ' '; + *bp = 0; + standoutbeg(); + fputs(linebuf, stdout); + standoutend(); + putchar('\n'); + } + return (strlen(linebuf)); +} + +static char * +itoa(int a) +{ + static char buf[12]; + + Sprintf(buf, "%d", a); + return (buf); +} + +static const char * +ordin(int n) +{ + int d1 = n % 10; + + return ((d1 == 0 || d1 > 3 || n / 10 == 1) ? "th" : (d1 == 1) ? "st" : + (d1 == 2) ? "nd" : "rd"); +} + +void +clearlocks(void) +{ + int x; + + signal(SIGHUP, SIG_IGN); + for (x = maxdlevel; x >= 0; x--) { + glo(x); + unlink(lock); /* not all levels need be present */ + } +} + +#ifdef NOSAVEONHANGUP +void +hangup(int unused __attribute__((unused))) +{ + signal(SIGINT, SIG_IGN); + clearlocks(); + exit(1); +} +#endif /* NOSAVEONHANGUP */ + +char * +eos(char *s) +{ + while (*s) + s++; + return (s); +} + +/* it is the callers responsibility to check that there is room for c */ +void +charcat(char *s, char c) +{ + while (*s) + s++; + *s++ = c; + *s = 0; +} + +/* + * Called with args from main if argc >= 0. In this case, list scores as + * requested. Otherwise, find scores for the current player (and list them + * if argc == -1). + */ +void +prscore(int argc, char **argv) +{ + char **players = NULL; + int playerct; + int rank; + struct toptenentry *t1, *t2; + const char *recfile = RECORD; + FILE *rfile; + int flg = 0; + int i; +#ifdef nonsense + long total_score = 0L; + char totchars[10]; + int totcharct = 0; +#endif /* nonsense */ + int outflg = (argc >= -1); +#ifdef PERS_IS_UID + int uid = -1; +#else + char *player0; +#endif /* PERS_IS_UID */ + + if (!(rfile = fopen(recfile, "r"))) { + puts("Cannot open record file!"); + return; + } + + if (argc > 1 && !strncmp(argv[1], "-s", 2)) { + if (!argv[1][2]) { + argc--; + argv++; + } else if (!argv[1][3] && strchr("CFKSTWX", argv[1][2])) { + argv[1]++; + argv[1][0] = '-'; + } else + argv[1] += 2; + } + if (argc <= 1) { +#ifdef PERS_IS_UID + uid = getuid(); + playerct = 0; +#else + player0 = plname; + if (!*player0) + player0 = "hackplayer"; + playerct = 1; + players = &player0; +#endif /* PERS_IS_UID */ + } else { + playerct = --argc; + players = ++argv; + } + if (outflg) + putchar('\n'); + + t1 = tt_head = newttentry(); + for (rank = 1;; rank++) { + if (fscanf(rfile, "%6s %d %d %d %d %d %ld %c%c %[^,],%[^\n]", + t1->date, &t1->uid, + &t1->level, &t1->maxlvl, + &t1->hp, &t1->maxhp, &t1->points, + &t1->plchar, &t1->sex, t1->name, t1->death) != 11) + t1->points = 0; + if (t1->points == 0) + break; +#ifdef PERS_IS_UID + if (!playerct && t1->uid == uid) + flg++; + else +#endif /* PERS_IS_UID */ + for (i = 0; i < playerct; i++) { + if (strcmp(players[i], "all") == 0 || + strncmp(t1->name, players[i], NAMSZ) == 0 || + (players[i][0] == '-' && + players[i][1] == t1->plchar && + players[i][2] == 0) || + (digit(players[i][0]) && rank <= atoi(players[i]))) + flg++; + } + t1 = t1->tt_next = newttentry(); + } + fclose(rfile); + if (!flg) { + if (outflg) { + printf("Cannot find any entries for "); + if (playerct < 1) + printf("you.\n"); + else { + if (playerct > 1) + printf("any of "); + for (i = 0; i < playerct; i++) + printf("%s%s", players[i], + (i < playerct - 1) ? ", " : ".\n"); + printf("Call is: %s -s [playernames]\n", hname); + } + } + return; + } + + if (outflg) + outheader(); + t1 = tt_head; + for (rank = 1; t1->points != 0; rank++, t1 = t2) { + t2 = t1->tt_next; +#ifdef PERS_IS_UID + if (!playerct && t1->uid == uid) + goto outwithit; + else +#endif /* PERS_IS_UID */ + for (i = 0; i < playerct; i++) { + if (strcmp(players[i], "all") == 0 || + strncmp(t1->name, players[i], NAMSZ) == 0 || + (players[i][0] == '-' && + players[i][1] == t1->plchar && + players[i][2] == 0) || + (digit(players[i][0]) && rank <= + atoi(players[i]))) { +outwithit: + if (outflg) + outentry(rank, t1, 0); +#ifdef nonsense + total_score += t1->points; + if (totcharct < sizeof(totchars) - 1) + totchars[totcharct++] = t1->plchar; +#endif /* nonsense */ + break; + } + } + free(t1); + } +#ifdef nonsense + totchars[totcharct] = 0; + + /* + * We would like to determine whether he is experienced. However, the + * information collected here only tells about the scores/roles that + * got into the topten (top 100?). We should maintain a .hacklog or + * something in his home directory. + */ + flags.beginner = (total_score < 6000); + for (i = 0; i < 6; i++) + if (!strchr(totchars, "CFKSTWX"[i])) { + flags.beginner = 1; + if (!pl_character[0]) + pl_character[0] = "CFKSTWX"[i]; + break; + } +#endif /* nonsense */ +} diff --git a/hack/hack.engrave.c b/hack/hack.engrave.c new file mode 100644 index 0000000..a319fa9 --- /dev/null +++ b/hack/hack.engrave.c @@ -0,0 +1,337 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.engrave.c - version 1.0.3 */ +/* $FreeBSD: src/games/hack/hack.engrave.c,v 1.4 1999/11/16 02:57:04 billf Exp $ */ + +#include "hack.h" + +extern char nul[]; +extern struct obj zeroobj; +struct engr { + struct engr *nxt_engr; + char *engr_txt; + xchar engr_x, engr_y; + unsigned engr_lth; /* for save & restore; not length of text */ + long engr_time; /* moment engraving was (will be) finished */ + xchar engr_type; +#define DUST 1 +#define ENGRAVE 2 +#define BURN 3 +} *head_engr; + +static struct engr *engr_at(xchar, xchar); +static void del_engr(struct engr *); + +static struct engr * +engr_at(xchar x, xchar y) +{ + struct engr *ep = head_engr; + + while (ep) { + if (x == ep->engr_x && y == ep->engr_y) + return(ep); + ep = ep->nxt_engr; + } + return (NULL); +} + +bool +sengr_at(const char *s, xchar x, xchar y) +{ + struct engr *ep = engr_at(x, y); + char *t; + int n; + + if (ep && ep->engr_time <= moves) { + t = ep->engr_txt; + n = strlen(s); + while (*t) { + if (!strncmp(s, t, n)) + return (1); + t++; + } + } + return (0); +} + +void +u_wipe_engr(int cnt) +{ + if (!u.uswallow && !Levitation) + wipe_engr_at(u.ux, u.uy, cnt); +} + +void +wipe_engr_at(xchar x, xchar y, xchar cnt) +{ + struct engr *ep = engr_at(x, y); + int lth, pos; + char ch; + + if (ep) { + if ((ep->engr_type != DUST) || Levitation) + cnt = rn2(1 + 50 / (cnt + 1)) ? 0 : 1; + lth = strlen(ep->engr_txt); + if (lth && cnt > 0) { + while (cnt--) { + pos = rn2(lth); + if ((ch = ep->engr_txt[pos]) == ' ') + continue; + ep->engr_txt[pos] = (ch != '?') ? '?' : ' '; + } + } + while (lth && ep->engr_txt[lth - 1] == ' ') + ep->engr_txt[--lth] = 0; + while (ep->engr_txt[0] == ' ') + ep->engr_txt++; + if (!ep->engr_txt[0]) + del_engr(ep); + } +} + +void +read_engr_at(int x, int y) +{ + struct engr *ep = engr_at(x, y); + + if (ep && ep->engr_txt[0]) { + switch (ep->engr_type) { + case DUST: + pline("Something is written here in the dust."); + break; + case ENGRAVE: + pline("Something is engraved here on the floor."); + break; + case BURN: + pline("Some text has been burned here in the floor."); + break; + default: + impossible("Something is written in a very strange way."); + } + pline("You read: \"%s\".", ep->engr_txt); + } +} + +void +make_engr_at(int x, int y, const char *s) +{ + struct engr *ep; + + if ((ep = engr_at(x, y))) + del_engr(ep); + ep = alloc((unsigned)(sizeof(struct engr) + strlen(s) + 1)); + ep->nxt_engr = head_engr; + head_engr = ep; + ep->engr_x = x; + ep->engr_y = y; + ep->engr_txt = (char *)(ep + 1); + strcpy(ep->engr_txt, s); + ep->engr_time = 0; + ep->engr_type = DUST; + ep->engr_lth = strlen(s) + 1; +} + +int +doengrave(void) +{ + int len; + char *sp; + struct engr *ep, *oep = engr_at(u.ux, u.uy); + char buf[BUFSZ]; + xchar type; + int spct; /* number of leading spaces */ + struct obj *otmp; + + multi = 0; + if (u.uswallow) { + pline("You're joking. Hahaha!"); /* riv05!a3 */ + return (0); + } + + /* one may write with finger, weapon or wand */ + otmp = getobj("#-)/", "write with"); + if (!otmp) + return (0); + + if (otmp == &zeroobj) + otmp = NULL; + if (otmp && otmp->otyp == WAN_FIRE && otmp->spe) { + type = BURN; + otmp->spe--; + } else { + /* first wield otmp */ + if (otmp != uwep) { + if (uwep && uwep->cursed) { + /* Andreas Bormann */ + pline("Since your weapon is welded to your hand,"); + pline("you use the %s.", aobjnam(uwep, NULL)); + otmp = uwep; + } else { + if (!otmp) + pline("You are now empty-handed."); + else if (otmp->cursed) + pline("The %s %s to your hand!", + aobjnam(otmp, "weld"), + (otmp->quan == 1) ? "itself" : "themselves"); + else + pline("You now wield %s.", doname(otmp)); + setuwep(otmp); + } + } + + if (!otmp) + type = DUST; + else if (otmp->otyp == DAGGER || otmp->otyp == TWO_HANDED_SWORD || + otmp->otyp == CRYSKNIFE || + otmp->otyp == LONG_SWORD || otmp->otyp == AXE) { + type = ENGRAVE; + if ((int)otmp->spe <= -3) { + type = DUST; + pline("Your %s too dull for engraving.", + aobjnam(otmp, "are")); + if (oep && oep->engr_type != DUST) + return (1); + } + } else + type = DUST; + } + if (Levitation && type != BURN) { /* riv05!a3 */ + pline("You can't reach the floor!"); + return (1); + } + if (oep && oep->engr_type == DUST) { + pline("You wipe out the message that was written here."); + del_engr(oep); + oep = NULL; + } + if (type == DUST && oep) { + pline("You cannot wipe out the message that is %s in the rock.", + (oep->engr_type == BURN) ? "burned" : "engraved"); + return (1); + } + + pline("What do you want to %s on the floor here? ", + (type == ENGRAVE) ? "engrave" : (type == BURN) ? "burn" : "write"); + getlin(buf); + clrlin(); + spct = 0; + sp = buf; + while (*sp == ' ') { + spct++; + sp++; + } + len = strlen(sp); + if (!len || *buf == '\033') { + if (type == BURN) + otmp->spe++; + return (0); + } + + switch (type) { + case DUST: + case BURN: + if (len > 15) { + multi = -(len / 10); + nomovemsg = "You finished writing."; + } + break; + case ENGRAVE: /* here otmp != 0 */ + { + int len2 = (otmp->spe + 3) * 2 + 1; + + pline("Your %s dull.", aobjnam(otmp, "get")); + if (len2 < len) { + len = len2; + sp[len] = 0; + otmp->spe = -3; + nomovemsg = "You cannot engrave more."; + } else { + otmp->spe -= len / 2; + nomovemsg = "You finished engraving."; + } + multi = -len; + } + break; + } + if (oep) + len += strlen(oep->engr_txt) + spct; + ep = alloc((unsigned)(sizeof(struct engr) + len + 1)); + ep->nxt_engr = head_engr; + head_engr = ep; + ep->engr_x = u.ux; + ep->engr_y = u.uy; + sp = (char *)(ep + 1); /* (char *)ep + sizeof(struct engr) */ + ep->engr_txt = sp; + if (oep) { + strcpy(sp, oep->engr_txt); + strcat(sp, buf); + del_engr(oep); + } else + strcpy(sp, buf); + ep->engr_lth = len + 1; + ep->engr_type = type; + ep->engr_time = moves - multi; + + /* kludge to protect pline against excessively long texts */ + if (len > BUFSZ - 20) + sp[BUFSZ - 20] = 0; + + return (1); +} + +void +save_engravings(int fd) +{ + struct engr *ep = head_engr; + + while (ep) { + if (!ep->engr_lth || !ep->engr_txt[0]) { + ep = ep->nxt_engr; + continue; + } + bwrite(fd, (char *)&(ep->engr_lth), sizeof(ep->engr_lth)); + bwrite(fd, (char *)ep, sizeof(struct engr) + ep->engr_lth); + ep = ep->nxt_engr; + } + bwrite(fd, (char *)nul, sizeof(unsigned)); + head_engr = NULL; +} + +void +rest_engravings(int fd) +{ + struct engr *ep; + unsigned lth; + + head_engr = NULL; + for (;;) { + mread(fd, (char *)<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 <fcntl.h> +#include <signal.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "def.objclass.h" + +typedef struct { + xchar x, y; +} coord; + +#include "def.monst.h" /* uses coord */ +#include "def.gold.h" +#include "def.trap.h" +#include "def.obj.h" +#include "def.flag.h" +#include "def.mkroom.h" +#include "def.wseg.h" + +#define plur(x) (((x) == 1) ? "" : "s") + +#define BUFSZ 256 /* for getlin buffers */ +#define PL_NSIZ 32 /* name of player, ghost, shopkeeper */ + +#include "def.rm.h" +#include "def.permonst.h" + +extern xchar xdnstair, ydnstair, xupstair, yupstair; /* stairs up and down. */ + +#define newstring(x) alloc((unsigned)(x)) +#include "hack.onames.h" + +#define ON 1 +#define OFF 0 + +extern struct obj *invent, *uwep, *uarm, *uarm2, *uarmh, *uarms, *uarmg, + *uleft, *uright, *fcobj; +extern struct obj *uchain; /* defined iff PUNISHED */ +extern struct obj *uball; /* defined if PUNISHED */ + +struct prop { +#define TIMEOUT 007777 /* mask */ +#define LEFT_RING W_RINGL /* 010000L */ +#define RIGHT_RING W_RINGR /* 020000L */ +#define INTRINSIC 040000L +#define LEFT_SIDE LEFT_RING +#define RIGHT_SIDE RIGHT_RING +#define BOTH_SIDES (LEFT_SIDE | RIGHT_SIDE) + long p_flgs; + void (*p_tofn)(void); /* called after timeout */ +}; + +struct you { + xchar ux, uy; + schar dx, dy, dz; /* direction of move (or zap or ... ) */ +#ifdef QUEST + schar di; /* direction of FF */ + xchar ux0, uy0; /* initial position FF */ +#endif /* QUEST */ + xchar udisx, udisy; /* last display pos */ + char usym; /* usually '@' */ + schar uluck; +#define LUCKMAX 10 /* on moonlit nights 11 */ +#define LUCKMIN (-10) + int last_str_turn:3; /* 0: none, 1: half turn, 2: full turn */ + /* +: turn right, -: turn left */ + unsigned udispl:1; /* @ on display */ + unsigned ulevel:4; /* 1 - 14 */ +#ifdef QUEST + unsigned uhorizon:7; +#endif /* QUEST */ + unsigned utrap:3; /* trap timeout */ + unsigned utraptype:1; /* defined if utrap nonzero */ +#define TT_BEARTRAP 0 +#define TT_PIT 1 + unsigned uinshop:6; /* used only in shk.c - (roomno+1) of shop */ + + +/* perhaps these #define's should also be generated by makedefs */ +#define TELEPAT LAST_RING /* not a ring */ +#define Telepat u.uprops[TELEPAT].p_flgs +#define FAST (LAST_RING+1) /* not a ring */ +#define Fast u.uprops[FAST].p_flgs +#define CONFUSION (LAST_RING+2) /* not a ring */ +#define Confusion u.uprops[CONFUSION].p_flgs +#define INVIS (LAST_RING+3) /* not a ring */ +#define Invis u.uprops[INVIS].p_flgs +#define Invisible (Invis && !See_invisible) +#define GLIB (LAST_RING+4) /* not a ring */ +#define Glib u.uprops[GLIB].p_flgs +#define PUNISHED (LAST_RING+5) /* not a ring */ +#define Punished u.uprops[PUNISHED].p_flgs +#define SICK (LAST_RING+6) /* not a ring */ +#define Sick u.uprops[SICK].p_flgs +#define BLIND (LAST_RING+7) /* not a ring */ +#define Blind u.uprops[BLIND].p_flgs +#define WOUNDED_LEGS (LAST_RING+8) /* not a ring */ +#define Wounded_legs u.uprops[WOUNDED_LEGS].p_flgs +#define STONED (LAST_RING+9) /* not a ring */ +#define Stoned u.uprops[STONED].p_flgs +#define PROP(x) (x-RIN_ADORNMENT) /* convert ring to index in uprops */ + unsigned umconf:1; + const char *usick_cause; + struct prop uprops[LAST_RING+10]; + + unsigned uswallow:1; /* set if swallowed by a monster */ + unsigned uswldtim:4; /* time you have been swallowed */ + unsigned uhs:3; /* hunger state - see hack.eat.c */ + schar ustr, ustrmax; + schar udaminc; + schar uac; + int uhp, uhpmax; + long int ugold, ugold0, uexp, urexp; + int uhunger; /* refd only in eat.c and shk.c */ + int uinvault; + struct monst *ustuck; + int nr_killed[CMNUM+2]; /* used for experience bookkeeping */ +}; + +extern struct you u; + +extern const char *traps[]; +extern char vowels[]; + +extern xchar curx, cury; /* cursor location on screen */ + +extern coord bhitpos; /* place where thrown weapon falls to the ground */ + +extern xchar seehx, seelx, seehy, seely; /* where to see*/ +extern const char *save_cm, *killer, *nomovemsg; + +extern xchar dlevel, maxdlevel; /* dungeon level */ + +extern long moves; + +extern int multi; + +extern char lock[]; + +extern const char *occtxt; +extern const char *hu_stat[]; + +#define DIST(x1,y1,x2,y2) (((x1)-(x2))*((x1)-(x2)) + ((y1)-(y2))*((y1)-(y2))) + +#define PL_CSIZ 20 /* sizeof pl_character */ +#define MAX_CARR_CAP 120 /* so that boulders can be heavier */ +#define MAXLEVEL 40 +#define FAR (COLNO+2) /* position outside screen */ + +extern schar xdir[], ydir[]; +extern int hackpid, locknum, doorindex, done_stopprint; +extern char mlarge[], pl_character[PL_CSIZ], genocided[60], fut_geno[60]; +extern char *hname, morc, plname[PL_NSIZ], sdir[]; +extern boolean level_exists[], restoring, in_mklev; +extern struct permonst pm_eel, pm_ghost; +extern void (*afternmv)(void); +extern struct monst *mydogs; +extern bool (*occupation)(void); + +/* Non-static function prototypes */ + +/* alloc.c */ +void *alloc(size_t); + +/* hack.apply.c */ +int doapply(void); +int holetime(void); +void dighole(void); + +/* hack.bones.c */ +void savebones(void); +int getbones(void); + +/* hack.c */ +void unsee(void); +void seeoff(bool); +void domove(void); +int dopickup(void); +void pickup(int); +void lookaround(void); +bool monster_nearby(void); +bool cansee(xchar, xchar); +int sgn(int); +void setsee(void); +void nomul(int); +int abon(void); +int dbon(void); +void losestr(int); +void losehp(int, const char *); +void losehp_m(int, struct monst *); +void losexp(void); +int inv_weight(void); +long newuexp(void); + +/* hack.cmd.c */ +void rhack(const char *); +bool movecmd(char); +bool getdir(bool); +void confdir(void); +#ifdef QUEST +void finddir(void); +#endif +bool isok(int, int); + +/* hack.do.c */ +int dodrop(void); +void dropx(struct obj *); +int doddrop(void); +int dodown(void); +int doup(void); +void goto_level(int, bool); +int donull(void); +int dopray(void); +int dothrow(void); +struct obj *splitobj(struct obj *, int); +void more_experienced(int, int); +void set_wounded_legs(long, int); +void heal_legs(void); + +/* hack.do_name.c */ +coord getpos(int, const char *); +int do_mname(void); +int ddocall(void); +void docall(struct obj *); +char *monnam(struct monst *); +char *Monnam(struct monst *); +char *amonnam(struct monst *, const char *); +char *Amonnam(struct monst *, const char *); +char *Xmonnam(struct monst *); + +/* hack.do_wear.c */ +int doremarm(void); +int doremring(void); +bool armoroff(struct obj *); +int doweararm(void); +int dowearring(void); +void ringoff(struct obj *); +void find_ac(void); +void glibr(void); +struct obj *some_armor(void); +void corrode_armor(void); + +/* hack.dog.c */ +void makedog(void); +void losedogs(void); +void keepdogs(void); +void fall_down(struct monst *); +int dog_move(struct monst *, int); +int inroom(xchar, xchar); +bool tamedog(struct monst *, struct obj *); + +/* hack.eat.c */ +void init_uhunger(void); +int doeat(void); +void gethungry(void); +void morehungry(int); +void lesshungry(int); +bool poisonous(struct obj *); + +/* hack.end.c */ +void done1(int); +void done_in_by(struct monst *); +void done(const char *); +void clearlocks(void); +#ifdef NOSAVEONHANGUP +void hangup(int); +#endif +char *eos(char *); +void charcat(char *, char); +void prscore(int, char **); + +/* hack.engrave.c */ +bool sengr_at(const char *, xchar, xchar); +void u_wipe_engr(int); +void wipe_engr_at(xchar, xchar, xchar); +void read_engr_at(int, int); +void make_engr_at(int, int, const char *); +int doengrave(void); +void save_engravings(int); +void rest_engravings(int); + +/* hack.fight.c */ +int hitmm(struct monst *, struct monst *); +void mondied(struct monst *); +int fightm(struct monst *); +bool thitu(int, int, const char *); +bool hmon(struct monst *, struct obj *, int); +bool attack(struct monst *); + +/* hack.invent.c */ +struct obj *addinv(struct obj *); +void useup(struct obj *); +void freeinv(struct obj *); +void delobj(struct obj *); +void freeobj(struct obj *); +void freegold(struct gold *); +void deltrap(struct trap *); +struct monst *m_at(int, int); +struct obj *o_at(int, int); +struct obj *sobj_at(int, int, int); +bool carried(struct obj *); +bool carrying(int); +struct obj *o_on(unsigned int, struct obj *); +struct trap *t_at(int, int); +struct gold *g_at(int, int); +struct obj *getobj(const char *, const char *); +int ggetobj(const char *, int (*)(struct obj *), int); +int askchain(struct obj *, char *, int, int (*)(struct obj *), + bool (*)(struct obj *), int); +void prinv(struct obj *); +int ddoinv(void); +int dotypeinv(void); +int dolook(void); +void stackobj(struct obj *); +int doprgold(void); +int doprwep(void); +int doprarm(void); +int doprring(void); +bool digit(char); + +/* hack.ioctl.c */ +void getioctls(void); +void setioctls(void); +#ifdef SUSPEND +int dosuspend(void); +#endif + +/* hack.lev.c */ +void savelev(int, xchar); +void bwrite(int, char *, unsigned int); +void saveobjchn(int, struct obj *); +void savemonchn(int, struct monst *); +void getlev(int, int, xchar); +void mread(int, char *, unsigned int); +void mklev(void); + +/* hack.main.c */ +void glo(int); +void askname(void); +void impossible(const char *, ...) __printflike(1, 2); +void stop_occupation(void); + +/* hack.makemon.c */ +struct monst *makemon(struct permonst *, int, int); +coord enexto(xchar, xchar); +bool goodpos(int, int); +void rloc(struct monst *); +struct monst *mkmon_at(char, int, int); + +/* hack.mhitu.c */ +bool mhitu(struct monst *); +bool hitu(struct monst *, int); + +/* hack.mklev.c */ +void makelevel(void); +void mktrap(int, int, struct mkroom *); + +/* hack.mkmaze.c */ +void makemaz(void); +coord mazexy(void); + +/* hack.mkobj.c */ +struct obj *mkobj_at(int, int, int); +void mksobj_at(int, int, int); +struct obj *mkobj(int); +struct obj *mksobj(int); +bool letter(char); +int weight(struct obj *); +void mkgold(long, int, int); + +/* hack.mkshop.c */ +#ifndef QUEST +void mkshop(void); +void mkzoo(int); +void mkswamp(void); +#endif + +/* hack.mon.c */ +void movemon(void); +void justswld(struct monst *, const char *); +void youswld(struct monst *, int, int, const char *); +bool dochug(struct monst *); +int m_move(struct monst *, int); +int mfndpos(struct monst *, coord *, int *, int); +int dist(int, int); +void poisoned(const char *, const char *); +void mondead(struct monst *); +void replmon(struct monst *, struct monst *); +void relmon(struct monst *); +void monfree(struct monst *); +void unstuck(struct monst *); +void killed(struct monst *); +void kludge(const char *, const char *); +void rescham(void); +bool newcham(struct monst *, struct permonst *); +void mnexto(struct monst *); +void setmangry(struct monst *); +bool canseemon(struct monst *); + +/* hack.o_init.c */ +int letindex(char); +void init_objects(void); +int probtype(char); +void oinit(void); +void savenames(int); +void restnames(int); +int dodiscovered(void); + +/* hack.objnam.c */ +char *typename(int); +char *xname(struct obj *); +char *doname(struct obj *); +void setan(const char *, char *); +char *aobjnam(struct obj *, const char *); +char *Doname(struct obj *); +struct obj *readobjnam(char *); + +/* hack.options.c */ +void initoptions(void); +int doset(void); + +/* hack.pager.c */ +int dowhatis(void); +void set_whole_screen(void); +#ifdef NEWS +bool readnews(void); +#endif +void set_pager(int); +bool page_line(const char *); +void cornline(int, const char *); +int dohelp(void); +bool page_file(const char *, bool); +#ifdef UNIX +#ifdef SHELL +int dosh(void); +#endif /* SHELL */ +bool child(bool); +#endif /* UNIX */ + +/* hack.potion.c */ +int dodrink(void); +void pluslvl(void); +void strange_feeling(struct obj *, const char *); +void potionhit(struct monst *, struct obj *); +void potionbreathe(struct obj *); +int dodip(void); + +/* hack.pri.c */ +void swallowed(void); +void panic(const char *, ...) __printflike(1, 2); +void atl(int, int, char); +void on_scr(int, int); +void tmp_at(schar, schar); +void Tmp_at(schar, schar); +void setclipped(void); +void at(xchar, xchar, char); +void prme(void); +int doredraw(void); +void docrt(void); +void docorner(int, int); +void curs_on_u(void); +void pru(void); +void prl(int, int); +char news0(xchar, xchar); +void newsym(int, int); +void mnewsym(int, int); +void nosee(int, int); +#ifndef QUEST +void prl1(int, int); +void nose1(int, int); +#endif +bool vism_at(int, int); +void unpobj(struct obj *); +void seeobjs(void); +void seemons(void); +void pmon(struct monst *); +void unpmon(struct monst *); +void nscr(void); +void bot(void); +#ifdef WAN_PROBING +void mstatusline(struct monst *); +#endif +void cls(void); + +/* hack.read.c */ +int doread(void); +int identify(struct obj *); +void litroom(bool); + +/* hack.rip.c */ +void outrip(void); + +/* hack.rumors.c */ +void outrumor(void); + +/* hack.save.c */ +int dosave(void); +#ifndef NOSAVEONHANGUP +void hangup(int); +#endif +bool dorecover(int); +struct obj *restobjchn(int); +struct monst *restmonchn(int); + +/* hack.search.c */ +int findit(void); +int dosearch(void); +int doidtrap(void); +void wakeup(struct monst *); +void seemimic(struct monst *); + +/* hack.shk.c */ +#ifdef QUEST +void obfree(struct obj *, struct obj *); +int inshop(void); +void shopdig(void); +void addtobill(void); +void subfrombill(void); +void splitbill(void); +int dopay(void); +void paybill(void); +int doinvbill(void); +void shkdead(void); +int shkcatch(void); +int shk_move(void); +void replshk(struct monst *, struct monst *); +const char *shkname(void); +#else +char *shkname(struct monst *); +void shkdead(struct monst *); +void replshk(struct monst *, struct monst *); +int inshop(void); +void obfree(struct obj *, struct obj *); +int dopay(void); +void paybill(void); +void addtobill(struct obj *); +void splitbill(struct obj *, struct obj *); +void subfrombill(struct obj *); +int doinvbill(int); +bool shkcatch(struct obj *); +int shk_move(struct monst *); +void shopdig(int); +#endif +bool online(int, int); +bool follower(struct monst *); + +/* hack.shknam.c */ +void findname(char *, char); + +/* hack.steal.c */ +long somegold(void); +void stealgold(struct monst *); +bool steal(struct monst *); +void mpickobj(struct monst *, struct obj *); +bool stealamulet(struct monst *); +void relobj(struct monst *, int); + +/* hack.termcap.c */ +void startup(void); +void start_screen(void); +void end_screen(void); +void curs(int, int); +void cl_end(void); +void clear_screen(void); +void home(void); +void standoutbeg(void); +void standoutend(void); +void backsp(void); +void bell(void); +void cl_eos(void); + +/* hack.timeout.c */ +void p_timeout(void); + +/* hack.topl.c */ +int doredotopl(void); +void remember_topl(void); +void addtopl(const char *); +void more(void); +void cmore(const char *); +void clrlin(void); +void pline(const char *, ...) __printflike(1, 2); +void vpline(const char *, va_list) __printflike(1, 0); +void putsym(char); +void putstr(const char *); + +/* hack.track.c */ +void initrack(void); +void settrack(void); +coord *gettrack(int, int); + +/* hack.trap.c */ +struct trap *maketrap(int, int, int); +void dotrap(struct trap *); +int mintrap(struct monst *); +void selftouch(const char *); +void float_up(void); +void float_down(void); +void tele(void); +int dotele(void); +void placebc(int); +void unplacebc(void); +void level_tele(void); +void drown(void); + +/* hack.tty.c */ +void gettty(void); +void settty(const char *); +void setftty(void); +void error(const char *, ...) __printflike(1, 2); +void getlin(char *); +void getret(void); +void cgetret(const char *); +void xwaitforspace(const char *); +char *parse(void); +char readchar(void); +void end_of_input(void); + +/* hack.u_init.c */ +void u_init(void); +void plnamesuffix(void); + +/* hack.unix.c */ +void setrandom(void); +int getyear(void); +char *getdate(void); +int phase_of_the_moon(void); +bool night(void); +bool midnight(void); +void gethdate(const char *); +bool uptodate(int); +void getlock(void); +#ifdef MAIL +void getmailstatus(void); +void ckmailstatus(void); +void readmail(void); +#endif +void regularize(char *); + +/* hack.vault.c */ +void setgd(void); +int gd_move(void); +void replgd(struct monst *, struct monst *); +void invault(void); +#ifdef QUEST +void gddead(struct monst *); +#else +void gddead(void); +#endif + +/* hack.version.c */ +int doversion(void); + +/* hack.wield.c */ +void setuwep(struct obj *); +int dowield(void); +void corrode_weapon(void); +bool chwepon(struct obj *, int); + +/* hack.wizard.c */ +void amulet(void); +bool wiz_hit(struct monst *); +void inrange(struct monst *); + +/* hack.worm.c */ +#ifndef NOWORM +bool getwn(struct monst *); +void initworm(struct monst *); +void worm_move(struct monst *); +void worm_nomove(struct monst *); +void wormdead(struct monst *); +void wormhit(struct monst *); +void wormsee(unsigned int); +void pwseg(struct wseg *); +void cutworm(struct monst *, xchar, xchar, uchar); +#endif + +/* hack.worn.c */ +void setworn(struct obj *, long); +void setnotworn(struct obj *); + +/* hack.zap.c */ +int dozap(void); +const char *exclam(int); +void hit(const char *, struct monst *, const char *); +void miss(const char *, struct monst *); +struct monst *bhit(int, int, int, char, + void (*)(struct monst *, struct obj *), + bool (*)(struct obj *, struct obj *), struct obj *); +struct monst *boomhit(int, int); +void buzz(int, xchar, xchar, int, int); +void fracture_rock(struct obj *); + +/* rnd.c */ +int rn1(int, int); +int rn2(int); +int rnd(int); +int d(int, int); diff --git a/hack/hack.invent.c b/hack/hack.invent.c new file mode 100644 index 0000000..bae4b75 --- /dev/null +++ b/hack/hack.invent.c @@ -0,0 +1,964 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.invent.c - version 1.0.3 */ +/* $FreeBSD: src/games/hack/hack.invent.c,v 1.4 1999/11/16 10:26:36 marcel Exp $ */ + +#include "hack.h" +extern struct obj zeroobj; +extern char quitchars[]; + +static void assigninvlet(struct obj *); +static struct obj *mkgoldobj(long); +static bool ckunpaid(struct obj *); +static char obj_to_let(struct obj *); +static char *xprname(struct obj *, char); +static void doinv(char *); +static bool merged(struct obj *, struct obj *, bool); +static bool countgold(void); + +#ifndef NOWORM +extern struct wseg *wsegs[32]; +#endif /* NOWORM */ + +#define NOINVSYM '#' + +static int lastinvnr = 51; /* 0 ... 51 */ + +static void +assigninvlet(struct obj *otmp) +{ + boolean inuse[52]; + int i; + struct obj *obj; + + for (i = 0; i < 52; i++) + inuse[i] = FALSE; + for (obj = invent; obj; obj = obj->nobj) + if (obj != otmp) { + i = obj->invlet; + if ('a' <= i && i <= 'z') + inuse[i - 'a'] = TRUE; + else if ('A' <= i && i <= 'Z') + inuse[i - 'A' + 26] = TRUE; + if (i == otmp->invlet) + otmp->invlet = 0; + } + if ((i = otmp->invlet) && + (('a' <= i && i <= 'z') || ('A' <= i && i <= 'Z'))) + return; + for (i = lastinvnr + 1; i != lastinvnr; i++) { + if (i == 52) { + i = -1; + continue; + } + if (!inuse[i]) + break; + } + otmp->invlet = (inuse[i] ? NOINVSYM : + (i < 26) ? ('a' + i) : ('A' + i - 26)); + lastinvnr = i; +} + +struct obj * +addinv(struct obj *obj) +{ + struct obj *otmp; + + /* merge or attach to end of chain */ + if (!invent) { + invent = obj; + otmp = NULL; + } else + for (otmp = invent; /* otmp */; otmp = otmp->nobj) { + if (merged(otmp, obj, 0)) + return (otmp); + if (!otmp->nobj) { + otmp->nobj = obj; + break; + } + } + obj->nobj = 0; + + if (flags.invlet_constant) { + assigninvlet(obj); + /* + * The ordering of the chain is nowhere significant + * so in case you prefer some other order than the + * historical one, change the code below. + */ + if (otmp) { /* find proper place in chain */ + otmp->nobj = 0; + if ((invent->invlet ^ 040) > (obj->invlet ^ 040)) { + obj->nobj = invent; + invent = obj; + } else + for (otmp = invent;; otmp = otmp->nobj) { + if (!otmp->nobj || + (otmp->nobj->invlet ^ 040) > + (obj->invlet ^ 040)) { + obj->nobj = otmp->nobj; + otmp->nobj = obj; + break; + } + } + } + } + + return (obj); +} + +void +useup(struct obj *obj) +{ + if (obj->quan > 1) { + obj->quan--; + obj->owt = weight(obj); + } else { + setnotworn(obj); + freeinv(obj); + obfree(obj, NULL); + } +} + +void +freeinv(struct obj *obj) +{ + struct obj *otmp; + + if (obj == invent) + invent = invent->nobj; + else { + for (otmp = invent; otmp->nobj != obj; otmp = otmp->nobj) + if (!otmp->nobj) + panic("freeinv"); + otmp->nobj = obj->nobj; + } +} + +/* destroy object in fobj chain (if unpaid, it remains on the bill) */ +void +delobj(struct obj *obj) +{ + freeobj(obj); + unpobj(obj); + obfree(obj, NULL); +} + +/* unlink obj from chain starting with fobj */ +void +freeobj(struct obj *obj) +{ + struct obj *otmp; + + if (obj == fobj) + fobj = fobj->nobj; + else { + for (otmp = fobj; otmp->nobj != obj; otmp = otmp->nobj) + if (!otmp) + panic("error in freeobj"); + otmp->nobj = obj->nobj; + } +} + +/* Note: freegold throws away its argument! */ +void +freegold(struct gold *gold) +{ + struct gold *gtmp; + + if (gold == fgold) + fgold = gold->ngold; + else { + for (gtmp = fgold; gtmp->ngold != gold; gtmp = gtmp->ngold) + if (!gtmp) + panic("error in freegold"); + gtmp->ngold = gold->ngold; + } + free(gold); +} + +void +deltrap(struct trap *trap) +{ + struct trap *ttmp; + + if (trap == ftrap) + ftrap = ftrap->ntrap; + else { + for (ttmp = ftrap; ttmp->ntrap != trap; ttmp = ttmp->ntrap) + ; /* nothing */ + ttmp->ntrap = trap->ntrap; + } + free(trap); +} + +struct wseg *m_atseg; + +struct monst * +m_at(int x, int y) +{ + struct monst *mtmp; +#ifndef NOWORM + struct wseg *wtmp; +#endif /* NOWORM */ + + m_atseg = NULL; + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { + if (mtmp->mx == x && mtmp->my == y) + return (mtmp); +#ifndef NOWORM + if (mtmp->wormno) { + for (wtmp = wsegs[mtmp->wormno]; wtmp; wtmp = wtmp->nseg) + if (wtmp->wx == x && wtmp->wy == y) { + m_atseg = wtmp; + return (mtmp); + } + } + +#endif /* NOWORM */ + } + return (0); +} + +struct obj * +o_at(int x, int y) +{ + struct obj *otmp; + + for (otmp = fobj; otmp; otmp = otmp->nobj) + if (otmp->ox == x && otmp->oy == y) + return (otmp); + return (0); +} + +struct obj * +sobj_at(int n, int x, int y) +{ + struct obj *otmp; + + for (otmp = fobj; otmp; otmp = otmp->nobj) + if (otmp->ox == x && otmp->oy == y && otmp->otyp == n) + return (otmp); + return (0); +} + +bool +carried(struct obj *obj) +{ + struct obj *otmp; + + for (otmp = invent; otmp; otmp = otmp->nobj) + if (otmp == obj) + return (1); + return (0); +} + +bool +carrying(int type) +{ + struct obj *otmp; + + for (otmp = invent; otmp; otmp = otmp->nobj) + if (otmp->otyp == type) + return (TRUE); + return (FALSE); +} + +struct obj * +o_on(unsigned int id, struct obj *objchn) +{ + while (objchn) { + if (objchn->o_id == id) + return (objchn); + objchn = objchn->nobj; + } + return (NULL); +} + +struct trap * +t_at(int x, int y) +{ + struct trap *trap = ftrap; + + while (trap) { + if (trap->tx == x && trap->ty == y) + return (trap); + trap = trap->ntrap; + } + return (0); +} + +struct gold * +g_at(int x, int y) +{ + struct gold *gold = fgold; + + while (gold) { + if (gold->gx == x && gold->gy == y) + return (gold); + gold = gold->ngold; + } + return (0); +} + +/* make dummy object structure containing gold - for temporary use only */ +static struct obj * +mkgoldobj(long q) +{ + struct obj *otmp; + + otmp = newobj(0); + /* should set o_id etc. but otmp will be freed soon */ + otmp->olet = '$'; + u.ugold -= q; + OGOLD(otmp) = q; + flags.botl = 1; + return (otmp); +} + +/* + * getobj returns: + * struct obj *xxx: object to do something with. + * NULL error return: no object. + * &zeroobj explicitly no object (as in w-). + */ +struct obj * +getobj(const char *let, const char *word) +{ + struct obj *otmp; + char ilet, ilet1, ilet2; + char buf[BUFSZ]; + char lets[BUFSZ]; + int foo = 0, foo2; + char *bp = buf; + xchar allowcnt = 0; /* 0, 1 or 2 */ + boolean allowgold = FALSE; + boolean allowall = FALSE; + boolean allownone = FALSE; + xchar foox = 0; + long cnt; + + if (*let == '0') + let++, allowcnt = 1; + if (*let == '$') + let++, allowgold = TRUE; + if (*let == '#') + let++, allowall = TRUE; + if (*let == '-') + let++, allownone = TRUE; + if (allownone) + *bp++ = '-'; + if (allowgold) + *bp++ = '$'; + if (bp > buf && bp[-1] == '-') + *bp++ = ' '; + + ilet = 'a'; + for (otmp = invent; otmp; otmp = otmp->nobj) { + if (!*let || strchr(let, otmp->olet)) { + bp[foo++] = flags.invlet_constant ? otmp->invlet : ilet; + + /* ugly check: remove inappropriate things */ + if ((!strcmp(word, "take off") && + !(otmp->owornmask & (W_ARMOR - W_ARM2))) + || (!strcmp(word, "wear") && + (otmp->owornmask & (W_ARMOR | W_RING))) + || (!strcmp(word, "wield") && + (otmp->owornmask & W_WEP))) { + foo--; + foox++; + } + } + if (ilet == 'z') + ilet = 'A'; + else + ilet++; + } + bp[foo] = 0; + if (foo == 0 && bp > buf && bp[-1] == ' ') + *--bp = 0; + strcpy(lets, bp); /* necessary since we destroy buf */ + if (foo > 5) { /* compactify string */ + foo = foo2 = 1; + ilet2 = bp[0]; + ilet1 = bp[1]; + while ((ilet = bp[++foo2] = bp[++foo])) { + if (ilet == ilet1 + 1) { + if (ilet1 == ilet2 + 1) + bp[foo2 - 1] = ilet1 = '-'; + else if (ilet2 == '-') { + bp[--foo2] = ++ilet1; + continue; + } + } + ilet2 = ilet1; + ilet1 = ilet; + } + } + if (!foo && !allowall && !allowgold && !allownone) { + pline("You don't have anything %sto %s.", + foox ? "else " : "", word); + return (0); + } + for (;;) { + if (!buf[0]) + pline("What do you want to %s [*]? ", word); + else + pline("What do you want to %s [%s or ?*]? ", + word, buf); + + cnt = 0; + ilet = readchar(); + while (digit(ilet) && allowcnt) { + if (cnt < 100000000) + cnt = 10 * cnt + (ilet - '0'); + else + cnt = 999999999; + allowcnt = 2; /* signal presence of cnt */ + ilet = readchar(); + } + if (digit(ilet)) { + pline("No count allowed with this command."); + continue; + } + if (strchr(quitchars, ilet)) + return (NULL); + if (ilet == '-') + return (allownone ? &zeroobj : NULL); + if (ilet == '$') { + if (!allowgold) { + pline("You cannot %s gold.", word); + continue; + } + if (!(allowcnt == 2 && cnt < u.ugold)) + cnt = u.ugold; + return (mkgoldobj(cnt)); + } + if (ilet == '?') { + doinv(lets); + if (!(ilet = morc)) + continue; + /* he typed a letter (not a space) to more() */ + } else if (ilet == '*') { + doinv(NULL); + if (!(ilet = morc)) + continue; + /* ... */ + } + if (flags.invlet_constant) { + for (otmp = invent; otmp; otmp = otmp->nobj) + if (otmp->invlet == ilet) + break; + } else { + if (ilet >= 'A' && ilet <= 'Z') + ilet += 'z' - 'A' + 1; + ilet -= 'a'; + for (otmp = invent; otmp && ilet; + ilet--, otmp = otmp->nobj) + ; /* nothing */ + } + if (!otmp) { + pline("You don't have that object."); + continue; + } + if (cnt < 0 || otmp->quan < cnt) { + pline("You don't have that many! [You have %u]" , + otmp->quan); + continue; + } + break; + } + if (!allowall && let && !strchr(let, otmp->olet)) { + pline("That is a silly thing to %s.", word); + return (0); + } + if (allowcnt == 2) { /* cnt given */ + if (cnt == 0) + return (0); + if (cnt != otmp->quan) { + struct obj *obj; + obj = splitobj(otmp, (int)cnt); + if (otmp == uwep) + setuwep(obj); + } + } + return (otmp); +} + +static bool +ckunpaid(struct obj *otmp) +{ + return (otmp->unpaid); +} + +/* interactive version of getobj - used for Drop and Identify */ +/* return the number of times fn was called successfully */ +int +ggetobj(const char *word, int (*fn)(struct obj *), int max) +{ + char buf[BUFSZ]; + char *ip; + char sym; + int oletct = 0, iletct = 0; + boolean allflag = FALSE; + char olets[20], ilets[20]; + bool (*ckfn)(struct obj *) = (bool (*)()) 0; + xchar allowgold = (u.ugold && !strcmp(word, "drop")) ? 1 : 0; /* BAH */ + + if (!invent && !allowgold) { + pline("You have nothing to %s.", word); + return (0); + } else { + struct obj *otmp = invent; + int uflg = 0; + + if (allowgold) + ilets[iletct++] = '$'; + ilets[iletct] = 0; + while (otmp) { + if (!strchr(ilets, otmp->olet)) { + ilets[iletct++] = otmp->olet; + ilets[iletct] = 0; + } + if (otmp->unpaid) + uflg = 1; + otmp = otmp->nobj; + } + ilets[iletct++] = ' '; + if (uflg) + ilets[iletct++] = 'u'; + if (invent) + ilets[iletct++] = 'a'; + ilets[iletct] = 0; + } + pline("What kinds of thing do you want to %s? [%s] ", + word, ilets); + getlin(buf); + if (buf[0] == '\033') { + clrlin(); + return (0); + } + ip = buf; + olets[0] = 0; + while ((sym = *ip++)) { + if (sym == ' ') + continue; + if (sym == '$') { + if (allowgold == 1) + (*fn)(mkgoldobj(u.ugold)); + else if (!u.ugold) + pline("You have no gold."); + allowgold = 2; + } else if (sym == 'a' || sym == 'A') + allflag = TRUE; + else if (sym == 'u' || sym == 'U') + ckfn = ckunpaid; + else if (strchr("!%?[()=*/\"0", sym)) { + if (!strchr(olets, sym)) { + olets[oletct++] = sym; + olets[oletct] = 0; + } + } else + pline("You don't have any %c's.", sym); + } + if (allowgold == 2 && !oletct) + return (1); /* he dropped gold (or at least tried to) */ + else + return (askchain(invent, olets, allflag, fn, ckfn, max)); +} + +/* + * Walk through the chain starting at objchn and ask for all objects + * with olet in olets (if nonNULL) and satisfying ckfn (if nonNULL) + * whether the action in question (i.e., fn) has to be performed. + * If allflag then no questions are asked. Max gives the max nr of + * objects to be treated. Return the number of objects treated. + */ +int +askchain(struct obj *objchn, char *olets, int allflag, + int (*fn)(struct obj *), bool (*ckfn)(struct obj *), int max) +{ + struct obj *otmp, *otmp2; + char sym, ilet; + int cnt = 0; + + ilet = 'a' - 1; + for (otmp = objchn; otmp; otmp = otmp2) { + if (ilet == 'z') + ilet = 'A'; + else + ilet++; + otmp2 = otmp->nobj; + if (olets && *olets && !strchr(olets, otmp->olet)) + continue; + if (ckfn && !(*ckfn)(otmp)) + continue; + if (!allflag) { + pline("%s", xprname(otmp, ilet)); + addtopl(" [nyaq]? "); + sym = readchar(); + } else + sym = 'y'; + + switch (sym) { + case 'a': + allflag = 1; + case 'y': + cnt += (*fn)(otmp); + if (--max == 0) + goto ret; + case 'n': + default: + break; + case 'q': + goto ret; + } + } + pline(cnt ? "That was all." : "No applicable objects."); +ret: + return (cnt); +} + +/* should of course only be called for things in invent */ +static char +obj_to_let(struct obj *obj) +{ + struct obj *otmp; + char ilet; + + if (flags.invlet_constant) + return (obj->invlet); + ilet = 'a'; + for (otmp = invent; otmp && otmp != obj; otmp = otmp->nobj) + if (++ilet > 'z') + ilet = 'A'; + return (otmp ? ilet : NOINVSYM); +} + +void +prinv(struct obj *obj) +{ + pline("%s", xprname(obj, obj_to_let(obj))); +} + +static char * +xprname(struct obj *obj, char let) +{ + static char li[BUFSZ]; + + sprintf(li, "%c - %s.", + flags.invlet_constant ? obj->invlet : let, + doname(obj)); + return (li); +} + +int +ddoinv(void) +{ + doinv(NULL); + return (0); +} + +/* called with 0 or "": all objects in inventory */ +/* otherwise: all objects with (serial) letter in lets */ +static void +doinv(char *lets) +{ + struct obj *otmp; + char ilet; + int ct = 0; + char any[BUFSZ]; + + morc = 0; /* just to be sure */ + if (!invent) { + pline("Not carrying anything."); + return; + } + + cornline(0, NULL); + ilet = 'a'; + for (otmp = invent; otmp; otmp = otmp->nobj) { + if (flags.invlet_constant) + ilet = otmp->invlet; + if (!lets || !*lets || strchr(lets, ilet)) { + cornline(1, xprname(otmp, ilet)); + any[ct++] = ilet; + } + if (!flags.invlet_constant) + if (++ilet > 'z') + ilet = 'A'; + } + any[ct] = 0; + cornline(2, any); +} + +int +dotypeinv(void) /* free after Robert Viduya */ +{ + /* Changed to one type only, so he doesnt have to type cr */ + char c, ilet; + char stuff[BUFSZ]; + int stct; + struct obj *otmp; + boolean billx = inshop() && doinvbill(0); + boolean unpd = FALSE; + + if (!invent && !u.ugold && !billx) { + pline("You aren't carrying anything."); + return (0); + } + + stct = 0; + if (u.ugold) + stuff[stct++] = '$'; + stuff[stct] = 0; + for (otmp = invent; otmp; otmp = otmp->nobj) { + if (!strchr(stuff, otmp->olet)) { + stuff[stct++] = otmp->olet; + stuff[stct] = 0; + } + if (otmp->unpaid) + unpd = TRUE; + } + if (unpd) + stuff[stct++] = 'u'; + if (billx) + stuff[stct++] = 'x'; + stuff[stct] = 0; + + if (stct > 1) { + pline("What type of object [%s] do you want an inventory of? ", + stuff); + c = readchar(); + if (strchr(quitchars, c)) + return (0); + } else + c = stuff[0]; + + if (c == '$') + return (doprgold()); + + if (c == 'x' || c == 'X') { + if (billx) + doinvbill(1); + else + pline("No used-up objects on the shopping bill."); + return (0); + } + + if ((c == 'u' || c == 'U') && !unpd) { + pline("You are not carrying any unpaid objects."); + return (0); + } + + stct = 0; + ilet = 'a'; + for (otmp = invent; otmp; otmp = otmp->nobj) { + if (flags.invlet_constant) + ilet = otmp->invlet; + if (c == otmp->olet || (c == 'u' && otmp->unpaid)) + stuff[stct++] = ilet; + if (!flags.invlet_constant) + if (++ilet > 'z') + ilet = 'A'; + } + stuff[stct] = '\0'; + if (stct == 0) + pline("You have no such objects."); + else + doinv(stuff); + + return (0); +} + +/* look at what is here */ +int +dolook(void) +{ + struct obj *otmp, *otmp0 = NULL; + struct gold *gold = NULL; + const char *verb = Blind ? "feel" : "see"; + int ct = 0; + + if (!u.uswallow) { + if (Blind) { + pline("You try to feel what is lying here on the floor."); + if (Levitation) { /* ab@unido */ + pline("You cannot reach the floor!"); + return (1); + } + } + otmp0 = o_at(u.ux, u.uy); + gold = g_at(u.ux, u.uy); + } + + if (u.uswallow || (!otmp0 && !gold)) { + pline("You %s no objects here.", verb); + return (!!Blind); + } + + cornline(0, "Things that are here:"); + for (otmp = otmp0; otmp; otmp = otmp->nobj) { + if (otmp->ox == u.ux && otmp->oy == u.uy) { + ct++; + cornline(1, doname(otmp)); + if (Blind && otmp->otyp == DEAD_COCKATRICE && !uarmg) { + pline("Touching the dead cockatrice is a fatal mistake ..."); + pline("You die ..."); + killer = "dead cockatrice"; + done("died"); + } + } + } + + if (gold) { + char gbuf[30]; + + sprintf(gbuf, "%ld gold piece%s", + gold->amount, plur(gold->amount)); + if (!ct++) + pline("You %s here %s.", verb, gbuf); + else + cornline(1, gbuf); + } + + if (ct == 1 && !gold) { + pline("You %s here %s.", verb, doname(otmp0)); + cornline(3, NULL); + } + if (ct > 1) + cornline(2, NULL); + return (!!Blind); +} + +void +stackobj(struct obj *obj) +{ + struct obj *otmp = fobj; + + for (otmp = fobj; otmp; otmp = otmp->nobj) + if (otmp != obj) + if (otmp->ox == obj->ox && otmp->oy == obj->oy && + merged(obj, otmp, 1)) + return; +} + +/* merge obj with otmp and delete obj if types agree */ +static bool +merged(struct obj *otmp, struct obj *obj, bool lose) +{ + if (obj->otyp == otmp->otyp && + obj->unpaid == otmp->unpaid && + obj->spe == otmp->spe && + obj->dknown == otmp->dknown && + obj->cursed == otmp->cursed && + (strchr("%*?!", obj->olet) || + (obj->known == otmp->known && + (obj->olet == WEAPON_SYM && obj->otyp < BOOMERANG)))) { + otmp->quan += obj->quan; + otmp->owt += obj->owt; + if (lose) + freeobj(obj); + obfree(obj, otmp); /* free(obj), bill->otmp */ + return (1); + } else + return (0); +} + +/* + * Gold is no longer displayed; in fact, when you have a lot of money, + * it may take a while before you have counted it all. + * [Bug: d$ and pickup still tell you how much it was.] + */ +static long goldcounted; + +static bool +countgold(void) +{ + if ((goldcounted += 100 * (u.ulevel + 1)) >= u.ugold) { + long eps = 0; + if (!rn2(2)) + eps = rnd((int)(u.ugold / 100 + 1)); + pline("You probably have about %ld gold pieces.", + u.ugold + eps); + return (0); /* done */ + } + return (1); /* continue */ +} + +int +doprgold(void) +{ + if (!u.ugold) + pline("You do not carry any gold."); + else if (u.ugold <= 500) + pline("You are carrying %ld gold pieces.", u.ugold); + else { + pline("You sit down in order to count your gold pieces."); + goldcounted = 500; + occupation = countgold; + occtxt = "counting your gold"; + } + return (1); +} + +/* --- end of gold counting section --- */ + +int +doprwep(void) +{ + if (!uwep) + pline("You are empty handed."); + else + prinv(uwep); + return (0); +} + +int +doprarm(void) +{ + if (!uarm && !uarmg && !uarms && !uarmh) + pline("You are not wearing any armor."); + else { + char lets[6]; + int ct = 0; + + if (uarm) + lets[ct++] = obj_to_let(uarm); + if (uarm2) + lets[ct++] = obj_to_let(uarm2); + if (uarmh) + lets[ct++] = obj_to_let(uarmh); + if (uarms) + lets[ct++] = obj_to_let(uarms); + if (uarmg) + lets[ct++] = obj_to_let(uarmg); + lets[ct] = 0; + doinv(lets); + } + return (0); +} + +int +doprring(void) +{ + if (!uleft && !uright) + pline("You are not wearing any rings."); + else { + char lets[3]; + int ct = 0; + + if (uleft) + lets[ct++] = obj_to_let(uleft); + if (uright) + lets[ct++] = obj_to_let(uright); + lets[ct] = 0; + doinv(lets); + } + return (0); +} + +bool +digit(char c) +{ + return (c >= '0' && c <= '9'); +} diff --git a/hack/hack.ioctl.c b/hack/hack.ioctl.c new file mode 100644 index 0000000..980755a --- /dev/null +++ b/hack/hack.ioctl.c @@ -0,0 +1,47 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.ioctl.c - version 1.0.2 */ +/* $FreeBSD: src/games/hack/hack.ioctl.c,v 1.2 1999/09/12 07:01:23 marcel Exp $ + * $DragonFly: src/games/hack/hack.ioctl.c,v 1.4 2006/08/21 19:45:32 pavalos Exp $ + * + * This cannot be part of hack.tty.c (as it was earlier) since on some + * systems (e.g. MUNIX) the include files <termio.h> and <sgtty.h> define the + * same constants, and the C preprocessor complains. + */ +#include "hack.h" +#include <termios.h> +struct termios termio; + +void +getioctls(void) +{ + tcgetattr(fileno(stdin), &termio); +} + +void +setioctls(void) +{ + tcsetattr(fileno(stdin), TCSANOW, &termio); +} + +#ifdef SUSPEND +#include <signal.h> +int +dosuspend(void) +{ +#ifdef SIGTSTP + if (signal(SIGTSTP, SIG_IGN) == SIG_DFL) { + settty(NULL); + signal(SIGTSTP, SIG_DFL); + kill(0, SIGTSTP); + gettty(); + setftty(); + docrt(); + } else { + pline("I don't think your shell has job control."); + } +#else /* SIGTSTP */ + pline("Sorry, it seems we have no SIGTSTP here. Try ! or S."); +#endif /* SIGTSTP */ + return (0); +} +#endif /* SUSPEND */ diff --git a/hack/hack.lev.c b/hack/hack.lev.c new file mode 100644 index 0000000..d47b873 --- /dev/null +++ b/hack/hack.lev.c @@ -0,0 +1,276 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.lev.c - version 1.0.3 */ +/* $FreeBSD: src/games/hack/hack.lev.c,v 1.4 1999/11/16 10:26:36 marcel Exp $ */ + +#include "hack.h" +extern struct obj *billobjs; +extern char SAVEF[]; +extern char nul[]; + +#ifndef NOWORM +extern struct wseg *wsegs[32], *wheads[32]; +extern long wgrowtime[32]; +#endif /* NOWORM */ + +boolean level_exists[MAXLEVEL+1]; + +static void savegoldchn(int, struct gold *); +static void savetrapchn(int, struct trap *); + +void +savelev(int fd, xchar lev) +{ +#ifndef NOWORM + struct wseg *wtmp, *wtmp2; + int tmp; +#endif /* NOWORM */ + + if (fd < 0) + panic("Save on bad file!"); /* impossible */ + if (lev >= 0 && lev <= MAXLEVEL) + level_exists[lev] = TRUE; + + bwrite(fd, (char *)&hackpid, sizeof(hackpid)); + bwrite(fd, (char *)&lev, sizeof(lev)); + bwrite(fd, (char *)levl, sizeof(levl)); + bwrite(fd, (char *)&moves, sizeof(long)); + bwrite(fd, (char *)&xupstair, sizeof(xupstair)); + bwrite(fd, (char *)&yupstair, sizeof(yupstair)); + bwrite(fd, (char *)&xdnstair, sizeof(xdnstair)); + bwrite(fd, (char *)&ydnstair, sizeof(ydnstair)); + savemonchn(fd, fmon); + savegoldchn(fd, fgold); + savetrapchn(fd, ftrap); + saveobjchn(fd, fobj); + saveobjchn(fd, billobjs); + billobjs = NULL; + save_engravings(fd); +#ifndef QUEST + bwrite(fd, (char *)rooms, sizeof(rooms)); + bwrite(fd, (char *)doors, sizeof(doors)); +#endif /* QUEST */ + fgold = 0; + ftrap = 0; + fmon = 0; + fobj = 0; +#ifndef NOWORM + bwrite(fd, (char *)wsegs, sizeof(wsegs)); + for (tmp = 1; tmp < 32; tmp++) { + for (wtmp = wsegs[tmp]; wtmp; wtmp = wtmp2) { + wtmp2 = wtmp->nseg; + bwrite(fd, (char *)wtmp, sizeof(struct wseg)); + } + wsegs[tmp] = NULL; + } + bwrite(fd, (char *)wgrowtime, sizeof(wgrowtime)); +#endif /* NOWORM */ +} + +void +bwrite(int fd, char *loc, unsigned int num) +{ + /* lint wants the 3rd arg of write to be an int; lint -p an unsigned */ + if (write(fd, loc, (int)num) != (int)num) + panic("cannot write %u bytes to file #%d", num, fd); +} + +void +saveobjchn(int fd, struct obj *otmp) +{ + struct obj *otmp2; + unsigned xl; + int minusone = -1; + + while (otmp) { + otmp2 = otmp->nobj; + xl = otmp->onamelth; + bwrite(fd, (char *)&xl, sizeof(int)); + bwrite(fd, (char *)otmp, xl + sizeof(struct obj)); + free(otmp); + otmp = otmp2; + } + bwrite(fd, (char *)&minusone, sizeof(int)); +} + +void +savemonchn(int fd, struct monst *mtmp) +{ + struct monst *mtmp2; + unsigned xl; + int minusone = -1; + struct permonst *monbegin = &mons[0]; + + bwrite(fd, (char *)&monbegin, sizeof(monbegin)); + + while (mtmp) { + mtmp2 = mtmp->nmon; + xl = mtmp->mxlth + mtmp->mnamelth; + bwrite(fd, (char *)&xl, sizeof(int)); + bwrite(fd, (char *)mtmp, xl + sizeof(struct monst)); + if (mtmp->minvent) + saveobjchn(fd, mtmp->minvent); + free(mtmp); + mtmp = mtmp2; + } + bwrite(fd, (char *)&minusone, sizeof(int)); +} + +static void +savegoldchn(int fd, struct gold *gold) +{ + struct gold *gold2; + + while (gold) { + gold2 = gold->ngold; + bwrite(fd, (char *)gold, sizeof(struct gold)); + free(gold); + gold = gold2; + } + bwrite(fd, nul, sizeof(struct gold)); +} + +static void +savetrapchn(int fd, struct trap *trap) +{ + struct trap *trap2; + + while (trap) { + trap2 = trap->ntrap; + bwrite(fd, (char *)trap, sizeof(struct trap)); + free(trap); + trap = trap2; + } + bwrite(fd, nul, sizeof(struct trap)); +} + +void +getlev(int fd, int pid, xchar lev) +{ + struct gold *gold; + struct trap *trap; +#ifndef NOWORM + struct wseg *wtmp; +#endif /* NOWORM */ + int tmp; + long omoves; + int hpid; + xchar dlvl; + + /* First some sanity checks */ + mread(fd, (char *)&hpid, sizeof(hpid)); + mread(fd, (char *)&dlvl, sizeof(dlvl)); + if ((pid && pid != hpid) || (lev && dlvl != lev)) { + pline("Strange, this map is not as I remember it."); + pline("Somebody is trying some trickery here ..."); + pline("This game is void ..."); + done("tricked"); + } + + fgold = 0; + ftrap = 0; + mread(fd, (char *)levl, sizeof(levl)); + mread(fd, (char *)&omoves, sizeof(omoves)); + mread(fd, (char *)&xupstair, sizeof(xupstair)); + mread(fd, (char *)&yupstair, sizeof(yupstair)); + mread(fd, (char *)&xdnstair, sizeof(xdnstair)); + mread(fd, (char *)&ydnstair, sizeof(ydnstair)); + + fmon = restmonchn(fd); + + /* regenerate animals while on another level */ + { + long tmoves = (moves > omoves) ? moves - omoves : 0; + struct monst *mtmp, *mtmp2; + + for (mtmp = fmon; mtmp; mtmp = mtmp2) { + long newhp; /* tmoves may be very large */ + + mtmp2 = mtmp->nmon; + if (strchr(genocided, mtmp->data->mlet)) { + mondead(mtmp); + continue; + } + + if (mtmp->mtame && tmoves > 250) { + mtmp->mtame = 0; + mtmp->mpeaceful = 0; + } + + newhp = mtmp->mhp + + (strchr(MREGEN, mtmp->data->mlet) ? tmoves : tmoves / 20); + if (newhp > mtmp->mhpmax) + mtmp->mhp = mtmp->mhpmax; + else + mtmp->mhp = newhp; + } + } + + setgd(); + gold = newgold(); + mread(fd, (char *)gold, sizeof(struct gold)); + while (gold->gx) { + gold->ngold = fgold; + fgold = gold; + gold = newgold(); + mread(fd, (char *)gold, sizeof(struct gold)); + } + free(gold); + trap = newtrap(); + mread(fd, (char *)trap, sizeof(struct trap)); + while (trap->tx) { + trap->ntrap = ftrap; + ftrap = trap; + trap = newtrap(); + mread(fd, (char *)trap, sizeof(struct trap)); + } + free(trap); + fobj = restobjchn(fd); + billobjs = restobjchn(fd); + rest_engravings(fd); +#ifndef QUEST + mread(fd, (char *)rooms, sizeof(rooms)); + mread(fd, (char *)doors, sizeof(doors)); +#endif /* QUEST */ +#ifndef NOWORM + mread(fd, (char *)wsegs, sizeof(wsegs)); + for (tmp = 1; tmp < 32; tmp++) + if (wsegs[tmp]) { + wheads[tmp] = wsegs[tmp] = wtmp = newseg(); + for (;;) { + mread(fd, (char *)wtmp, sizeof(struct wseg)); + if (!wtmp->nseg) + break; + wheads[tmp]->nseg = wtmp = newseg(); + wheads[tmp] = wtmp; + } + } + mread(fd, (char *)wgrowtime, sizeof(wgrowtime)); +#endif /* NOWORM */ +} + +void +mread(int fd, char *buf, unsigned int len) +{ + int rlen; + + rlen = read(fd, buf, (int)len); + if (rlen != (int)len) { + pline("Read %d instead of %u bytes.\n", rlen, len); + if (restoring) { + unlink(SAVEF); + error("Error restoring old game."); + } + panic("Error reading level file."); + } +} + +void +mklev(void) +{ + if (getbones()) + return; + + in_mklev = TRUE; + makelevel(); + in_mklev = FALSE; +} diff --git a/hack/hack.main.c b/hack/hack.main.c new file mode 100644 index 0000000..d17bdd9 --- /dev/null +++ b/hack/hack.main.c @@ -0,0 +1,507 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.main.c - version 1.0.3 */ +/* $FreeBSD: src/games/hack/hack.main.c,v 1.9 1999/11/16 10:26:36 marcel Exp $ */ + +#include <sys/stat.h> +#include "hack.h" + +#ifdef QUEST +#define gamename "quest" +#else +#define gamename "hack" +#endif + +void (*afternmv)(void); +bool (*occupation)(void); +const char *occtxt; + + +int hackpid; /* current pid */ +int locknum; /* max num of players */ +#ifdef DEF_PAGER +char *catmore; /* default pager */ +#endif +char SAVEF[PL_NSIZ + 11] = "save/"; /* save/99999player */ +char *hname; /* name of the game (argv[0] of call) */ +char obuf[BUFSIZ]; /* BUFSIZ is defined in stdio.h */ + +extern long wailmsg; + +#ifdef CHDIR +static void chdirx(const char *, bool); +#endif + +int +main(int argc, char *argv[]) +{ + int fd; +#ifdef CHDIR + char *dir; +#endif + + hname = argv[0]; + hackpid = getpid(); + +#ifdef CHDIR /* otherwise no chdir() */ + /* + * See if we must change directory to the playground. + * (Perhaps hack runs suid and playground is inaccessible + * for the player.) + * The environment variable HACKDIR is overridden by a + * -d command line option (must be the first option given) + */ + + dir = getenv("HACKDIR"); + if (argc > 1 && !strncmp(argv[1], "-d", 2)) { + argc--; + argv++; + dir = argv[0] + 2; + if (*dir == '=' || *dir == ':') + dir++; + if (!*dir && argc > 1) { + argc--; + argv++; + dir = argv[0]; + } + if (!*dir) + error("Flag -d must be followed by a directory name."); + } +#endif + + /* + * Who am i? Algorithm: 1. Use name as specified in HACKOPTIONS + * 2. Use $USER or $LOGNAME (if 1. fails) + * 3. Use getlogin() (if 2. fails) + * The resulting name is overridden by command line options. + * If everything fails, or if the resulting name is some generic + * account like "games", "play", "player", "hack" then eventually + * we'll ask him. + * Note that we trust him here; it is possible to play under + * somebody else's name. + */ + { + char *s; + + initoptions(); + if (!*plname && (s = getenv("USER"))) + strncpy(plname, s, sizeof(plname) - 1); + if (!*plname && (s = getenv("LOGNAME"))) + strncpy(plname, s, sizeof(plname) - 1); + if (!*plname && (s = getlogin())) + strncpy(plname, s, sizeof(plname) - 1); + } + + /* + * Now we know the directory containing 'record' and + * may do a prscore(). + */ + if (argc > 1 && !strncmp(argv[1], "-s", 2)) { +#ifdef CHDIR + chdirx(dir, 0); +#endif + prscore(argc, argv); + exit(0); + } + + /* + * It seems he really wants to play. + * Remember tty modes, to be restored on exit. + */ + gettty(); + setbuf(stdout, obuf); + umask(007); + setrandom(); + startup(); + cls(); + u.uhp = 1; /* prevent RIP on early quits */ + u.ux = FAR; /* prevent nscr() */ + signal(SIGHUP, hangup); + + /* + * Find the creation date of this game, + * so as to avoid restoring outdated savefiles. + */ + gethdate(hname); + + /* + * We cannot do chdir earlier, otherwise gethdate will fail. + */ +#ifdef CHDIR + chdirx(dir, 1); +#endif + + /* + * Process options. + */ + while (argc > 1 && argv[1][0] == '-') { + argv++; + argc--; + switch (argv[0][1]) { +#ifdef WIZARD + case 'D': + wizard = TRUE; + break; +#endif +#ifdef NEWS + case 'n': + flags.nonews = TRUE; + break; +#endif + case 'u': + if (argv[0][2]) + strncpy(plname, argv[0] + 2, sizeof(plname) - 1); + else if (argc > 1) { + argc--; + argv++; + strncpy(plname, argv[0], sizeof(plname) - 1); + } else + printf("Player name expected after -u\n"); + break; + default: + /* allow -T for Tourist, etc. */ + strncpy(pl_character, argv[0] + 1, + sizeof(pl_character) - 1); + } + } + + if (argc > 1) + locknum = atoi(argv[1]); +#ifdef MAX_NR_OF_PLAYERS + if (!locknum || locknum > MAX_NR_OF_PLAYERS) + locknum = MAX_NR_OF_PLAYERS; +#endif +#ifdef DEF_PAGER + if (!(catmore = getenv("HACKPAGER")) && !(catmore = getenv("PAGER"))) + catmore = DEF_PAGER; +#endif +#ifdef MAIL + getmailstatus(); +#endif +#ifdef WIZARD + if (wizard) + strcpy(plname, "wizard"); + else +#endif + if (!*plname || !strncmp(plname, "player", 4) + || !strncmp(plname, "games", 4)) + askname(); + plnamesuffix(); /* strip suffix from name; calls askname() */ + /* again if suffix was whole name */ + /* accepts any suffix */ +#ifdef WIZARD + if (!wizard) { +#endif + /* + * check for multiple games under the same name + * (if !locknum) or check max nr of players (otherwise) + */ + signal(SIGQUIT, SIG_IGN); + signal(SIGINT, SIG_IGN); + if (!locknum) + strcpy(lock, plname); + getlock(); /* sets lock if locknum != 0 */ +#ifdef WIZARD + } else { + char *sfoo; + strcpy(lock, plname); + if ((sfoo = getenv("MAGIC"))) + while (*sfoo) { + switch (*sfoo++) { + case 'n': + srandom(*sfoo++); + break; + } + } + if ((sfoo = getenv("GENOCIDED")) != NULL) { + if (*sfoo == '!') { + struct permonst *pm = mons; + char *gp = genocided; + + while (pm < mons + CMNUM + 2) { + if (!strchr(sfoo, pm->mlet)) + *gp++ = pm->mlet; + pm++; + } + *gp = 0; + } else + strncpy(genocided, sfoo, sizeof(genocided) - 1); + strcpy(fut_geno, genocided); + } + } +#endif + setftty(); + sprintf(SAVEF, "save/%d%s", getuid(), plname); + regularize(SAVEF + 5); /* avoid . or / in name */ + if ((fd = open(SAVEF, O_RDONLY)) >= 0 && + (uptodate(fd) || unlink(SAVEF) == 666)) { + signal(SIGINT, done1); + pline("Restoring old save file..."); + fflush(stdout); + if (!dorecover(fd)) + goto not_recovered; + pline("Hello %s, welcome to %s!", plname, gamename); + flags.move = 0; + } else { +not_recovered: + fobj = fcobj = invent = 0; + fmon = fallen_down = 0; + ftrap = 0; + fgold = 0; + flags.ident = 1; + init_objects(); + u_init(); + + signal(SIGINT, done1); + mklev(); + u.ux = xupstair; + u.uy = yupstair; + inshop(); + setsee(); + flags.botlx = 1; + makedog(); + { + struct monst *mtmp; + if ((mtmp = m_at(u.ux, u.uy)) != NULL) + mnexto(mtmp); /* riv05!a3 */ + } + seemons(); +#ifdef NEWS + if (flags.nonews || !readnews()) + /* after reading news we did docrt() already */ +#endif + docrt(); + + /* give welcome message before pickup messages */ + pline("Hello %s, welcome to %s!", plname, gamename); + + pickup(1); + read_engr_at(u.ux, u.uy); + flags.move = 1; + } + + flags.moonphase = phase_of_the_moon(); + if (flags.moonphase == FULL_MOON) { + pline("You are lucky! Full moon tonight."); + u.uluck++; + } else if (flags.moonphase == NEW_MOON) + pline("Be careful! New moon tonight."); + + initrack(); + + for (;;) { + if (flags.move) { /* actual time passed */ + settrack(); + + if (moves % 2 == 0 || + (!(Fast & ~INTRINSIC) && (!Fast || rn2(3)))) { + movemon(); + if (!rn2(70)) + makemon(NULL, 0, 0); + } + if (Glib) + glibr(); + p_timeout(); + ++moves; + if (flags.time) + flags.botl = 1; + if (u.uhp < 1) { + pline("You die..."); + done("died"); + } + if (u.uhp * 10 < u.uhpmax && moves - wailmsg > 50) { + wailmsg = moves; + if (u.uhp == 1) + pline("You hear the wailing of the Banshee..."); + else + pline("You hear the howling of the CwnAnnwn..."); + } + if (u.uhp < u.uhpmax) { + if (u.ulevel > 9) { + if (Regeneration || !(moves % 3)) { + flags.botl = 1; + u.uhp += rnd((int)u.ulevel - 9); + if (u.uhp > u.uhpmax) + u.uhp = u.uhpmax; + } + } else if (Regeneration || + (!(moves % (22 - u.ulevel * 2)))) { + flags.botl = 1; + u.uhp++; + } + } + if (Teleportation && !rn2(85)) + tele(); + if (Searching && multi >= 0) + dosearch(); + gethungry(); + invault(); + amulet(); + } + if (multi < 0) { + if (!++multi) { + pline("%s", nomovemsg ? nomovemsg : + "You can move again."); + nomovemsg = 0; + if (afternmv) + (*afternmv)(); + afternmv = NULL; + } + } + find_ac(); +#ifndef QUEST + if (!flags.mv || Blind) +#endif + { + seeobjs(); + seemons(); + nscr(); + } + if (flags.botl || flags.botlx) + bot(); + + flags.move = 1; + + if (multi >= 0 && occupation) { + if (monster_nearby()) + stop_occupation(); + else if ((*occupation)() == 0) + occupation = NULL; + continue; + } + + if (multi > 0) { +#ifdef QUEST + if (flags.run >= 4) + finddir(); +#endif + lookaround(); + if (!multi) { /* lookaround may clear multi */ + flags.move = 0; + continue; + } + if (flags.mv) { + if (multi < COLNO && !--multi) + flags.mv = flags.run = 0; + domove(); + } else { + --multi; + rhack(save_cm); + } + } else if (multi == 0) { +#ifdef MAIL + ckmailstatus(); +#endif + rhack(NULL); + } + if (multi && multi % 7 == 0) + fflush(stdout); + } +} + +void +glo(int foo) +{ + /* construct the string xlock.n */ + char *tf; + + tf = lock; + while (*tf && *tf != '.') + tf++; + (void)sprintf(tf, ".%d", foo); +} + +/* + * plname is filled either by an option (-u Player or -uPlayer) or + * explicitly (-w implies wizard) or by askname. + * It may still contain a suffix denoting pl_character. + */ +void +askname(void) +{ + int c, ct; + + printf("\nWho are you? "); + fflush(stdout); + ct = 0; + while ((c = getchar()) != '\n') { + if (c == EOF) + error("End of input\n"); + /* some people get confused when their erase char is not ^H */ + if (c == '\010') { + if (ct) + ct--; + continue; + } + if (c != '-') + if (c < 'A' || (c > 'Z' && c < 'a') || c > 'z') + c = '_'; + if (ct < (int)sizeof(plname) - 1) + plname[ct++] = c; + } + plname[ct] = 0; + if (ct == 0) + askname(); +} + +/* VARARGS1 */ +void +impossible(const char *s, ...) +{ + va_list ap; + + va_start(ap, s); + vpline(s, ap); + va_end(ap); + pline("Program in disorder - perhaps you'd better Quit."); +} + +#ifdef CHDIR +static void +chdirx(const char *dir, bool wr) +{ +#ifdef SECURE + if (dir /* User specified directory? */ +#ifdef HACKDIR + && strcmp(dir, HACKDIR) /* and not the default? */ +#endif + ) { + /* revoke */ + setgid(getgid()); + } +#endif + +#ifdef HACKDIR + if (dir == NULL) + dir = HACKDIR; +#endif + + if (dir && chdir(dir) < 0) { + perror(dir); + error("Cannot chdir to %s.", dir); + } + + /* warn the player if he cannot write the record file */ + /* perhaps we should also test whether . is writable */ + /* unfortunately the access systemcall is worthless */ + if (wr) { + int fd; + + if (dir == NULL) + dir = "."; + if ((fd = open(RECORD, O_RDWR)) < 0) { + printf("Warning: cannot write %s/%s", dir, RECORD); + getret(); + } else + close(fd); + } +} +#endif + +void +stop_occupation(void) +{ + if (occupation) { + pline("You stop %s.", occtxt); + occupation = NULL; + } +} diff --git a/hack/hack.makemon.c b/hack/hack.makemon.c new file mode 100644 index 0000000..4c9bb07 --- /dev/null +++ b/hack/hack.makemon.c @@ -0,0 +1,212 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.makemon.c - version 1.0.2 */ +/* $FreeBSD: src/games/hack/hack.makemon.c,v 1.4 1999/11/16 10:26:36 marcel Exp $ */ +/* $DragonFly: src/games/hack/hack.makemon.c,v 1.4 2006/08/21 19:45:32 pavalos Exp $ */ + +#include "hack.h" + +struct monst zeromonst; + +/* + * called with [x,y] = coordinates; + * [0,0] means anyplace + * [u.ux,u.uy] means: call mnexto (if !in_mklev) + * + * In case we make an Orc or killer bee, we make an entire horde (swarm); + * note that in this case we return only one of them (the one at [x,y]). + */ +struct monst * +makemon(struct permonst *ptr, int x, int y) +{ + struct monst *mtmp; + int tmp, ct; + boolean anything = (!ptr); + + if (x != 0 || y != 0) + if (m_at(x, y)) + return (NULL); + if (ptr) { + if (strchr(fut_geno, ptr->mlet)) + return (NULL); + } else { + ct = CMNUM - strlen(fut_geno); + if (strchr(fut_geno, 'm')) /* make only 1 minotaur */ + ct++; + if (strchr(fut_geno, '@')) + ct++; + if (ct <= 0) /* no more monsters! */ + return (0); + tmp = rn2(ct * dlevel / 24 + 7); + if (tmp < dlevel - 4) + tmp = rn2(ct * dlevel / 24 + 12); + if (tmp >= ct) + tmp = rn1(ct - ct / 2, ct / 2); + for (ct = 0; ct < CMNUM; ct++) { + ptr = &mons[ct]; + if (strchr(fut_geno, ptr->mlet)) + continue; + if (!tmp--) + goto gotmon; + } + panic("makemon?"); + } +gotmon: + mtmp = newmonst(ptr->pxlth); + *mtmp = zeromonst; /* clear all entries in structure */ + for (ct = 0; (unsigned)ct < ptr->pxlth; ct++) + ((char *)&(mtmp->mextra[0]))[ct] = 0; + mtmp->nmon = fmon; + fmon = mtmp; + mtmp->m_id = flags.ident++; + mtmp->data = ptr; + mtmp->mxlth = ptr->pxlth; + if (ptr->mlet == 'D') + mtmp->mhpmax = mtmp->mhp = 80; + else if (!ptr->mlevel) + mtmp->mhpmax = mtmp->mhp = rnd(4); + else + mtmp->mhpmax = mtmp->mhp = d(ptr->mlevel, 8); + mtmp->mx = x; + mtmp->my = y; + mtmp->mcansee = 1; + if (ptr->mlet == 'M') { + mtmp->mimic = 1; + mtmp->mappearance = ']'; + } + if (!in_mklev) { + if (x == u.ux && y == u.uy && ptr->mlet != ' ') + mnexto(mtmp); + if (x == 0 && y == 0) + rloc(mtmp); + } + if (ptr->mlet == 's' || ptr->mlet == 'S') { + mtmp->mhide = mtmp->mundetected = 1; + if (in_mklev) + if (mtmp->mx && mtmp->my) + mkobj_at(0, mtmp->mx, mtmp->my); + } + if (ptr->mlet == ':') { + mtmp->cham = 1; + newcham(mtmp, &mons[dlevel + 14 + rn2(CMNUM - 14 - dlevel)]); + } + if (ptr->mlet == 'I' || ptr->mlet == ';') + mtmp->minvis = 1; + if (ptr->mlet == 'L' || ptr->mlet == 'N' + || (in_mklev && strchr("&w;", ptr->mlet) && rn2(5))) + mtmp->msleep = 1; + +#ifndef NOWORM + if (ptr->mlet == 'w' && getwn(mtmp)) + initworm(mtmp); +#endif /* NOWORM */ + + if (anything) + if (ptr->mlet == 'O' || ptr->mlet == 'k') { + coord mm; + int cnt = rnd(10); + mm.x = x; + mm.y = y; + while (cnt--) { + mm = enexto(mm.x, mm.y); + makemon(ptr, mm.x, mm.y); + } + } + + return (mtmp); +} + +coord +enexto(xchar xx, xchar yy) +{ + xchar x, y; + coord foo[15], *tfoo; + int range; + + tfoo = foo; + range = 1; + do { /* full kludge action. */ + for (x = xx - range; x <= xx + range; x++) + if (goodpos(x, yy - range)) { + tfoo->x = x; + tfoo++->y = yy - range; + if (tfoo == &foo[15]) + goto foofull; + } + for (x = xx - range; x <= xx + range; x++) + if (goodpos(x, yy + range)) { + tfoo->x = x; + tfoo++->y = yy + range; + if (tfoo == &foo[15]) + goto foofull; + } + for (y = yy + 1 - range; y < yy + range; y++) + if (goodpos(xx - range, y)) { + tfoo->x = xx - range; + tfoo++->y = y; + if (tfoo == &foo[15]) + goto foofull; + } + for (y = yy + 1 - range; y < yy + range; y++) + if (goodpos(xx + range, y)) { + tfoo->x = xx + range; + tfoo++->y = y; + if (tfoo == &foo[15]) + goto foofull; + } + range++; + } while (tfoo == foo); +foofull: + return (foo[rn2(tfoo - foo)]); +} + +bool +goodpos(int x, int y) /* used only in mnexto and rloc */ +{ + return ( + !(x < 1 || x > COLNO - 2 || y < 1 || y > ROWNO - 2 || + m_at(x, y) || !ACCESSIBLE(levl[x][y].typ) + || (x == u.ux && y == u.uy) + || sobj_at(ENORMOUS_ROCK, x, y) + )); +} + +void +rloc(struct monst *mtmp) +{ + int tx, ty; + char ch = mtmp->data->mlet; + +#ifndef NOWORM + if (ch == 'w' && mtmp->mx) /* do not relocate worms */ + return; +#endif /* NOWORM */ + do { + tx = rn1(COLNO - 3, 2); + ty = rn2(ROWNO); + } while (!goodpos(tx, ty)); + mtmp->mx = tx; + mtmp->my = ty; + if (u.ustuck == mtmp) { + if (u.uswallow) { + u.ux = tx; + u.uy = ty; + docrt(); + } else + u.ustuck = 0; + } + pmon(mtmp); +} + +struct monst * +mkmon_at(char let, int x, int y) +{ + int ct; + struct permonst *ptr; + + for (ct = 0; ct < CMNUM; ct++) { + ptr = &mons[ct]; + if (ptr->mlet == let) + return (makemon(ptr, x, y)); + } + return (0); +} diff --git a/hack/hack.mfndpos.h b/hack/hack.mfndpos.h new file mode 100644 index 0000000..f4da529 --- /dev/null +++ b/hack/hack.mfndpos.h @@ -0,0 +1,12 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.mfndpos.h - version 1.0.2 */ + +#define ALLOW_TRAPS 0777 +#define ALLOW_U 01000 +#define ALLOW_M 02000 +#define ALLOW_TM 04000 +#define ALLOW_ALL (ALLOW_U | ALLOW_M | ALLOW_TM | ALLOW_TRAPS) +#define ALLOW_SSM 010000 +#define ALLOW_ROCK 020000 +#define NOTONL 040000 +#define NOGARLIC 0100000 diff --git a/hack/hack.mhitu.c b/hack/hack.mhitu.c new file mode 100644 index 0000000..b9b410d --- /dev/null +++ b/hack/hack.mhitu.c @@ -0,0 +1,385 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.mhitu.c - version 1.0.3 */ +/* $FreeBSD: src/games/hack/hack.mhitu.c,v 1.4 1999/11/16 10:26:36 marcel Exp $ */ +/* $DragonFly: src/games/hack/hack.mhitu.c,v 1.4 2006/08/21 19:45:32 pavalos Exp $ */ + +#include "hack.h" + +/* + * mhitu: monster hits you + * returns 1 if monster dies (e.g. 'y', 'F'), 0 otherwise + */ +bool +mhitu(struct monst *mtmp) +{ + struct permonst *mdat = mtmp->data; + int tmp, ctmp; + + nomul(0); + + /* If swallowed, can only be affected by hissers and by u.ustuck */ + if (u.uswallow) { + if (mtmp != u.ustuck) { + if (mdat->mlet == 'c' && !rn2(13)) { + pline("Outside, you hear %s's hissing!", + monnam(mtmp)); + pline("%s gets turned to stone!", + Monnam(u.ustuck)); + pline("And the same fate befalls you."); + done_in_by(mtmp); + /* "notreached": not return(1); */ + } + return (0); + } + switch (mdat->mlet) { /* now mtmp == u.ustuck */ + case ',': + youswld(mtmp, (u.uac > 0) ? u.uac + 4 : 4, + 5, "The trapper"); + break; + case '\'': + youswld(mtmp, rnd(6), 7, "The lurker above"); + break; + case 'P': + youswld(mtmp, d(2, 4), 12, "The purple worm"); + break; + default: + /* This is not impossible! */ + pline("The mysterious monster totally digests you."); + u.uhp = 0; + } + if (u.uhp < 1) + done_in_by(mtmp); + return (0); + } + + if (mdat->mlet == 'c' && Stoned) + return (0); + + /* make eels visible the moment they hit/miss us */ + if (mdat->mlet == ';' && mtmp->minvis && cansee(mtmp->mx, mtmp->my)) { + mtmp->minvis = 0; + pmon(mtmp); + } + if (!strchr("1&DuxynNF", mdat->mlet)) + tmp = hitu(mtmp, d(mdat->damn, mdat->damd)); + else + tmp = 0; + if (strchr(UNDEAD, mdat->mlet) && midnight()) + tmp += hitu(mtmp, d(mdat->damn, mdat->damd)); + + ctmp = tmp && !mtmp->mcan && + (!uarm || objects[uarm->otyp].a_can < rnd(3) || !rn2(50)); + switch (mdat->mlet) { + case '1': + if (wiz_hit(mtmp)) /* he disappeared */ + return (1); + break; + case '&': + if (!mtmp->cham && !mtmp->mcan && !rn2(13)) { + makemon(PM_DEMON, u.ux, u.uy); + } else { + hitu(mtmp, d(2, 6)); + hitu(mtmp, d(2, 6)); + hitu(mtmp, rnd(3)); + hitu(mtmp, rnd(3)); + hitu(mtmp, rn1(4, 2)); + } + break; + case ',': + if (tmp) + justswld(mtmp, "The trapper"); + break; + case '\'': + if (tmp) + justswld(mtmp, "The lurker above"); + break; + case ';': + if (ctmp) { + if (!u.ustuck && !rn2(10)) { + pline("%s swings itself around you!", + Monnam(mtmp)); + u.ustuck = mtmp; + } else if (u.ustuck == mtmp && + levl[mtmp->mx][mtmp->my].typ == POOL) { + pline("%s drowns you ...", Monnam(mtmp)); + done("drowned"); + } + } + break; + case 'A': + if (ctmp && rn2(2)) { + if (Poison_resistance) + pline("The sting doesn't seem to affect you."); + else { + pline("You feel weaker!"); + losestr(1); + } + } + break; + case 'C': + hitu(mtmp, rnd(6)); + break; + case 'c': + if (!rn2(5)) { + pline("You hear %s's hissing!", monnam(mtmp)); + if (ctmp || !rn2(20) || (flags.moonphase == NEW_MOON + && !carrying(DEAD_LIZARD))) + Stoned = 5; + } + break; + case 'D': + if (rn2(6) || mtmp->mcan) { + hitu(mtmp, d(3, 10)); + hitu(mtmp, rnd(8)); + hitu(mtmp, rnd(8)); + break; + } + kludge("%s breathes fire!", "The dragon"); + buzz(-1, mtmp->mx, mtmp->my, u.ux - mtmp->mx, u.uy - mtmp->my); + break; + case 'd': + hitu(mtmp, d(2, (flags.moonphase == FULL_MOON) ? 3 : 4)); + break; + case 'e': + hitu(mtmp, d(3, 6)); + break; + case 'F': + if (mtmp->mcan) + break; + kludge("%s explodes!", "The freezing sphere"); + if (Cold_resistance) + pline("You don't seem affected by it."); + else { + xchar dn; + if (17 - (u.ulevel / 2) > rnd(20)) { + pline("You get blasted!"); + dn = 6; + } else { + pline("You duck the blast..."); + dn = 3; + } + losehp_m(d(dn, 6), mtmp); + } + mondead(mtmp); + return (1); + case 'g': + if (ctmp && multi >= 0 && !rn2(3)) { + kludge("You are frozen by %ss juices", "the cube'"); + nomul(-rnd(10)); + } + break; + case 'h': + if (ctmp && multi >= 0 && !rn2(5)) { + nomul(-rnd(10)); + kludge("You are put to sleep by %ss bite!", + "the homunculus'"); + } + break; + case 'j': + tmp = hitu(mtmp, rnd(3)); + tmp &= hitu(mtmp, rnd(3)); + if (tmp) { + hitu(mtmp, rnd(4)); + hitu(mtmp, rnd(4)); + } + break; + case 'k': + if ((hitu(mtmp, rnd(4)) || !rn2(3)) && ctmp) + poisoned("bee's sting", mdat->mname); + break; + case 'L': + if (tmp) + stealgold(mtmp); + break; + case 'N': + if (mtmp->mcan && !Blind) { + pline("%s tries to seduce you, but you seem not interested.", + Amonnam(mtmp, "plain")); + if (rn2(3)) + rloc(mtmp); + } else if (steal(mtmp)) { + rloc(mtmp); + mtmp->mflee = 1; + } + break; + case 'n': + if (!uwep && !uarm && !uarmh && !uarms && !uarmg) { + pline("%s hits! (I hope you don't mind)", + Monnam(mtmp)); + u.uhp += rnd(7); + if (!rn2(7)) + u.uhpmax++; + if (u.uhp > u.uhpmax) + u.uhp = u.uhpmax; + flags.botl = 1; + if (!rn2(50)) + rloc(mtmp); + } else { + hitu(mtmp, d(2, 6)); + hitu(mtmp, d(2, 6)); + } + break; + case 'o': + tmp = hitu(mtmp, rnd(6)); + if (hitu(mtmp, rnd(6)) && tmp && /* hits with both paws */ + !u.ustuck && rn2(2)) { + u.ustuck = mtmp; + kludge("%s has grabbed you!", "The owlbear"); + u.uhp -= d(2, 8); + } else if (u.ustuck == mtmp) { + u.uhp -= d(2, 8); + pline("You are being crushed."); + } + break; + case 'P': + if (ctmp && !rn2(4)) + justswld(mtmp, "The purple worm"); + else + hitu(mtmp, d(2, 4)); + break; + case 'Q': + hitu(mtmp, rnd(2)); + hitu(mtmp, rnd(2)); + break; + case 'R': + if (tmp && uarmh && !uarmh->rustfree && + (int)uarmh->spe >= -1) { + pline("Your helmet rusts!"); + uarmh->spe--; + } else if (ctmp && uarm && !uarm->rustfree && /* Mike Newton */ + uarm->otyp < STUDDED_LEATHER_ARMOR && + (int)uarm->spe >= -1) { + pline("Your armor rusts!"); + uarm->spe--; + } + break; + case 'S': + if (ctmp && !rn2(8)) + poisoned("snake's bite", mdat->mname); + break; + case 's': + if (tmp && !rn2(8)) + poisoned("scorpion's sting", mdat->mname); + hitu(mtmp, rnd(8)); + hitu(mtmp, rnd(8)); + break; + case 'T': + hitu(mtmp, rnd(6)); + hitu(mtmp, rnd(6)); + break; + case 't': + if (!rn2(5)) + rloc(mtmp); + break; + case 'u': + mtmp->mflee = 1; + break; + case 'U': + hitu(mtmp, d(3, 4)); + hitu(mtmp, d(3, 4)); + break; + case 'v': + if (ctmp && !u.ustuck) + u.ustuck = mtmp; + break; + case 'V': + if (tmp) + u.uhp -= 4; + if (ctmp) + losexp(); + break; + case 'W': + if (ctmp) + losexp(); + break; +#ifndef NOWORM + case 'w': + if (tmp) + wormhit(mtmp); +#endif /* NOWORM */ + break; + case 'X': + hitu(mtmp, rnd(5)); + hitu(mtmp, rnd(5)); + hitu(mtmp, rnd(5)); + break; + case 'x': + { + long side = rn2(2) ? RIGHT_SIDE : LEFT_SIDE; + pline("%s pricks in your %s leg!", + Monnam(mtmp), (side == RIGHT_SIDE) ? "right" : "left"); + set_wounded_legs(side, rnd(50)); + losehp_m(2, mtmp); + break; + } + case 'y': + if (mtmp->mcan) + break; + mondead(mtmp); + if (!Blind) { + pline("You are blinded by a blast of light!"); + Blind = d(4, 12); + seeoff(0); + } + return (1); + case 'Y': + hitu(mtmp, rnd(6)); + break; + } + if (u.uhp < 1) + done_in_by(mtmp); + return (0); +} + +bool +hitu(struct monst *mtmp, int dam) +{ + bool res; + int tmp; + + nomul(0); + if (u.uswallow) + return (0); + + if (mtmp->mhide && mtmp->mundetected) { + mtmp->mundetected = 0; + if (!Blind) { + struct obj *obj; + if ((obj = o_at(mtmp->mx, mtmp->my)) != NULL) + pline("%s was hidden under %s!", + Xmonnam(mtmp), doname(obj)); + } + } + + tmp = u.uac; + /* give people with Ac = -10 at least some vulnerability */ + if (tmp < 0) { + dam += tmp; /* decrease damage */ + if (dam <= 0) + dam = 1; + tmp = -rn2(-tmp); + } + tmp += mtmp->data->mlevel; + if (multi < 0) + tmp += 4; + if ((Invis && mtmp->data->mlet != 'I') || !mtmp->mcansee) + tmp -= 2; + if (mtmp->mtrapped) + tmp -= 2; + if (tmp <= rnd(20)) { + if (Blind) + pline("It misses."); + else + pline("%s misses.", Monnam(mtmp)); + res = 0; + } else { + if (Blind) + pline("It hits!"); + else + pline("%s hits!", Monnam(mtmp)); + losehp_m(dam, mtmp); + res = 1; + } + stop_occupation(); + return (res); +} diff --git a/hack/hack.mklev.c b/hack/hack.mklev.c new file mode 100644 index 0000000..ad51f4b --- /dev/null +++ b/hack/hack.mklev.c @@ -0,0 +1,795 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.mklev.c - version 1.0.3 */ +/* $FreeBSD: src/games/hack/hack.mklev.c,v 1.6 1999/11/16 10:26:36 marcel Exp $ */ +/* $DragonFly: src/games/hack/hack.mklev.c,v 1.4 2006/08/21 19:45:32 pavalos Exp $ */ + +#include "hack.h" + +#define somex() ((random()%(croom->hx-croom->lx+1))+croom->lx) +#define somey() ((random()%(croom->hy-croom->ly+1))+croom->ly) + +#define XLIM 4 /* define minimum required space around a room */ +#define YLIM 3 +boolean secret; /* TRUE while making a vault: increase [XY]LIM */ +struct mkroom rooms[MAXNROFROOMS+1]; +int smeq[MAXNROFROOMS+1]; +coord doors[DOORMAX]; +int doorindex; +struct rm zerorm; +schar nxcor; +boolean goldseen; +int nroom; +xchar xdnstair, xupstair, ydnstair, yupstair; + +/* Definitions used by makerooms() and addrs() */ +#define MAXRS 50 /* max lth of temp rectangle table - arbitrary */ +struct rectangle { + xchar rlx, rly, rhx, rhy; +} rs[MAXRS + 1]; +int rscnt, rsmax; /* 0..rscnt-1: currently under consideration */ + /* rscnt..rsmax: discarded */ + +static bool makerooms(void); +static void addrs(int, int, int, int); +static void addrsx(int, int, int, int, bool); +static int comp(const void *, const void *); +static coord finddpos(int, int, int, int); +static bool okdoor(int, int); +static void dodoor(int, int, struct mkroom *); +static void dosdoor(int, int, struct mkroom *, int); +static bool maker(schar, schar, schar, schar); +static void makecorridors(void); +static void join(int, int); +static void make_niches(void); +static void makevtele(void); +static void makeniche(bool); + +void +makelevel(void) +{ + struct mkroom *croom, *troom; + unsigned tryct; + int x, y; + + nroom = 0; + doorindex = 0; + rooms[0].hx = -1; /* in case we are in a maze */ + + for (x = 0; x < COLNO; x++) + for (y = 0; y < ROWNO; y++) + levl[x][y] = zerorm; + + oinit(); /* assign level dependent obj probabilities */ + + if (dlevel >= rn1(3, 26)) { /* there might be several mazes */ + makemaz(); + return; + } + + /* construct the rooms */ + nroom = 0; + secret = FALSE; + makerooms(); + + /* construct stairs (up and down in different rooms if possible) */ + croom = &rooms[rn2(nroom)]; + xdnstair = somex(); + ydnstair = somey(); + levl[xdnstair][ydnstair].scrsym = '>'; + levl[xdnstair][ydnstair].typ = STAIRS; + if (nroom > 1) { + troom = croom; + croom = &rooms[rn2(nroom - 1)]; + if (croom >= troom) + croom++; + } + xupstair = somex(); /* %% < and > might be in the same place */ + yupstair = somey(); + levl[xupstair][yupstair].scrsym = '<'; + levl[xupstair][yupstair].typ = STAIRS; + + /* for each room: put things inside */ + for (croom = rooms; croom->hx > 0; croom++) { + /* put a sleeping monster inside */ + /* + * Note: monster may be on the stairs. This cannot be + * avoided: maybe the player fell through a trapdoor while a + * monster was on the stairs. Conclusion: we have to check + * for monsters on the stairs anyway. + */ + if (!rn2(3)) + makemon(NULL, somex(), somey()); + + /* put traps and mimics inside */ + goldseen = FALSE; + while (!rn2(8 - (dlevel / 6))) + mktrap(0, 0, croom); + if (!goldseen && !rn2(3)) + mkgold(0L, somex(), somey()); + if (!rn2(3)) { + mkobj_at(0, somex(), somey()); + tryct = 0; + while (!rn2(5)) { + if (++tryct > 100) { + printf("tryct overflow4\n"); + break; + } + mkobj_at(0, somex(), somey()); + } + } + } + + qsort((char *)rooms, nroom, sizeof(struct mkroom), comp); + makecorridors(); + make_niches(); + + /* make a secret treasure vault, not connected to the rest */ + if (nroom <= (2 * MAXNROFROOMS / 3)) + if (rn2(3)) { + troom = &rooms[nroom]; + secret = TRUE; + if (makerooms()) { + troom->rtype = VAULT; /* treasure vault */ + for (x = troom->lx; x <= troom->hx; x++) + for (y = troom->ly; y <= troom->hy; y++) + mkgold((long)(rnd(dlevel * + 100) + 50), x, y); + if (!rn2(3)) + makevtele(); + } + } + +#ifndef QUEST +#ifdef WIZARD + if (wizard && getenv("SHOPTYPE")) + mkshop(); + else +#endif /* WIZARD */ + if (dlevel > 1 && dlevel < 20 && rn2(dlevel) < 3) + mkshop(); + else if (dlevel > 6 && !rn2(7)) + mkzoo(ZOO); + else if (dlevel > 9 && !rn2(5)) + mkzoo(BEEHIVE); + else if (dlevel > 11 && !rn2(6)) + mkzoo(MORGUE); + else if (dlevel > 18 && !rn2(6)) + mkswamp(); +#endif /* QUEST */ +} + +static bool +makerooms(void) +{ + struct rectangle *rsp; + int lx, ly, hx, hy, lowx, lowy, hix, hiy, dx, dy; + int tryct = 0, xlim, ylim; + + /* init */ + xlim = XLIM + secret; + ylim = YLIM + secret; + if (nroom == 0) { + rsp = rs; + rsp->rlx = rsp->rly = 0; + rsp->rhx = COLNO - 1; + rsp->rhy = ROWNO - 1; + rsmax = 1; + } + rscnt = rsmax; + + /* make rooms until satisfied */ + while (rscnt > 0 && nroom < MAXNROFROOMS - 1) { + if (!secret && nroom > (MAXNROFROOMS / 3) && + !rn2((MAXNROFROOMS - nroom) * (MAXNROFROOMS - nroom))) + return (0); + + /* pick a rectangle */ + rsp = &rs[rn2(rscnt)]; + hx = rsp->rhx; + hy = rsp->rhy; + lx = rsp->rlx; + ly = rsp->rly; + + /* find size of room */ + if (secret) + dx = dy = 1; + else { + dx = 2 + rn2((hx - lx - 8 > 20) ? 12 : 8); + dy = 2 + rn2(4); + if (dx * dy > 50) + dy = 50 / dx; + } + + /* look whether our room will fit */ + if (hx - lx < dx + dx / 2 + 2 * xlim || hy - ly < dy + dy / 3 + 2 * ylim) { + /* no, too small */ + /* maybe we throw this area out */ + if (secret || !rn2(MAXNROFROOMS + 1 - nroom - tryct)) { + rscnt--; + rs[rsmax] = *rsp; + *rsp = rs[rscnt]; + rs[rscnt] = rs[rsmax]; + tryct = 0; + } else + tryct++; + continue; + } + + lowx = lx + xlim + rn2(hx - lx - dx - 2 * xlim + 1); + lowy = ly + ylim + rn2(hy - ly - dy - 2 * ylim + 1); + hix = lowx + dx; + hiy = lowy + dy; + + if (maker(lowx, dx, lowy, dy)) { + if (secret) + return (1); + addrs(lowx - 1, lowy - 1, hix + 1, hiy + 1); + tryct = 0; + } else if (tryct++ > 100) + break; + } + return (0); /* failed to make vault - very strange */ +} + +static void +addrs(int lowx, int lowy, int hix, int hiy) +{ + struct rectangle *rsp; + int lx, ly, hx, hy, xlim, ylim; + boolean discarded; + + xlim = XLIM + secret; + ylim = YLIM + secret; + + /* walk down since rscnt and rsmax change */ + for (rsp = &rs[rsmax - 1]; rsp >= rs; rsp--) { + if ((lx = rsp->rlx) > hix || (ly = rsp->rly) > hiy || + (hx = rsp->rhx) < lowx || (hy = rsp->rhy) < lowy) + continue; + if ((discarded = (rsp >= &rs[rscnt]))) { + *rsp = rs[--rsmax]; + } else { + rsmax--; + rscnt--; + *rsp = rs[rscnt]; + if (rscnt != rsmax) + rs[rscnt] = rs[rsmax]; + } + if (lowy - ly > 2 * ylim + 4) + addrsx(lx, ly, hx, lowy - 2, discarded); + if (lowx - lx > 2 * xlim + 4) + addrsx(lx, ly, lowx - 2, hy, discarded); + if (hy - hiy > 2 * ylim + 4) + addrsx(lx, hiy + 2, hx, hy, discarded); + if (hx - hix > 2 * xlim + 4) + addrsx(hix + 2, ly, hx, hy, discarded); + } +} + +/* discarded: piece of a discarded area */ +static void +addrsx(int lx, int ly, int hx, int hy, bool discarded) +{ + struct rectangle *rsp; + + /* check inclusions */ + for (rsp = rs; rsp < &rs[rsmax]; rsp++) { + if (lx >= rsp->rlx && hx <= rsp->rhx && + ly >= rsp->rly && hy <= rsp->rhy) + return; + } + + /* make a new entry */ + if (rsmax >= MAXRS) { +#ifdef WIZARD + if (wizard) + pline("MAXRS may be too small."); +#endif /* WIZARD */ + return; + } + rsmax++; + if (!discarded) { + *rsp = rs[rscnt]; + rsp = &rs[rscnt]; + rscnt++; + } + rsp->rlx = lx; + rsp->rly = ly; + rsp->rhx = hx; + rsp->rhy = hy; +} + +static int +comp(const void *vx, const void *vy) +{ + const struct mkroom *x, *y; + + x = vx; + y = vy; + if (x->lx < y->lx) + return (-1); + return (x->lx > y->lx); +} + +static coord +finddpos(int xl, int yl, int xh, int yh) +{ + coord ff; + int x, y; + + x = (xl == xh) ? xl : (xl + rn2(xh - xl + 1)); + y = (yl == yh) ? yl : (yl + rn2(yh - yl + 1)); + if (okdoor(x, y)) + goto gotit; + + for (x = xl; x <= xh; x++) + for (y = yl; y <= yh; y++) + if (okdoor(x, y)) + goto gotit; + + for (x = xl; x <= xh; x++) + for (y = yl; y <= yh; y++) + if (levl[x][y].typ == DOOR || levl[x][y].typ == SDOOR) + goto gotit; + /* cannot find something reasonable -- strange */ + x = xl; + y = yh; +gotit: + ff.x = x; + ff.y = y; + return (ff); +} + +/* see whether it is allowable to create a door at [x,y] */ +static bool +okdoor(int x, int y) +{ + if (levl[x - 1][y].typ == DOOR || levl[x + 1][y].typ == DOOR || + levl[x][y + 1].typ == DOOR || levl[x][y - 1].typ == DOOR || + levl[x - 1][y].typ == SDOOR || levl[x + 1][y].typ == SDOOR || + levl[x][y - 1].typ == SDOOR || levl[x][y + 1].typ == SDOOR || + (levl[x][y].typ != HWALL && levl[x][y].typ != VWALL) || + doorindex >= DOORMAX) + return (0); + return (1); +} + +static void +dodoor(int x, int y, struct mkroom *aroom) +{ + if (doorindex >= DOORMAX) { + impossible("DOORMAX exceeded?"); + return; + } + if (!okdoor(x, y) && nxcor) + return; + dosdoor(x, y, aroom, rn2(8) ? DOOR : SDOOR); +} + +static void +dosdoor(int x, int y, struct mkroom *aroom, int type) +{ + struct mkroom *broom; + int tmp; + + if (!IS_WALL(levl[x][y].typ)) /* avoid SDOORs with '+' as scrsym */ + type = DOOR; + levl[x][y].typ = type; + if (type == DOOR) + levl[x][y].scrsym = '+'; + aroom->doorct++; + broom = aroom + 1; + if (broom->hx < 0) + tmp = doorindex; + else + for (tmp = doorindex; tmp > broom->fdoor; tmp--) + doors[tmp] = doors[tmp - 1]; + doorindex++; + doors[tmp].x = x; + doors[tmp].y = y; + for (; broom->hx >= 0; broom++) + broom->fdoor++; +} + +/* Only called from makerooms() */ +static bool +maker(schar lowx, schar ddx, schar lowy, schar ddy) +{ + struct mkroom *croom; + int x, y, hix = lowx + ddx, hiy = lowy + ddy; + int xlim = XLIM + secret, ylim = YLIM + secret; + + if (nroom >= MAXNROFROOMS) + return (0); + if (lowx < XLIM) + lowx = XLIM; + if (lowy < YLIM) + lowy = YLIM; + if (hix > COLNO - XLIM - 1) + hix = COLNO - XLIM - 1; + if (hiy > ROWNO - YLIM - 1) + hiy = ROWNO - YLIM - 1; +chk: + if (hix <= lowx || hiy <= lowy) + return (0); + + /* check area around room (and make room smaller if necessary) */ + for (x = lowx - xlim; x <= hix + xlim; x++) { + for (y = lowy - ylim; y <= hiy + ylim; y++) { + if (levl[x][y].typ) { +#ifdef WIZARD + if (wizard && !secret) + pline("Strange area [%d,%d] in maker().", x, y); +#endif /* WIZARD */ + if (!rn2(3)) + return (0); + if (x < lowx) + lowx = x + xlim + 1; + else + hix = x - xlim - 1; + if (y < lowy) + lowy = y + ylim + 1; + else + hiy = y - ylim - 1; + goto chk; + } + } + } + + croom = &rooms[nroom]; + + /* on low levels the room is lit (usually) */ + /* secret vaults are always lit */ + if ((rnd(dlevel) < 10 && rn2(77)) || (ddx == 1 && ddy == 1)) { + for (x = lowx - 1; x <= hix + 1; x++) + for (y = lowy - 1; y <= hiy + 1; y++) + levl[x][y].lit = 1; + croom->rlit = 1; + } else + croom->rlit = 0; + croom->lx = lowx; + croom->hx = hix; + croom->ly = lowy; + croom->hy = hiy; + croom->rtype = croom->doorct = croom->fdoor = 0; + + for (x = lowx - 1; x <= hix + 1; x++) + for (y = lowy - 1; y <= hiy + 1; y += (hiy - lowy + 2)) { + levl[x][y].scrsym = '-'; + levl[x][y].typ = HWALL; + } + for (x = lowx - 1; x <= hix + 1; x += (hix - lowx + 2)) + for (y = lowy; y <= hiy; y++) { + levl[x][y].scrsym = '|'; + levl[x][y].typ = VWALL; + } + for (x = lowx; x <= hix; x++) + for (y = lowy; y <= hiy; y++) { + levl[x][y].scrsym = '.'; + levl[x][y].typ = ROOM; + } + + smeq[nroom] = nroom; + croom++; + croom->hx = -1; + nroom++; + return (1); +} + +static void +makecorridors(void) +{ + int a, b; + + nxcor = 0; + for (a = 0; a < nroom - 1; a++) + join(a, a + 1); + for (a = 0; a < nroom - 2; a++) + if (smeq[a] != smeq[a + 2]) + join(a, a + 2); + for (a = 0; a < nroom; a++) + for (b = 0; b < nroom; b++) + if (smeq[a] != smeq[b]) + join(a, b); + if (nroom > 2) + for (nxcor = rn2(nroom) + 4; nxcor; nxcor--) { + a = rn2(nroom); + b = rn2(nroom - 2); + if (b >= a) + b += 2; + join(a, b); + } +} + +static void +join(int a, int b) +{ + coord cc, tt; + int tx, ty, xx, yy; + struct rm *crm; + struct mkroom *croom, *troom; + int dx, dy, dix, diy, cct; + + croom = &rooms[a]; + troom = &rooms[b]; + + /* + * find positions cc and tt for doors in croom and troom and + * direction for a corridor between them + */ + + if (troom->hx < 0 || croom->hx < 0 || doorindex >= DOORMAX) + return; + if (troom->lx > croom->hx) { + dx = 1; + dy = 0; + xx = croom->hx + 1; + tx = troom->lx - 1; + cc = finddpos(xx, croom->ly, xx, croom->hy); + tt = finddpos(tx, troom->ly, tx, troom->hy); + } else if (troom->hy < croom->ly) { + dy = -1; + dx = 0; + yy = croom->ly - 1; + cc = finddpos(croom->lx, yy, croom->hx, yy); + ty = troom->hy + 1; + tt = finddpos(troom->lx, ty, troom->hx, ty); + } else if (troom->hx < croom->lx) { + dx = -1; + dy = 0; + xx = croom->lx - 1; + tx = troom->hx + 1; + cc = finddpos(xx, croom->ly, xx, croom->hy); + tt = finddpos(tx, troom->ly, tx, troom->hy); + } else { + dy = 1; + dx = 0; + yy = croom->hy + 1; + ty = troom->ly - 1; + cc = finddpos(croom->lx, yy, croom->hx, yy); + tt = finddpos(troom->lx, ty, troom->hx, ty); + } + xx = cc.x; + yy = cc.y; + tx = tt.x - dx; + ty = tt.y - dy; + if (nxcor && levl[xx + dx][yy + dy].typ) + return; + dodoor(xx, yy, croom); + + cct = 0; + while (xx != tx || yy != ty) { + xx += dx; + yy += dy; + + /* loop: dig corridor at [xx,yy] and find new [xx,yy] */ + if (cct++ > 500 || (nxcor && !rn2(35))) + return; + + if (xx == COLNO - 1 || xx == 0 || yy == 0 || yy == ROWNO - 1) + return; /* impossible */ + + crm = &levl[xx][yy]; + if (!(crm->typ)) { + if (rn2(100)) { + crm->typ = CORR; + crm->scrsym = CORR_SYM; + if (nxcor && !rn2(50)) + mkobj_at(ROCK_SYM, xx, yy); + } else { + crm->typ = SCORR; + crm->scrsym = ' '; + } + } else if (crm->typ != CORR && crm->typ != SCORR) { + /* strange ... */ + return; + } + /* find next corridor position */ + dix = abs(xx - tx); + diy = abs(yy - ty); + + /* do we have to change direction ? */ + if (dy && dix > diy) { + int ddx = (xx > tx) ? -1 : 1; + + crm = &levl[xx + ddx][yy]; + if (!crm->typ || crm->typ == CORR || crm->typ == SCORR) { + dx = ddx; + dy = 0; + continue; + } + } else if (dx && diy > dix) { + int ddy = (yy > ty) ? -1 : 1; + + crm = &levl[xx][yy + ddy]; + if (!crm->typ || crm->typ == CORR || crm->typ == SCORR) { + dy = ddy; + dx = 0; + continue; + } + } + + /* continue straight on? */ + crm = &levl[xx + dx][yy + dy]; + if (!crm->typ || crm->typ == CORR || crm->typ == SCORR) + continue; + + /* no, what must we do now?? */ + if (dx) { + dx = 0; + dy = (ty < yy) ? -1 : 1; + crm = &levl[xx + dx][yy + dy]; + if (!crm->typ || crm->typ == CORR || crm->typ == SCORR) + continue; + dy = -dy; + continue; + } else { + dy = 0; + dx = (tx < xx) ? -1 : 1; + crm = &levl[xx + dx][yy + dy]; + if (!crm->typ || crm->typ == CORR || crm->typ == SCORR) + continue; + dx = -dx; + continue; + } + } + + /* we succeeded in digging the corridor */ + dodoor(tt.x, tt.y, troom); + + if (smeq[a] < smeq[b]) + smeq[b] = smeq[a]; + else + smeq[a] = smeq[b]; +} + +static void +make_niches(void) +{ + int ct = rnd(nroom / 2 + 1); + while (ct--) + makeniche(FALSE); +} + +static void +makevtele(void) +{ + makeniche(TRUE); +} + +static void +makeniche(bool with_trap) +{ + struct mkroom *aroom; + struct rm *rm; + int vct = 8; + coord dd; + int dy, xx, yy; + struct trap *ttmp; + + if (doorindex < DOORMAX) + while (vct--) { + aroom = &rooms[rn2(nroom - 1)]; + if (aroom->rtype != 0) /* not an ordinary room */ + continue; + if (aroom->doorct == 1 && rn2(5)) + continue; + if (rn2(2)) { + dy = 1; + dd = finddpos(aroom->lx, aroom->hy + 1, + aroom->hx, + aroom->hy + 1); + } else { + dy = -1; + dd = finddpos(aroom->lx, aroom->ly - 1, + aroom->hx, + aroom->ly - 1); + } + xx = dd.x; + yy = dd.y; + if ((rm = &levl[xx][yy + dy])->typ) + continue; + if (with_trap || !rn2(4)) { + rm->typ = SCORR; + rm->scrsym = ' '; + if (with_trap) { + ttmp = maketrap(xx, yy + dy, TELEP_TRAP); + ttmp->once = 1; + make_engr_at(xx, yy - dy, "ad ae?ar um"); + } + dosdoor(xx, yy, aroom, SDOOR); + } else { + rm->typ = CORR; + rm->scrsym = CORR_SYM; + if (rn2(7)) + dosdoor(xx, yy, aroom, rn2(5) ? SDOOR : DOOR); + else { + mksobj_at(SCR_TELEPORTATION, xx, yy + dy); + if (!rn2(3)) + mkobj_at(0, xx, yy + dy); + } + } + return; + } +} + +/* make a trap somewhere (in croom if mazeflag = 0) */ +void +mktrap(int num, int mazeflag, struct mkroom *croom) +{ + struct trap *ttmp; + int kind, nopierc, nomimic, fakedoor, fakegold, tryct = 0; + xchar mx, my; + + if (!num || num >= TRAPNUM) { + nopierc = (dlevel < 4) ? 1 : 0; + nomimic = (dlevel < 9 || goldseen) ? 1 : 0; + if (strchr(fut_geno, 'M')) + nomimic = 1; + kind = rn2(TRAPNUM - nopierc - nomimic); + /* note: PIERC = 7, MIMIC = 8, TRAPNUM = 9 */ + } else + kind = num; + + if (kind == MIMIC) { + struct monst *mtmp; + + fakedoor = (!rn2(3) && !mazeflag); + fakegold = (!fakedoor && !rn2(2)); + if (fakegold) + goldseen = TRUE; + do { + if (++tryct > 200) + return; + if (fakedoor) { + /* note: fakedoor maybe on actual door */ + if (rn2(2)) { + if (rn2(2)) + mx = croom->hx + 1; + else + mx = croom->lx - 1; + my = somey(); + } else { + if (rn2(2)) + my = croom->hy + 1; + else + my = croom->ly - 1; + mx = somex(); + } + } else if (mazeflag) { + coord mm; + mm = mazexy(); + mx = mm.x; + my = mm.y; + } else { + mx = somex(); + my = somey(); + } + } while (m_at(mx, my) || levl[mx][my].typ == STAIRS); + if ((mtmp = makemon(PM_MIMIC, mx, my)) != NULL) { + mtmp->mimic = 1; + mtmp->mappearance = + fakegold ? '$' : fakedoor ? '+' : + (mazeflag && rn2(2)) ? AMULET_SYM : + "=/)%?![<>"[rn2(9)]; + } + return; + } + + do { + if (++tryct > 200) + return; + if (mazeflag) { + coord mm; + mm = mazexy(); + mx = mm.x; + my = mm.y; + } else { + mx = somex(); + my = somey(); + } + } while (t_at(mx, my) || levl[mx][my].typ == STAIRS); + ttmp = maketrap(mx, my, kind); + if (mazeflag && !rn2(10) && ttmp->ttyp < PIERC) + ttmp->tseen = 1; +} diff --git a/hack/hack.mkmaze.c b/hack/hack.mkmaze.c new file mode 100644 index 0000000..29800c0 --- /dev/null +++ b/hack/hack.mkmaze.c @@ -0,0 +1,157 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.mkmaze.c - version 1.0.2 */ +/* $FreeBSD: src/games/hack/hack.mkmaze.c,v 1.4 1999/11/16 10:26:37 marcel Exp $ */ +/* $DragonFly: src/games/hack/hack.mkmaze.c,v 1.3 2006/08/21 19:45:32 pavalos Exp $ */ + +#include "hack.h" +extern struct permonst pm_wizard; +struct permonst hell_hound = + { "hell hound", 'd', 12, 14, 2, 3, 6, 0 }; + +static void walkfrom(int, int); +static void move(int *, int *, int); +static bool okay(int, int, int); + +void +makemaz(void) +{ + int x, y; + int zx, zy; + coord mm; + boolean al = (dlevel >= 30 && !flags.made_amulet); + + for (x = 2; x < COLNO - 1; x++) + for (y = 2; y < ROWNO - 1; y++) + levl[x][y].typ = (x % 2 && y % 2) ? 0 : HWALL; + if (al) { + struct monst *mtmp; + + zx = 2 * (COLNO / 4) - 1; + zy = 2 * (ROWNO / 4) - 1; + for (x = zx - 2; x < zx + 4; x++) + for (y = zy - 2; y <= zy + 2; y++) { + levl[x][y].typ = + (y == zy - 2 || y == zy + 2 || x == + zx - 2 || x == zx + 3) ? POOL : + (y == zy - 1 || y == zy + 1 || x == + zx - 1 || x == zx + 2) ? HWALL : + ROOM; + } + + mkobj_at(AMULET_SYM, zx, zy); + flags.made_amulet = 1; + walkfrom(zx + 4, zy); + if ((mtmp = makemon(&hell_hound, zx, zy)) != NULL) + mtmp->msleep = 1; + if ((mtmp = makemon(PM_WIZARD, zx + 1, zy)) != NULL) { + mtmp->msleep = 1; + flags.no_of_wizards = 1; + } + } else { + mm = mazexy(); + zx = mm.x; + zy = mm.y; + walkfrom(zx, zy); + mksobj_at(WAN_WISHING, zx, zy); + mkobj_at(ROCK_SYM, zx, zy); /* put a rock on top of it */ + } + + for (x = 2; x < COLNO - 1; x++) + for (y = 2; y < ROWNO - 1; y++) { + switch (levl[x][y].typ) { + case HWALL: + levl[x][y].scrsym = '-'; + break; + case ROOM: + levl[x][y].scrsym = '.'; + break; + } + } + for (x = rn1(8, 11); x; x--) { + mm = mazexy(); + mkobj_at(rn2(2) ? GEM_SYM : 0, mm.x, mm.y); + } + for (x = rn1(10, 2); x; x--) { + mm = mazexy(); + mkobj_at(ROCK_SYM, mm.x, mm.y); + } + mm = mazexy(); + makemon(PM_MINOTAUR, mm.x, mm.y); + for (x = rn1(5, 7); x; x--) { + mm = mazexy(); + makemon(NULL, mm.x, mm.y); + } + for (x = rn1(6, 7); x; x--) { + mm = mazexy(); + mkgold(0L, mm.x, mm.y); + } + for (x = rn1(6, 7); x; x--) + mktrap(0, 1, NULL); + mm = mazexy(); + levl[(xupstair = mm.x)][(yupstair = mm.y)].scrsym = '<'; + levl[xupstair][yupstair].typ = STAIRS; + xdnstair = ydnstair = 0; +} + +static void +walkfrom(int x, int y) +{ + int q, a, dir; + int dirs[4]; + + levl[x][y].typ = ROOM; + for (;;) { + q = 0; + for (a = 0; a < 4; a++) + if (okay(x, y, a)) + dirs[q++] = a; + if (!q) + return; + dir = dirs[rn2(q)]; + move(&x, &y, dir); + levl[x][y].typ = ROOM; + move(&x, &y, dir); + walkfrom(x, y); + } +} + +static void +move(int *x, int *y, int dir) +{ + switch (dir) { + case 0: + --(*y); + break; + case 1: + (*x)++; + break; + case 2: + (*y)++; + break; + case 3: + --(*x); + break; + } +} + +static bool +okay(int x, int y, int dir) +{ + move(&x, &y, dir); + move(&x, &y, dir); + if (x < 3 || y < 3 || x > COLNO - 3 || y > ROWNO - 3 || + levl[x][y].typ != 0) + return (0); + else + return (1); +} + +coord +mazexy(void) +{ + coord mm; + + mm.x = 3 + 2 * rn2(COLNO / 2 - 2); + mm.y = 3 + 2 * rn2(ROWNO / 2 - 2); + return (mm); +} diff --git a/hack/hack.mkobj.c b/hack/hack.mkobj.c new file mode 100644 index 0000000..cf44d1a --- /dev/null +++ b/hack/hack.mkobj.c @@ -0,0 +1,160 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.mkobj.c - version 1.0.3 */ +/* $FreeBSD: src/games/hack/hack.mkobj.c,v 1.5 1999/11/16 10:26:37 marcel Exp $ */ +/* $DragonFly: src/games/hack/hack.mkobj.c,v 1.4 2006/08/21 19:45:32 pavalos Exp $ */ + +#include "hack.h" + +char mkobjstr[] = "))[[!!!!????%%%%/=**))[[!!!!????%%%%/=**(%"; + +struct obj * +mkobj_at(int let, int x, int y) +{ + struct obj *otmp = mkobj(let); + + otmp->ox = x; + otmp->oy = y; + otmp->nobj = fobj; + fobj = otmp; + return (otmp); +} + +void +mksobj_at(int otyp, int x, int y) +{ + struct obj *otmp = mksobj(otyp); + + otmp->ox = x; + otmp->oy = y; + otmp->nobj = fobj; + fobj = otmp; +} + +struct obj * +mkobj(int let) +{ + if (!let) + let = mkobjstr[rn2(sizeof(mkobjstr) - 1)]; + return ( + mksobj( + letter(let) ? + CORPSE + + ((let > 'Z') ? (let - 'a' + 'Z' - '@' + + 1) : (let - '@')) + : probtype(let) + ) + ); +} + +struct obj zeroobj; + +struct obj * +mksobj(int otyp) +{ + struct obj *otmp; + char let = objects[otyp].oc_olet; + + otmp = newobj(0); + *otmp = zeroobj; + otmp->age = moves; + otmp->o_id = flags.ident++; + otmp->quan = 1; + otmp->olet = let; + otmp->otyp = otyp; + otmp->dknown = strchr("/=!?*", let) ? 0 : 1; + switch (let) { + case WEAPON_SYM: + otmp->quan = (otmp->otyp <= ROCK) ? rn1(6, 6) : 1; + if (!rn2(11)) + otmp->spe = rnd(3); + else if (!rn2(10)) { + otmp->cursed = 1; + otmp->spe = -rnd(3); + } + break; + case FOOD_SYM: + if (otmp->otyp >= CORPSE) + break; +#ifdef NOT_YET_IMPLEMENTED + /* if tins are to be identified, need to adapt doname() etc */ + if (otmp->otyp == TIN) + otmp->spe = rnd(...); +#endif /* NOT_YET_IMPLEMENTED */ + /* fall into next case */ + case GEM_SYM: + otmp->quan = rn2(6) ? 1 : 2; + case TOOL_SYM: + case CHAIN_SYM: + case BALL_SYM: + case ROCK_SYM: + case POTION_SYM: + case SCROLL_SYM: + case AMULET_SYM: + break; + case ARMOR_SYM: + if (!rn2(8)) + otmp->cursed = 1; + if (!rn2(10)) + otmp->spe = rnd(3); + else if (!rn2(9)) { + otmp->spe = -rnd(3); + otmp->cursed = 1; + } + break; + case WAND_SYM: + if (otmp->otyp == WAN_WISHING) + otmp->spe = 3; + else + otmp->spe = rn1(5, + (objects[otmp->otyp].bits & NODIR) ? 11 : 4); + break; + case RING_SYM: + if (objects[otmp->otyp].bits & SPEC) { + if (!rn2(3)) { + otmp->cursed = 1; + otmp->spe = -rnd(2); + } else + otmp->spe = rnd(2); + } else if (otmp->otyp == RIN_TELEPORTATION || + otmp->otyp == RIN_AGGRAVATE_MONSTER || + otmp->otyp == RIN_HUNGER || !rn2(9)) + otmp->cursed = 1; + break; + default: + panic("impossible mkobj"); + } + otmp->owt = weight(otmp); + return (otmp); +} + +bool +letter(char c) +{ + return (('@' <= c && c <= 'Z') || ('a' <= c && c <= 'z')); +} + +int +weight(struct obj *obj) +{ + int wt = objects[obj->otyp].oc_weight; + return (wt ? wt * obj->quan : (obj->quan + 1) / 2); +} + +void +mkgold(long num, int x, int y) +{ + struct gold *gold; + long amount = (num ? num : 1 + (rnd(dlevel + 2) * rnd(30))); + + if ((gold = g_at(x, y)) != NULL) + gold->amount += amount; + else { + gold = newgold(); + gold->ngold = fgold; + gold->gx = x; + gold->gy = y; + gold->amount = amount; + fgold = gold; + /* do sth with display? */ + } +} diff --git a/hack/hack.mkshop.c b/hack/hack.mkshop.c new file mode 100644 index 0000000..48c217e --- /dev/null +++ b/hack/hack.mkshop.c @@ -0,0 +1,304 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.mkshop.c - version 1.0.3 */ +/* $FreeBSD: src/games/hack/hack.mkshop.c,v 1.4 1999/11/16 10:26:37 marcel Exp $ */ +/* $DragonFly: src/games/hack/hack.mkshop.c,v 1.4 2006/08/21 19:45:32 pavalos Exp $ */ + +#ifndef QUEST +#include "hack.h" +#include "def.eshk.h" +#define ESHK ((struct eshk *)(&(shk->mextra[0]))) +extern int nroom; +extern char shtypes[]; /* = "=/)%?!["; 8 types: 7 specialized, 1 mixed */ +schar shprobs[] = { 3,3,5,5,10,10,14,50 }; /* their probabilities */ + +static struct permonst *morguemon(void); +static bool nexttodoor(int, int); +static bool has_dnstairs(struct mkroom *); +static bool has_upstairs(struct mkroom *); +static bool isbig(struct mkroom *); +static int dist2(int, int, int, int); +static int sq(int); + +void +mkshop(void) +{ + struct mkroom *sroom; + int sh, sx, sy, i = -1; + char let; + int roomno; + struct monst *shk; + +#ifdef WIZARD + /* first determine shoptype */ + if (wizard) { + char *ep = getenv("SHOPTYPE"); + if (ep) { + if (*ep == 'z' || *ep == 'Z') { + mkzoo(ZOO); + return; + } + if (*ep == 'm' || *ep == 'M') { + mkzoo(MORGUE); + return; + } + if (*ep == 'b' || *ep == 'B') { + mkzoo(BEEHIVE); + return; + } + if (*ep == 's' || *ep == 'S') { + mkswamp(); + return; + } + for (i = 0; shtypes[i]; i++) + if (*ep == shtypes[i]) + break; + goto gottype; + } + } +gottype: +#endif /* WIZARD */ + for (sroom = &rooms[0], roomno = 0;; sroom++, roomno++) { + if (sroom->hx < 0) + return; + if (sroom - rooms >= nroom) { + pline("rooms not closed by -1?"); + return; + } + if (sroom->rtype) + continue; + if (!sroom->rlit || has_dnstairs(sroom) || has_upstairs(sroom)) + continue; + if ( +#ifdef WIZARD + (wizard && getenv("SHOPTYPE") && sroom->doorct != 0) || +#endif /* WIZARD */ + (sroom->doorct <= 2 && sroom->doorct > 0)) + break; + } + + if (i < 0) { /* shoptype not yet determined */ + int j; + + for (j = rn2(100), i = 0; (j -= shprobs[i]) >= 0; i++) + if (!shtypes[i]) /* superfluous */ + break; + if (isbig(sroom) && i + SHOPBASE == WANDSHOP) + i = GENERAL - SHOPBASE; + } + sroom->rtype = i + SHOPBASE; + let = shtypes[i]; + sh = sroom->fdoor; + sx = doors[sh].x; + sy = doors[sh].y; + if (sx == sroom->lx - 1) + sx++; + else if (sx == sroom->hx + 1) + sx--; + else if (sy == sroom->ly - 1) + sy++; + else if (sy == sroom->hy + 1) + sy--; + else { +#ifdef WIZARD + /* This is said to happen sometimes, but I've never seen it. */ + if (wizard) { + int j = sroom->doorct; + + pline("Where is shopdoor?"); + pline("Room at (%d,%d),(%d,%d).", sroom->lx, sroom->ly, + sroom->hx, sroom->hy); + pline("doormax=%d doorct=%d fdoor=%d", + doorindex, sroom->doorct, sh); + while (j--) { + pline("door [%d,%d]", doors[sh].x, doors[sh].y); + sh++; + } + more(); + } +#endif /* WIZARD */ + return; + } + if (!(shk = makemon(PM_SHK, sx, sy))) + return; + shk->isshk = shk->mpeaceful = 1; + shk->msleep = 0; + shk->mtrapseen = ~0; /* we know all the traps already */ + ESHK->shoproom = roomno; + ESHK->shoplevel = dlevel; + ESHK->shd = doors[sh]; + ESHK->shk.x = sx; + ESHK->shk.y = sy; + ESHK->robbed = 0; + ESHK->visitct = 0; + ESHK->following = 0; + shk->mgold = 1000 + 30 * rnd(100); /* initial capital */ + ESHK->billct = 0; + findname(ESHK->shknam, let); + for (sx = sroom->lx; sx <= sroom->hx; sx++) + for (sy = sroom->ly; sy <= sroom->hy; sy++) { + struct monst *mtmp; + if ((sx == sroom->lx && doors[sh].x == sx - 1) || + (sx == sroom->hx && doors[sh].x == sx + 1) || + (sy == sroom->ly && doors[sh].y == sy - 1) || + (sy == sroom->hy && doors[sh].y == sy + 1)) + continue; + if (rn2(100) < dlevel && !m_at(sx, sy) && + (mtmp = makemon(PM_MIMIC, sx, sy))) { + mtmp->mimic = 1; + mtmp->mappearance = + (let && rn2(10) < dlevel) ? let : ']'; + continue; + } + mkobj_at(let, sx, sy); + } +} + +void +mkzoo(int type) +{ + struct mkroom *sroom; + struct monst *mon; + int sh, sx, sy, i; + int goldlim = 500 * dlevel; + int moct = 0; + + i = nroom; + for (sroom = &rooms[rn2(nroom)];; sroom++) { + if (sroom == &rooms[nroom]) + sroom = &rooms[0]; + if (!i-- || sroom->hx < 0) + return; + if (sroom->rtype) + continue; + if (type == MORGUE && sroom->rlit) + continue; + if (has_upstairs(sroom) || (has_dnstairs(sroom) && rn2(3))) + continue; + if (sroom->doorct == 1 || !rn2(5)) + break; + } + sroom->rtype = type; + sh = sroom->fdoor; + for (sx = sroom->lx; sx <= sroom->hx; sx++) + for (sy = sroom->ly; sy <= sroom->hy; sy++) { + if ((sx == sroom->lx && doors[sh].x == sx - 1) || + (sx == sroom->hx && doors[sh].x == sx + 1) || + (sy == sroom->ly && doors[sh].y == sy - 1) || + (sy == sroom->hy && doors[sh].y == sy + 1)) + continue; + mon = makemon( + (type == MORGUE) ? morguemon() : + (type == BEEHIVE) ? PM_KILLER_BEE : NULL, + sx, sy); + if (mon) + mon->msleep = 1; + switch (type) { + case ZOO: + i = sq(dist2(sx, sy, doors[sh].x, doors[sh].y)); + if (i >= goldlim) + i = 5 * dlevel; + goldlim -= i; + mkgold((long)(10 + rn2(i)), sx, sy); + break; + case MORGUE: + /* Usually there is one dead body in the morgue */ + if (!moct && rn2(3)) { + mksobj_at(CORPSE, sx, sy); + moct++; + } + break; + case BEEHIVE: + if (!rn2(3)) + mksobj_at(LUMP_OF_ROYAL_JELLY, sx, sy); + break; + } + } +} + +static struct permonst * +morguemon(void) +{ + int i = rn2(100), hd = rn2(dlevel); + + if (hd > 10 && i < 10) + return (PM_DEMON); + if (hd > 8 && i > 85) + return (PM_VAMPIRE); + return ((i < 40) ? PM_GHOST : (i < 60) ? PM_WRAITH : PM_ZOMBIE); +} + +void +mkswamp(void) /* Michiel Huisjes & Fred de Wilde */ +{ + struct mkroom *sroom; + int sx, sy, i, eelct = 0; + + for (i = 0; i < 5; i++) { /* 5 tries */ + sroom = &rooms[rn2(nroom)]; + if (sroom->hx < 0 || sroom->rtype || + has_upstairs(sroom) || has_dnstairs(sroom)) + continue; + + /* satisfied; make a swamp */ + sroom->rtype = SWAMP; + for (sx = sroom->lx; sx <= sroom->hx; sx++) + for (sy = sroom->ly; sy <= sroom->hy; sy++) + if ((sx + sy) % 2 && !o_at(sx, sy) && + !t_at(sx, sy) && !m_at(sx, sy) && + !nexttodoor(sx, sy)) { + levl[sx][sy].typ = POOL; + levl[sx][sy].scrsym = POOL_SYM; + if (!eelct || !rn2(4)) { + makemon(PM_EEL, sx, sy); + eelct++; + } + } + } +} + +static bool +nexttodoor(int sx, int sy) +{ + int dx, dy; + struct rm *lev; + for (dx = -1; dx <= 1; dx++) + for (dy = -1; dy <= 1; dy++) + if ((lev = &levl[sx + dx][sy + dy])->typ == DOOR || + lev->typ == SDOOR || lev->typ == LDOOR) + return (1); + return (0); +} + +static bool +has_dnstairs(struct mkroom *sroom) +{ + return (sroom->lx <= xdnstair && xdnstair <= sroom->hx && + sroom->ly <= ydnstair && ydnstair <= sroom->hy); +} + +static bool +has_upstairs(struct mkroom *sroom) +{ + return (sroom->lx <= xupstair && xupstair <= sroom->hx && + sroom->ly <= yupstair && yupstair <= sroom->hy); +} + +static bool +isbig(struct mkroom *sroom) +{ + int area = (sroom->hx - sroom->lx) * (sroom->hy - sroom->ly); + return (area > 20); +} + +static int +dist2(int x0, int y0, int x1, int y1) +{ + return ((x0 - x1) * (x0 - x1) + (y0 - y1) * (y0 - y1)); +} + +static int +sq(int a) +{ + return (a * a); +} +#endif /* QUEST */ diff --git a/hack/hack.mon.c b/hack/hack.mon.c new file mode 100644 index 0000000..2554926 --- /dev/null +++ b/hack/hack.mon.c @@ -0,0 +1,961 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.mon.c - version 1.0.3 */ +/* $FreeBSD: src/games/hack/hack.mon.c,v 1.5 1999/11/16 10:26:37 marcel Exp $ */ +/* $DragonFly: src/games/hack/hack.mon.c,v 1.6 2008/06/05 18:01:49 swildner Exp $ */ + +#include "hack.h" +#include "hack.mfndpos.h" + +int warnlevel; /* used by movemon and dochugw */ +long lastwarntime; +int lastwarnlev; +static const char *warnings[] = { + "white", "pink", "red", "ruby", "purple", "black" +}; + +static int dochugw(struct monst *); +static void mpickgold(struct monst *); +static void mpickgems(struct monst *); +static void dmonsfree(void); +static bool ishuman(struct monst *); + +void +movemon(void) +{ + struct monst *mtmp; + int fr; + + warnlevel = 0; + + for (;;) { + /* find a monster that we haven't treated yet */ + /* + * note that mtmp or mtmp->nmon might get killed while mtmp + * moves, so we cannot just walk down the chain (even new + * monsters might get created!) + */ + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) + if (mtmp->mlstmv < moves) + goto next_mon; + /* treated all monsters */ + break; + +next_mon: + mtmp->mlstmv = moves; + + /* most monsters drown in pools */ + { + boolean inpool, iseel; + + inpool = (levl[mtmp->mx][mtmp->my].typ == POOL); + iseel = (mtmp->data->mlet == ';'); + if (inpool && !iseel) { + if (cansee(mtmp->mx, mtmp->my)) + pline("%s drowns.", Monnam(mtmp)); + mondead(mtmp); + continue; + } + /* but eels have a difficult time outside */ + if (iseel && !inpool) { + if (mtmp->mhp > 1) + mtmp->mhp--; + mtmp->mflee = 1; + mtmp->mfleetim += 2; + } + } + if (mtmp->mblinded && !--mtmp->mblinded) + mtmp->mcansee = 1; + if (mtmp->mfleetim && !--mtmp->mfleetim) + mtmp->mflee = 0; + if (mtmp->mimic) + continue; + if (mtmp->mspeed != MSLOW || !(moves % 2)) { + /* continue if the monster died fighting */ + fr = -1; + if (Conflict && cansee(mtmp->mx, mtmp->my) + && (fr = fightm(mtmp)) == 2) + continue; + if (fr < 0 && dochugw(mtmp)) + continue; + } + if (mtmp->mspeed == MFAST && dochugw(mtmp)) + continue; + } + + warnlevel -= u.ulevel; + if (warnlevel >= SIZE(warnings)) + warnlevel = SIZE(warnings) - 1; + if (warnlevel >= 0) + if (warnlevel > lastwarnlev || moves > lastwarntime + 5) { + const char *rr; + switch (Warning & (LEFT_RING | RIGHT_RING)) { + case LEFT_RING: + rr = "Your left ring glows"; + break; + case RIGHT_RING: + rr = "Your right ring glows"; + break; + case LEFT_RING | RIGHT_RING: + rr = "Both your rings glow"; + break; + default: + rr = "Your fingertips glow"; + break; + } + pline("%s %s!", rr, warnings[warnlevel]); + lastwarntime = moves; + lastwarnlev = warnlevel; + } + + dmonsfree(); /* remove all dead monsters */ +} + +void +justswld(struct monst *mtmp, const char *name) +{ + mtmp->mx = u.ux; + mtmp->my = u.uy; + u.ustuck = mtmp; + pmon(mtmp); + kludge("%s swallows you!", name); + more(); + seeoff(1); + u.uswallow = 1; + u.uswldtim = 0; + swallowed(); +} + +void +youswld(struct monst *mtmp, int dam, int die, const char *name) +{ + if (mtmp != u.ustuck) + return; + kludge("%s digests you!", name); + u.uhp -= dam; + if ((int)u.uswldtim++ >= die) { /* a3 */ + pline("It totally digests you!"); + u.uhp = -1; + } + if (u.uhp < 1) + done_in_by(mtmp); +#if 0 + flags.botlx = 1; /* should we show status line ? */ +#endif +} + +static int +dochugw(struct monst *mtmp) +{ + int x = mtmp->mx; + int y = mtmp->my; + int d1 = dochug(mtmp); + int dd; + + if (!d1) /* monster still alive */ + if (Warning) + if (!mtmp->mpeaceful) + if (mtmp->data->mlevel > warnlevel) + if ((dd = dist(mtmp->mx, mtmp->my)) < + dist(x, y)) + if (dd < 100) + if (!canseemon(mtmp)) + warnlevel = + mtmp-> + data-> + mlevel; + return (d1); +} + +/* returns 1 if monster died moving, 0 otherwise */ +bool +dochug(struct monst *mtmp) +{ + struct permonst *mdat; + int tmp = 0, nearby, scared; + + if (mtmp->cham && !rn2(6)) + newcham(mtmp, &mons[dlevel + 14 + rn2(CMNUM - 14 - dlevel)]); + mdat = mtmp->data; + if (mdat->mlevel < 0) + panic("bad monster %c (%d)", mdat->mlet, mdat->mlevel); + + /* regenerate monsters */ + if ((!(moves % 20) || strchr(MREGEN, mdat->mlet)) && + mtmp->mhp < mtmp->mhpmax) + mtmp->mhp++; + + if (mtmp->mfroz) /* frozen monsters don't do anything */ + return (0); + + if (mtmp->msleep) { + /* wake up, or get out of here. */ + /* ettins are hard to surprise */ + /* Nymphs and Leprechauns do not easily wake up */ + if (cansee(mtmp->mx, mtmp->my) && + (!Stealth || (mdat->mlet == 'e' && rn2(10))) && + (!strchr("NL", mdat->mlet) || !rn2(50)) && + (Aggravate_monster || strchr("d1", mdat->mlet) + || (!rn2(7) && !mtmp->mimic))) + mtmp->msleep = 0; + else + return (0); + } + + /* not frozen or sleeping: wipe out texts written in the dust */ + wipe_engr_at(mtmp->mx, mtmp->my, 1); + + /* confused monsters get unconfused with small probability */ + if (mtmp->mconf && !rn2(50)) + mtmp->mconf = 0; + + /* some monsters teleport */ + if (mtmp->mflee && strchr("tNL", mdat->mlet) && !rn2(40)) { + rloc(mtmp); + return (0); + } + if (mdat->mmove < rnd(6)) + return (0); + + /* fleeing monsters might regain courage */ + if (mtmp->mflee && !mtmp->mfleetim + && mtmp->mhp == mtmp->mhpmax && !rn2(25)) + mtmp->mflee = 0; + + nearby = (dist(mtmp->mx, mtmp->my) < 3); + scared = (nearby && (sengr_at("Elbereth", u.ux, u.uy) || + sobj_at(SCR_SCARE_MONSTER, u.ux, u.uy))); + if (scared && !mtmp->mflee) { + mtmp->mflee = 1; + mtmp->mfleetim = (rn2(7) ? rnd(10) : rnd(100)); + } + + if (!nearby || + mtmp->mflee || + mtmp->mconf || + (mtmp->minvis && !rn2(3)) || + (strchr("BIuy", mdat->mlet) && !rn2(4)) || + (mdat->mlet == 'L' && !u.ugold && (mtmp->mgold || rn2(2))) || + (!mtmp->mcansee && !rn2(4)) || + mtmp->mpeaceful + ) { + tmp = m_move(mtmp, 0); /* 2: monster died moving */ + if (tmp == 2 || (tmp && mdat->mmove <= 12)) + return (tmp == 2); + } + + if (!strchr("Ea", mdat->mlet) && nearby && + !mtmp->mpeaceful && u.uhp > 0 && !scared) { + if (mhitu(mtmp)) + return (1); /* monster died (e.g. 'y' or 'F') */ + } + /* extra movement for fast monsters */ + if (mdat->mmove - 12 > rnd(12)) + tmp = m_move(mtmp, 1); + return (tmp == 2); +} + +int +m_move(struct monst *mtmp, int after) +{ + struct monst *mtmp2; + int nx, ny, omx, omy, appr, nearer, cnt, i, j; + xchar gx, gy, nix, niy, chcnt; + schar chi; + boolean likegold = 0, likegems = 0, likeobjs; + char msym = mtmp->data->mlet; + schar mmoved = 0; /* not strictly nec.: chi >= 0 will do */ + coord poss[9]; + int info[9]; + + if (mtmp->mfroz || mtmp->msleep) + return (0); + if (mtmp->mtrapped) { + i = mintrap(mtmp); + if (i == 2) /* he died */ + return (2); + if (i == 1) /* still in trap, so didnt move */ + return (0); + } + if (mtmp->mhide && o_at(mtmp->mx, mtmp->my) && rn2(10)) + return (0); /* do not leave hiding place */ + +#ifndef NOWORM + if (mtmp->wormno) + goto not_special; +#endif /* NOWORM */ + + /* my dog gets a special treatment */ + if (mtmp->mtame) + return (dog_move(mtmp, after)); + + /* likewise for shopkeeper */ + if (mtmp->isshk) { + mmoved = shk_move(mtmp); + if (mmoved >= 0) + goto postmov; + mmoved = 0; /* follow player outside shop */ + } + + /* and for the guard */ + if (mtmp->isgd) { + mmoved = gd_move(); + goto postmov; + } + + /* teleport if that lies in our nature ('t') or when badly wounded ('1') */ + if ((msym == 't' && !rn2(5)) + || (msym == '1' && (mtmp->mhp < 7 || (!xdnstair && !rn2(5)) + || levl[u.ux][u.uy].typ == STAIRS))) { + if (mtmp->mhp < 7 || (msym == 't' && rn2(2))) + rloc(mtmp); + else + mnexto(mtmp); + mmoved = 1; + goto postmov; + } + + /* spit fire ('D') or use a wand ('1') when appropriate */ + if (strchr("D1", msym)) + inrange(mtmp); + + if (msym == 'U' && !mtmp->mcan && canseemon(mtmp) && + mtmp->mcansee && rn2(5)) { + if (!Confusion) + pline("%s's gaze has confused you!", Monnam(mtmp)); + else + pline("You are getting more and more confused."); + if (rn2(3)) + mtmp->mcan = 1; + Confusion += d(3, 4); /* timeout */ + } +not_special: + if (!mtmp->mflee && u.uswallow && u.ustuck != mtmp) + return (1); + appr = 1; + if (mtmp->mflee) + appr = -1; + if (mtmp->mconf || Invis || !mtmp->mcansee || + (strchr("BIy", msym) && !rn2(3))) + appr = 0; + omx = mtmp->mx; + omy = mtmp->my; + gx = u.ux; + gy = u.uy; + if (msym == 'L' && appr == 1 && mtmp->mgold > u.ugold) + appr = -1; + + /* + * random criterion for 'smell' or track finding ability should use + * mtmp->msmell or sth + */ + if (msym == '@' || + ('a' <= msym && msym <= 'z')) { + coord *cp; + schar mroom; + mroom = inroom(omx, omy); + if (mroom < 0 || mroom != inroom(u.ux, u.uy)) { + cp = gettrack(omx, omy); + if (cp) { + gx = cp->x; + gy = cp->y; + } + } + } + + /* look for gold or jewels nearby */ + likegold = (strchr("LOD", msym) != NULL); + likegems = (strchr("ODu", msym) != NULL); + likeobjs = mtmp->mhide; +#define SRCHRADIUS 25 + { + xchar mind = SRCHRADIUS; /* not too far away */ + int dd; + if (likegold) { + struct gold *gold; + for (gold = fgold; gold; gold = gold->ngold) + if ((dd = DIST(omx, omy, gold->gx, + gold->gy)) < mind) { + mind = dd; + gx = gold->gx; + gy = gold->gy; + } + } + if (likegems || likeobjs) { + struct obj *otmp; + for (otmp = fobj; otmp; otmp = otmp->nobj) + if (likeobjs || otmp->olet == GEM_SYM) + if (msym != 'u' || + objects[otmp->otyp].g_val != 0) + if ((dd = DIST(omx, omy, otmp->ox, + otmp->oy)) < mind) { + mind = dd; + gx = otmp->ox; + gy = otmp->oy; + } + } + if (mind < SRCHRADIUS && appr == -1) { + if (dist(omx, omy) < 10) { + gx = u.ux; + gy = u.uy; + } else + appr = 1; + } + } + nix = omx; + niy = omy; + cnt = mfndpos(mtmp, poss, info, + msym == 'u' ? NOTONL : + (msym == '@' || msym == '1') ? (ALLOW_SSM | ALLOW_TRAPS) : + strchr(UNDEAD, msym) ? NOGARLIC : ALLOW_TRAPS); + /* ALLOW_ROCK for some monsters ? */ + chcnt = 0; + chi = -1; + for (i = 0; i < cnt; i++) { + nx = poss[i].x; + ny = poss[i].y; + for (j = 0; j < MTSZ && j < cnt - 1; j++) + if (nx == mtmp->mtrack[j].x && ny == mtmp->mtrack[j].y) + if (rn2(4 * (cnt - j))) + goto nxti; +#ifdef STUPID + /* some stupid compilers think that this is too complicated */ + { + int d1 = DIST(nx, ny, gx, gy); + int d2 = DIST(nix, niy, gx, gy); + nearer = (d1 < d2); + } +#else + nearer = (DIST(nx, ny, gx, gy) < DIST(nix, niy, gx, gy)); +#endif /* STUPID */ + if ((appr == 1 && nearer) || (appr == -1 && !nearer) || + !mmoved || + (!appr && !rn2(++chcnt))) { + nix = nx; + niy = ny; + chi = i; + mmoved = 1; + } +nxti:; + } + if (mmoved) { + if (info[chi] & ALLOW_M) { + mtmp2 = m_at(nix, niy); + if (hitmm(mtmp, mtmp2) == 1 && rn2(4) && + hitmm(mtmp2, mtmp) == 2) + return (2); + return (0); + } + if (info[chi] & ALLOW_U) { + hitu(mtmp, d(mtmp->data->damn, mtmp->data->damd) + 1); + return (0); + } + mtmp->mx = nix; + mtmp->my = niy; + for (j = MTSZ - 1; j > 0; j--) + mtmp->mtrack[j] = mtmp->mtrack[j - 1]; + mtmp->mtrack[0].x = omx; + mtmp->mtrack[0].y = omy; +#ifndef NOWORM + if (mtmp->wormno) + worm_move(mtmp); +#endif /* NOWORM */ + } else { + if (msym == 'u' && rn2(2)) { + rloc(mtmp); + return (0); + } +#ifndef NOWORM + if (mtmp->wormno) + worm_nomove(mtmp); +#endif /* NOWORM */ + } +postmov: + if (mmoved == 1) { + if (mintrap(mtmp) == 2) /* he died */ + return (2); + if (likegold) + mpickgold(mtmp); + if (likegems) + mpickgems(mtmp); + if (mtmp->mhide) + mtmp->mundetected = 1; + } + pmon(mtmp); + return (mmoved); +} + +static void +mpickgold(struct monst *mtmp) +{ + struct gold *gold; + + while ((gold = g_at(mtmp->mx, mtmp->my)) != NULL) { + mtmp->mgold += gold->amount; + freegold(gold); + if (levl[mtmp->mx][mtmp->my].scrsym == '$') + newsym(mtmp->mx, mtmp->my); + } +} + +static void +mpickgems(struct monst *mtmp) +{ + struct obj *otmp; + + for (otmp = fobj; otmp; otmp = otmp->nobj) + if (otmp->olet == GEM_SYM) + if (otmp->ox == mtmp->mx && otmp->oy == mtmp->my) + if (mtmp->data->mlet != 'u' || + objects[otmp->otyp].g_val != 0) { + freeobj(otmp); + mpickobj(mtmp, otmp); + if (levl[mtmp->mx][mtmp->my].scrsym == GEM_SYM) + newsym(mtmp->mx, mtmp->my); /* %% */ + return; /* pick only one object */ + } +} + +/* return number of acceptable neighbour positions */ +int +mfndpos(struct monst *mon, coord poss[9], int info[9], int flag) +{ + int x, y, nx, ny, cnt = 0, ntyp; + struct monst *mtmp; + int nowtyp; + boolean pool; + + x = mon->mx; + y = mon->my; + nowtyp = levl[x][y].typ; + + pool = (mon->data->mlet == ';'); +nexttry: + /* + * eels prefer the water, but if there is no water nearby, they will + * crawl over land + */ + if (mon->mconf) { + flag |= ALLOW_ALL; + flag &= ~NOTONL; + } + for (nx = x - 1; nx <= x + 1; nx++) + for (ny = y - 1; ny <= y + 1; ny++) + if (nx != x || ny != y) + if (isok(nx, ny)) + if (!IS_ROCK(ntyp = levl[nx][ny].typ)) + if (!(nx != x && ny != y && + (nowtyp == DOOR || ntyp == DOOR))) + if ((ntyp == POOL) == pool) { + info[cnt] = 0; + if (nx == u.ux && ny == u.uy) { + if (!(flag & ALLOW_U)) + continue; + info[cnt] = ALLOW_U; + } else if ((mtmp = m_at(nx, ny)) != NULL) { + if (!(flag & ALLOW_M)) + continue; + info[cnt] = ALLOW_M; + if (mtmp->mtame) { + if (!(flag & ALLOW_TM)) + continue; + info[cnt] |= ALLOW_TM; + } + } + if (sobj_at(CLOVE_OF_GARLIC, nx, ny)) { + if (flag & NOGARLIC) + continue; + info[cnt] |= NOGARLIC; + } + if (sobj_at(SCR_SCARE_MONSTER, nx, ny) || + (!mon->mpeaceful && + sengr_at("Elbereth", nx, ny))) { + if (!(flag & ALLOW_SSM)) + continue; + info[cnt] |= ALLOW_SSM; + } + if (sobj_at(ENORMOUS_ROCK, nx, ny)) { + if (!(flag & ALLOW_ROCK)) + continue; + info[cnt] |= ALLOW_ROCK; + } + if (!Invis && online(nx, ny)) { + if (flag & NOTONL) + continue; + info[cnt] |= NOTONL; + } + /* we cannot avoid traps of an unknown kind */ + { + struct trap *ttmp = t_at(nx, ny); + int tt; + if (ttmp) { + tt = 1 << ttmp->ttyp; + if (mon->mtrapseen & tt) { + if (!(flag & tt)) + continue; + info[cnt] |= tt; + } + } + } + poss[cnt].x = nx; + poss[cnt].y = ny; + cnt++; + } + if (!cnt && pool && nowtyp != POOL) { + pool = FALSE; + goto nexttry; + } + return (cnt); +} + +int +dist(int x, int y) +{ + return ((x - u.ux) * (x - u.ux) + (y - u.uy) * (y - u.uy)); +} + +void +poisoned(const char *string, const char *pname) +{ + int i; + + if (Blind) + pline("It was poisoned."); + else + pline("The %s was poisoned!", string); + if (Poison_resistance) { + pline("The poison doesn't seem to affect you."); + return; + } + i = rn2(10); + if (i == 0) { + u.uhp = -1; + pline("I am afraid the poison was deadly ..."); + } else if (i <= 5) { + losestr(rn1(3, 3)); + } else { + losehp(rn1(10, 6), pname); + } + if (u.uhp < 1) { + killer = pname; + done("died"); + } +} + +void +mondead(struct monst *mtmp) +{ + relobj(mtmp, 1); + unpmon(mtmp); + relmon(mtmp); + unstuck(mtmp); + if (mtmp->isshk) + shkdead(mtmp); + if (mtmp->isgd) + gddead(); +#ifndef NOWORM + if (mtmp->wormno) + wormdead(mtmp); +#endif /* NOWORM */ + monfree(mtmp); +} + +/* called when monster is moved to larger structure */ +void +replmon(struct monst *mtmp, struct monst *mtmp2) +{ + relmon(mtmp); + monfree(mtmp); + mtmp2->nmon = fmon; + fmon = mtmp2; + if (u.ustuck == mtmp) + u.ustuck = mtmp2; + if (mtmp2->isshk) + replshk(mtmp, mtmp2); + if (mtmp2->isgd) + replgd(mtmp, mtmp2); +} + +void +relmon(struct monst *mon) +{ + struct monst *mtmp; + + if (mon == fmon) + fmon = fmon->nmon; + else { + for (mtmp = fmon; mtmp->nmon != mon; mtmp = mtmp->nmon) + ; /* nothing */ + mtmp->nmon = mon->nmon; + } +} + +/* + * we do not free monsters immediately, in order to have their name available + * shortly after their demise + */ +struct monst *fdmon; /* chain of dead monsters, need not to be saved */ + +void +monfree(struct monst *mtmp) +{ + mtmp->nmon = fdmon; + fdmon = mtmp; +} + +static void +dmonsfree(void) +{ + struct monst *mtmp; + + while ((mtmp = fdmon) != NULL) { + fdmon = mtmp->nmon; + free(mtmp); + } +} + +void +unstuck(struct monst *mtmp) +{ + if (u.ustuck == mtmp) { + if (u.uswallow) { + u.ux = mtmp->mx; + u.uy = mtmp->my; + u.uswallow = 0; + setsee(); + docrt(); + } + u.ustuck = 0; + } +} + +void +killed(struct monst *mtmp) +{ +#ifdef lint +#define NEW_SCORING + int tmp2; +#endif /* lint */ + int tmp, nk, x, y; + struct permonst *mdat; + + if (mtmp->cham) + mtmp->data = PM_CHAMELEON; + mdat = mtmp->data; + if (Blind) + pline("You destroy it!"); + else + pline("You destroy %s!", + mtmp->mtame ? amonnam(mtmp, "poor") : monnam(mtmp)); + if (u.umconf) { + if (!Blind) + pline("Your hands stop glowing blue."); + u.umconf = 0; + } + + /* count killed monsters */ +#define MAXMONNO 100 + nk = 1; /* in case we cannot find it in mons */ + tmp = mdat - mons; /* index in mons array (if not 'd', '@', ...) */ + if (tmp >= 0 && tmp < CMNUM + 2) { + u.nr_killed[tmp]++; + if ((nk = u.nr_killed[tmp]) > MAXMONNO && + !strchr(fut_geno, mdat->mlet)) + charcat(fut_geno, mdat->mlet); + } + + /* punish bad behaviour */ + if (mdat->mlet == '@') + Telepat = 0, u.uluck -= 2; + if (mtmp->mpeaceful || mtmp->mtame) + u.uluck--; + if (mdat->mlet == 'u') + u.uluck -= 5; + if ((int)u.uluck < LUCKMIN) + u.uluck = LUCKMIN; + + /* give experience points */ + tmp = 1 + mdat->mlevel * mdat->mlevel; + if (mdat->ac < 3) + tmp += 2 * (7 - mdat->ac); + if (strchr("AcsSDXaeRTVWU&In:P", mdat->mlet)) + tmp += 2 * mdat->mlevel; + if (strchr("DeV&P", mdat->mlet)) + tmp += (7 * mdat->mlevel); + if (mdat->mlevel > 6) + tmp += 50; + if (mdat->mlet == ';') + tmp += 1000; + +#ifdef NEW_SCORING + /* ------- recent addition: make nr of points decrease + * when this is not the first of this kind */ + { + int ul = u.ulevel; + int ml = mdat->mlevel; + + if (ul < 14) /* points are given based on present and future level */ + for (tmp2 = 0; !tmp2 || ul + tmp2 <= ml; tmp2++) + if (u.uexp + 1 + (tmp + ((tmp2 <= 0) ? 0 : 4 << (tmp2 - 1))) / nk + >= 10 * pow((unsigned)(ul - 1))) + if (++ul == 14) + break; + + tmp2 = ml - ul - 1; + tmp = (tmp + ((tmp2 < 0) ? 0 : 4 << tmp2)) / nk; + if (!tmp) + tmp = 1; + } + /* note: ul is not necessarily the future value of u.ulevel */ + /* ------- end of recent valuation change ------- */ +#endif /* NEW_SCORING */ + + more_experienced(tmp, 0); + flags.botl = 1; + while (u.ulevel < 14 && u.uexp >= newuexp()) { + pline("Welcome to experience level %u.", ++u.ulevel); + tmp = rnd(10); + if (tmp < 3) + tmp = rnd(10); + u.uhpmax += tmp; + u.uhp += tmp; + flags.botl = 1; + } + + /* dispose of monster and make cadaver */ + x = mtmp->mx; + y = mtmp->my; + mondead(mtmp); + tmp = mdat->mlet; + if (tmp == 'm') { /* he killed a minotaur, give him a wand of digging */ + /* note: the dead minotaur will be on top of it! */ + mksobj_at(WAN_DIGGING, x, y); + /* if (cansee(x, y)) atl(x, y, fobj->olet); */ + stackobj(fobj); + } else +#ifndef NOWORM + if (tmp == 'w') { + mksobj_at(WORM_TOOTH, x, y); + stackobj(fobj); + } else +#endif /* NOWORM */ + if (!letter(tmp) || (!strchr("mw", tmp) && !rn2(3))) + tmp = 0; + + if (ACCESSIBLE(levl[x][y].typ)) /* might be mimic in wall or dead eel*/ + if (x != u.ux || y != u.uy) /* might be here after swallowed */ + if (strchr("NTVm&", mdat->mlet) || rn2(5)) { + struct obj *obj2 = mkobj_at(tmp, x, y); + if (cansee(x, y)) + atl(x, y, obj2->olet); + stackobj(obj2); + } +} + +void +kludge(const char *str, const char *arg) +{ + if (Blind) { + if (*str == '%') + pline(str, "It"); + else + pline(str, "it"); + } else + pline(str, arg); +} + +void +rescham(void) /* force all chameleons to become normal */ +{ + struct monst *mtmp; + + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) + if (mtmp->cham) { + mtmp->cham = 0; + newcham(mtmp, PM_CHAMELEON); + } +} + +/* make a chameleon look like a new monster */ +/* returns 1 if the monster actually changed */ +bool +newcham(struct monst *mtmp, struct permonst *mdat) +{ + int mhp, hpn, hpd; + + if (mdat == mtmp->data) /* still the same monster */ + return (0); +#ifndef NOWORM + if (mtmp->wormno) /* throw tail away */ + wormdead(mtmp); +#endif /* NOWORM */ + if (u.ustuck == mtmp) { + if (u.uswallow) { + u.uswallow = 0; + u.uswldtim = 0; + mnexto(mtmp); + docrt(); + prme(); + } + u.ustuck = 0; + } + hpn = mtmp->mhp; + hpd = (mtmp->data->mlevel) * 8; + if (!hpd) + hpd = 4; + mtmp->data = mdat; + mhp = (mdat->mlevel) * 8; + /* new hp: same fraction of max as before */ + mtmp->mhp = 2 + (hpn * mhp) / hpd; + hpn = mtmp->mhpmax; + mtmp->mhpmax = 2 + (hpn * mhp) / hpd; + mtmp->minvis = (mdat->mlet == 'I') ? 1 : 0; +#ifndef NOWORM + if (mdat->mlet == 'w' && getwn(mtmp)) + initworm(mtmp); + /* perhaps we should clear mtmp->mtame here? */ +#endif /* NOWORM */ + unpmon(mtmp); /* necessary for 'I' and to force pmon */ + pmon(mtmp); + return (1); +} + +/* Make monster mtmp next to you (if possible) */ +void +mnexto(struct monst *mtmp) +{ + coord mm; + mm = enexto(u.ux, u.uy); + mtmp->mx = mm.x; + mtmp->my = mm.y; + pmon(mtmp); +} + +static bool +ishuman(struct monst *mtmp) +{ + return (mtmp->data->mlet == '@'); +} + +void +setmangry(struct monst *mtmp) +{ + if (!mtmp->mpeaceful) + return; + if (mtmp->mtame) + return; + mtmp->mpeaceful = 0; + if (ishuman(mtmp)) + pline("%s gets angry!", Monnam(mtmp)); +} + +/* + * not one hundred percent correct: now a snake may hide under an invisible + * object + */ +bool +canseemon(struct monst *mtmp) +{ + return ((!mtmp->minvis || See_invisible) + && (!mtmp->mhide || !o_at(mtmp->mx, mtmp->my)) + && cansee(mtmp->mx, mtmp->my)); +} diff --git a/hack/hack.monst.c b/hack/hack.monst.c new file mode 100644 index 0000000..5b743ac --- /dev/null +++ b/hack/hack.monst.c @@ -0,0 +1,79 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.monst.c - version 1.0.2 */ +/* $DragonFly: src/games/hack/hack.monst.c,v 1.3 2006/08/21 19:45:32 pavalos Exp $ */ + +#include "hack.h" +#include "def.eshk.h" + +struct permonst mons[CMNUM + 2] = { + { "bat", 'B',1,22,8,1,4,0 }, + { "gnome", 'G',1,6,5,1,6,0 }, + { "hobgoblin", 'H',1,9,5,1,8,0 }, + { "jackal", 'J',0,12,7,1,2,0 }, + { "kobold", 'K',1,6,7,1,4,0 }, + { "leprechaun", 'L',5,15,8,1,2,0 }, + { "giant rat", 'r',0,12,7,1,3,0 }, + { "acid blob", 'a',2,3,8,0,0,0 }, + { "floating eye", 'E',2,1,9,0,0,0 }, + { "homunculus", 'h',2,6,6,1,3,0 }, + { "imp", 'i',2,6,2,1,4,0 }, + { "orc", 'O',2,9,6,1,8,0 }, + { "yellow light", 'y',3,15,0,0,0,0 }, + { "zombie", 'Z',2,6,8,1,8,0 }, + { "giant ant", 'A',3,18,3,1,6,0 }, + { "fog cloud", 'f',3,1,0,1,6,0 }, + { "nymph", 'N',6,12,9,1,2,0 }, + { "piercer", 'p',3,1,3,2,6,0 }, + { "quasit", 'Q',3,15,3,1,4,0 }, + { "quivering blob", 'q',3,1,8,1,8,0 }, + { "violet fungi", 'v',3,1,7,1,4,0 }, + { "giant beetle", 'b',4,6,4,3,4,0 }, + { "centaur", 'C',4,18,4,1,6,0 }, + { "cockatrice", 'c',4,6,6,1,3,0 }, + { "gelatinous cube", 'g',4,6,8,2,4,0 }, + { "jaguar", 'j',4,15,6,1,8,0 }, + { "killer bee", 'k',4,14,4,2,4,0 }, + { "snake", 'S',4,15,3,1,6,0 }, + { "freezing sphere", 'F',2,13,4,0,0,0 }, + { "owlbear", 'o',5,12,5,2,6,0 }, + { "rust monster", 'R',10,18,3,0,0,0 }, + { "scorpion", 's',5,15,3,1,4,0 }, + { "tengu", 't',5,13,5,1,7,0 }, + { "wraith", 'W',5,12,5,1,6,0 }, +#ifdef NOWORM + { "wumpus", 'w',8,3,2,3,6,0 }, +#else + { "long worm", 'w',8,3,5,1,4,0 }, +#endif /* NOWORM */ + { "large dog", 'd',6,15,4,2,4,0 }, + { "leocrotta", 'l',6,18,4,3,6,0 }, + { "mimic", 'M',7,3,7,3,4,0 }, + { "troll", 'T',7,12,4,2,7,0 }, + { "unicorn", 'u',8,24,5,1,10,0 }, + { "yeti", 'Y',5,15,6,1,6,0 }, + { "stalker", 'I',8,12,3,4,4,0 }, + { "umber hulk", 'U',9,6,2,2,10,0 }, + { "vampire", 'V',8,12,1,1,6,0 }, + { "xorn", 'X',8,9,-2,4,6,0 }, + { "xan", 'x',7,18,-2,2,4,0 }, + { "zruty", 'z',9,8,3,3,6,0 }, + { "chameleon", ':',6,5,6,4,2,0 }, + { "dragon", 'D',10,9,-1,3,8,0 }, + { "ettin", 'e',10,12,3,2,8,0 }, + { "lurker above", '\'',10,3,3,0,0,0 }, + { "nurse", 'n',11,6,0,1,3,0 }, + { "trapper", ',',12,3,3,0,0,0 }, + { "purple worm", 'P',15,9,6,2,8,0 }, + { "demon", '&',10,12,-4,1,4,0 }, + { "minotaur", 'm',15,15,6,4,10,0 }, + { "shopkeeper", '@', 12, 18, 0, 4, 8, sizeof(struct eshk) } +}; + +struct permonst pm_ghost = { "ghost", ' ', 10, 3, -5, 1, 1, sizeof(plname) }; +struct permonst pm_wizard = { + "wizard of Yendor", '1', 15, 12, -2, 1, 12, 0 +}; +#ifdef MAIL +struct permonst pm_mail_daemon = { "mail daemon", '2', 100, 1, 10, 0, 0, 0 }; +#endif /* MAIL */ +struct permonst pm_eel = { "giant eel", ';', 15, 6, -3, 3, 6, 0 }; diff --git a/hack/hack.o_init.c b/hack/hack.o_init.c new file mode 100644 index 0000000..dc17896 --- /dev/null +++ b/hack/hack.o_init.c @@ -0,0 +1,184 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.o_init.c - version 1.0.3 */ +/* $FreeBSD: src/games/hack/hack.o_init.c,v 1.6 1999/11/16 10:26:37 marcel Exp $ */ +/* $DragonFly: src/games/hack/hack.o_init.c,v 1.4 2006/08/21 19:45:32 pavalos Exp $ */ + +#include "def.objects.h" +#include "hack.h" + +static void setgemprobs(void); +static bool interesting_to_discover(int); + +int +letindex(char let) +{ + int i = 0; + char ch; + + while ((ch = obj_symbols[i++]) != 0) + if (ch == let) + return (i); + return (0); +} + +void +init_objects(void) +{ + int i, j, first, last, sum, end; + char let; + const char *tmp; + + /* + * init base; if probs given check that they add up to 100, otherwise + * compute probs; shuffle descriptions + */ + end = SIZE(objects); + first = 0; + while (first < end) { + let = objects[first].oc_olet; + last = first + 1; + while (last < end && objects[last].oc_olet == let + && objects[last].oc_name != NULL) + last++; + i = letindex(let); + if ((!i && let != ILLOBJ_SYM) || bases[i] != 0) + error("initialization error"); + bases[i] = first; + + if (let == GEM_SYM) + setgemprobs(); +check: + sum = 0; + for (j = first; j < last; j++) + sum += objects[j].oc_prob; + if (sum == 0) { + for (j = first; j < last; j++) + objects[j].oc_prob = (100 + j - first) / (last - first); + goto check; + } + if (sum != 100) + error("init-prob error for %c", let); + + if (objects[first].oc_descr != NULL && let != TOOL_SYM) { + /* shuffle, also some additional descriptions */ + while (last < end && objects[last].oc_olet == let) + last++; + j = last; + while (--j > first) { + i = first + rn2(j + 1 - first); + tmp = objects[j].oc_descr; + objects[j].oc_descr = objects[i].oc_descr; + objects[i].oc_descr = tmp; + } + } + first = last; + } +} + +int +probtype(char let) +{ + int i = bases[letindex(let)]; + int prob = rn2(100); + + while ((prob -= objects[i].oc_prob) >= 0) + i++; + if (objects[i].oc_olet != let || !objects[i].oc_name) + panic("probtype(%c) error, i=%d", let, i); + return (i); +} + +static void +setgemprobs(void) +{ + int j, first; + + first = bases[letindex(GEM_SYM)]; + + for (j = 0; j < 9 - dlevel / 3; j++) + objects[first + j].oc_prob = 0; + first += j; + if (first >= LAST_GEM || first >= SIZE(objects) || + objects[first].oc_olet != GEM_SYM || + objects[first].oc_name == NULL) + printf("Not enough gems? - first=%d j=%d LAST_GEM=%d\n", + first, j, LAST_GEM); + for (j = first; j < LAST_GEM; j++) + objects[j].oc_prob = (20 + j - first) / (LAST_GEM - first); +} + +void +oinit(void) /* level dependent initialization */ +{ + setgemprobs(); +} + +void +savenames(int fd) +{ + int i; + unsigned len; + + bwrite(fd, (char *)bases, sizeof(bases)); + bwrite(fd, (char *)objects, sizeof(objects)); + /* + * as long as we use only one version of Hack/Quest we need not save + * oc_name and oc_descr, but we must save oc_uname for all objects + */ + for (i = 0; i < SIZE(objects); i++) { + if (objects[i].oc_uname) { + len = strlen(objects[i].oc_uname) + 1; + bwrite(fd, (char *)&len, sizeof(len)); + bwrite(fd, objects[i].oc_uname, len); + } + } +} + +void +restnames(int fd) +{ + int i; + unsigned len; + + mread(fd, (char *)bases, sizeof(bases)); + mread(fd, (char *)objects, sizeof(objects)); + for (i = 0; i < SIZE(objects); i++) + if (objects[i].oc_uname) { + mread(fd, (char *)&len, sizeof(len)); + objects[i].oc_uname = alloc(len); + mread(fd, objects[i].oc_uname, len); + } +} + +int +dodiscovered(void) /* free after Robert Viduya */ +{ + int i, end; + int ct = 0; + + cornline(0, "Discoveries"); + + end = SIZE(objects); + for (i = 0; i < end; i++) { + if (interesting_to_discover(i)) { + ct++; + cornline(1, typename(i)); + } + } + if (ct == 0) { + pline("You haven't discovered anything yet..."); + cornline(3, NULL); + } else + cornline(2, NULL); + + return (0); +} + +static bool +interesting_to_discover(int i) +{ + return ( + objects[i].oc_uname != NULL || + (objects[i].oc_name_known && objects[i].oc_descr != NULL) + ); +} diff --git a/hack/hack.objnam.c b/hack/hack.objnam.c new file mode 100644 index 0000000..2dc2397 --- /dev/null +++ b/hack/hack.objnam.c @@ -0,0 +1,584 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.objnam.c - version 1.0.2 */ +/* $FreeBSD: src/games/hack/hack.objnam.c,v 1.3 1999/11/16 02:57:08 billf Exp $ */ + +#include "hack.h" +#define Sprintf (void) sprintf +#define Strcat (void) strcat +#define Strcpy (void) strcpy +#define PREFIX 15 +extern int bases[]; + +static char *strprepend(char *, char *); +static char *sitoa(int); + +static char * +strprepend(char *s, char *pref) +{ + int i = strlen(pref); + + if (i > PREFIX) { + pline("WARNING: prefix too short."); + return (s); + } + s -= i; + strncpy(s, pref, i); /* do not copy trailing 0 */ + return (s); +} + +static char * +sitoa(int a) +{ + static char buf[13]; + + Sprintf(buf, (a < 0) ? "%d" : "+%d", a); + return (buf); +} + +char * +typename(int otyp) +{ + static char buf[BUFSZ]; + struct objclass *ocl = &objects[otyp]; + const char *an = ocl->oc_name; + const char *dn = ocl->oc_descr; + char *un = ocl->oc_uname; + int nn = ocl->oc_name_known; + + switch (ocl->oc_olet) { + case POTION_SYM: + Strcpy(buf, "potion"); + break; + case SCROLL_SYM: + Strcpy(buf, "scroll"); + break; + case WAND_SYM: + Strcpy(buf, "wand"); + break; + case RING_SYM: + Strcpy(buf, "ring"); + break; + default: + if (nn) { + Strcpy(buf, an); + if (otyp >= TURQUOISE && otyp <= JADE) + Strcat(buf, " stone"); + if (un) + Sprintf(eos(buf), " called %s", un); + if (dn) + Sprintf(eos(buf), " (%s)", dn); + } else { + Strcpy(buf, dn ? dn : an); + if (ocl->oc_olet == GEM_SYM) + Strcat(buf, " gem"); + if (un) + Sprintf(eos(buf), " called %s", un); + } + return (buf); + } + /* here for ring/scroll/potion/wand */ + if (nn) + Sprintf(eos(buf), " of %s", an); + if (un) + Sprintf(eos(buf), " called %s", un); + if (dn) + Sprintf(eos(buf), " (%s)", dn); + return (buf); +} + +char * +xname(struct obj *obj) +{ + static char bufr[BUFSZ]; + /* caution: doname() and aobjnam() below "know" these sizes */ + char *buf = &(bufr[PREFIX]); /* leave room for "17 -3 " */ + int nn = objects[obj->otyp].oc_name_known; + const char *an = objects[obj->otyp].oc_name; + const char *dn = objects[obj->otyp].oc_descr; + char *un = objects[obj->otyp].oc_uname; + int pl = (obj->quan != 1); + + if (!obj->dknown && !Blind) /* %% doesnt belong here */ + obj->dknown = 1; + switch (obj->olet) { + case AMULET_SYM: + Strcpy(buf, (obj->spe < 0 && obj->known) + ? "cheap plastic imitation of the " : ""); + Strcat(buf, "Amulet of Yendor"); + break; + case TOOL_SYM: + if (!nn) { + Strcpy(buf, dn); + break; + } + Strcpy(buf, an); + break; + case FOOD_SYM: + if (obj->otyp == DEAD_HOMUNCULUS && pl) { + pl = 0; + Strcpy(buf, "dead homunculi"); + break; + } + /* fungis ? */ + /* fall into next case */ + case WEAPON_SYM: + if (obj->otyp == WORM_TOOTH && pl) { + pl = 0; + Strcpy(buf, "worm teeth"); + break; + } + if (obj->otyp == CRYSKNIFE && pl) { + pl = 0; + Strcpy(buf, "crysknives"); + break; + } + /* fall into next case */ + case ARMOR_SYM: + case CHAIN_SYM: + case ROCK_SYM: + Strcpy(buf, an); + break; + case BALL_SYM: + Sprintf(buf, "%sheavy iron ball", + (obj->owt > objects[obj->otyp].oc_weight) ? "very " : ""); + break; + case POTION_SYM: + if (nn || un || !obj->dknown) { + Strcpy(buf, "potion"); + if (pl) { + pl = 0; + Strcat(buf, "s"); + } + if (!obj->dknown) + break; + if (un) { + Strcat(buf, " called "); + Strcat(buf, un); + } else { + Strcat(buf, " of "); + Strcat(buf, an); + } + } else { + Strcpy(buf, dn); + Strcat(buf, " potion"); + } + break; + case SCROLL_SYM: + Strcpy(buf, "scroll"); + if (pl) { + pl = 0; + Strcat(buf, "s"); + } + if (!obj->dknown) + break; + if (nn) { + Strcat(buf, " of "); + Strcat(buf, an); + } else if (un) { + Strcat(buf, " called "); + Strcat(buf, un); + } else { + Strcat(buf, " labeled "); + Strcat(buf, dn); + } + break; + case WAND_SYM: + if (!obj->dknown) + Sprintf(buf, "wand"); + else if (nn) + Sprintf(buf, "wand of %s", an); + else if (un) + Sprintf(buf, "wand called %s", un); + else + Sprintf(buf, "%s wand", dn); + break; + case RING_SYM: + if (!obj->dknown) + Sprintf(buf, "ring"); + else if (nn) + Sprintf(buf, "ring of %s", an); + else if (un) + Sprintf(buf, "ring called %s", un); + else + Sprintf(buf, "%s ring", dn); + break; + case GEM_SYM: + if (!obj->dknown) { + Strcpy(buf, "gem"); + break; + } + if (!nn) { + Sprintf(buf, "%s gem", dn); + break; + } + Strcpy(buf, an); + if (obj->otyp >= TURQUOISE && obj->otyp <= JADE) + Strcat(buf, " stone"); + break; + default: + Sprintf(buf, "glorkum %c (0%o) %u %d", + obj->olet, obj->olet, obj->otyp, obj->spe); + } + if (pl) { + char *p; + + for (p = buf; *p; p++) + if (!strncmp(" of ", p, 4)) { + /* pieces of, cloves of, lumps of */ + int c1, c2 = 's'; + + do { + c1 = c2; + c2 = *p; + *p++ = c1; + } while (c1); + goto nopl; + } + p = eos(buf) - 1; + if (*p == 's' || *p == 'z' || *p == 'x' || + (*p == 'h' && p[-1] == 's')) + Strcat(buf, "es"); /* boxes */ + else if (*p == 'y' && !strchr(vowels, p[-1])) + Strcpy(p, "ies"); /* rubies, zruties */ + else + Strcat(buf, "s"); + } +nopl: + if (obj->onamelth) { + Strcat(buf, " named "); + Strcat(buf, ONAME(obj)); + } + return (buf); +} + +char * +doname(struct obj *obj) +{ + char prefix[PREFIX]; + char *bp = xname(obj); + + if (obj->quan != 1) + Sprintf(prefix, "%u ", obj->quan); + else + Strcpy(prefix, "a "); + switch (obj->olet) { + case AMULET_SYM: + if (strncmp(bp, "cheap ", 6)) + Strcpy(prefix, "the "); + break; + case ARMOR_SYM: + if (obj->owornmask & W_ARMOR) + Strcat(bp, " (being worn)"); + /* fall into next case */ + case WEAPON_SYM: + if (obj->known) { + Strcat(prefix, sitoa(obj->spe)); + Strcat(prefix, " "); + } + break; + case WAND_SYM: + if (obj->known) + Sprintf(eos(bp), " (%d)", obj->spe); + break; + case RING_SYM: + if (obj->owornmask & W_RINGR) + Strcat(bp, " (on right hand)"); + if (obj->owornmask & W_RINGL) + Strcat(bp, " (on left hand)"); + if (obj->known && (objects[obj->otyp].bits & SPEC)) { + Strcat(prefix, sitoa(obj->spe)); + Strcat(prefix, " "); + } + break; + } + if (obj->owornmask & W_WEP) + Strcat(bp, " (weapon in hand)"); + if (obj->unpaid) + Strcat(bp, " (unpaid)"); + if (!strcmp(prefix, "a ") && strchr(vowels, *bp)) + Strcpy(prefix, "an "); + bp = strprepend(bp, prefix); + return (bp); +} + +/* used only in hack.fight.c (thitu) */ +void +setan(const char *str, char *buf) +{ + if (strchr(vowels, *str)) + Sprintf(buf, "an %s", str); + else + Sprintf(buf, "a %s", str); +} + +char * +aobjnam(struct obj *otmp, const char *verb) +{ + char *bp = xname(otmp); + char prefix[PREFIX]; + + if (otmp->quan != 1) { + Sprintf(prefix, "%u ", otmp->quan); + bp = strprepend(bp, prefix); + } + + if (verb) { + /* verb is given in plural (i.e., without trailing s) */ + Strcat(bp, " "); + if (otmp->quan != 1) + Strcat(bp, verb); + else if (!strcmp(verb, "are")) + Strcat(bp, "is"); + else { + Strcat(bp, verb); + Strcat(bp, "s"); + } + } + return (bp); +} + +char * +Doname(struct obj *obj) +{ + char *s = doname(obj); + + if ('a' <= *s && *s <= 'z') + *s -= ('a' - 'A'); + return (s); +} + +static const char *wrp[] = { "wand", "ring", "potion", "scroll", "gem" }; +char wrpsym[] = { WAND_SYM, RING_SYM, POTION_SYM, SCROLL_SYM, GEM_SYM }; + +struct obj * +readobjnam(char *bp) +{ + char *p; + int i; + int cnt, spe, spesgn, typ, heavy; + char let; + char *un, *dn, *an; + + cnt = spe = spesgn = typ = heavy = 0; + let = 0; + an = dn = un = NULL; + for (p = bp; *p; p++) + if ('A' <= *p && *p <= 'Z') + *p += 'a' - 'A'; + if (!strncmp(bp, "the ", 4)) + bp += 4; + else if (!strncmp(bp, "an ", 3)) { + cnt = 1; + bp += 3; + } else if (!strncmp(bp, "a ", 2)) { + cnt = 1; + bp += 2; + } + if (!cnt && digit(*bp)) { + cnt = atoi(bp); + while (digit(*bp)) + bp++; + while (*bp == ' ') + bp++; + } + if (!cnt) /* %% what with "gems" etc. ? */ + cnt = 1; + + if (*bp == '+' || *bp == '-') { + spesgn = (*bp++ == '+') ? 1 : -1; + spe = atoi(bp); + while (digit(*bp)) + bp++; + while (*bp == ' ') + bp++; + } else { + p = strrchr(bp, '('); + if (p) { + if (p > bp && p[-1] == ' ') + p[-1] = 0; + else + *p = 0; + p++; + spe = atoi(p); + while (digit(*p)) + p++; + if (strcmp(p, ")")) + spe = 0; + else + spesgn = 1; + } + } + /* + * now we have the actual name, as delivered by xname, say + * green potions called whisky + * scrolls labeled "QWERTY" + * egg + * dead zruties + * fortune cookies + * very heavy iron ball named hoei + * wand of wishing + * elven cloak + */ + for (p = bp; *p; p++) + if (!strncmp(p, " named ", 7)) + *p = 0; + + for (p = bp; *p; p++) + if (!strncmp(p, " called ", 8)) { + *p = 0; + un = p + 8; + } + for (p = bp; *p; p++) + if (!strncmp(p, " labeled ", 9)) { + *p = 0; + dn = p + 9; + } + + /* first change to singular if necessary */ + if (cnt != 1) { + /* find "cloves of garlic", "worthless pieces of blue glass" */ + for (p = bp; *p; p++) + if (!strncmp(p, "s of ", 5)) { + while ((*p = p[1])) + p++; + goto sing; + } + /* remove -s or -es (boxes) or -ies (rubies, zruties) */ + p = eos(bp); + if (p[-1] == 's') { + if (p[-2] == 'e') { + if (p[-3] == 'i') { + if (!strcmp(p - 7, "cookies")) + goto mins; + Strcpy(p - 3, "y"); + goto sing; + } + + /* note: cloves / knives from clove / knife */ + if (!strcmp(p - 6, "knives")) { + Strcpy(p - 3, "fe"); + goto sing; + } + + /* note: nurses, axes but boxes */ + if (!strcmp(p - 5, "boxes")) { + p[-2] = 0; + goto sing; + } + } +mins: + p[-1] = 0; + } else { + if (!strcmp(p - 9, "homunculi")) { + Strcpy(p - 1, "us"); /* !! makes string longer */ + goto sing; + } + if (!strcmp(p - 5, "teeth")) { + Strcpy(p - 5, "tooth"); + goto sing; + } + /* here we cannot find the plural suffix */ + } + } +sing: + if (!strcmp(bp, "amulet of yendor")) { + typ = AMULET_OF_YENDOR; + goto typfnd; + } + p = eos(bp); + if (!strcmp(p - 5, " mail")) { /* Note: ring mail is not a ring ! */ + let = ARMOR_SYM; + an = bp; + goto srch; + } + for (i = 0; (unsigned)i < sizeof(wrpsym); i++) { + int j = strlen(wrp[i]); + if (!strncmp(bp, wrp[i], j)) { + let = wrpsym[i]; + bp += j; + if (!strncmp(bp, " of ", 4)) + an = bp + 4; + /* else if (*bp) ?? */ + goto srch; + } + if (!strcmp(p - j, wrp[i])) { + let = wrpsym[i]; + p -= j; + *p = 0; + if (p[-1] == ' ') + p[-1] = 0; + dn = bp; + goto srch; + } + } + if (!strcmp(p - 6, " stone")) { + p[-6] = 0; + let = GEM_SYM; + an = bp; + goto srch; + } + if (!strcmp(bp, "very heavy iron ball")) { + heavy = 1; + typ = HEAVY_IRON_BALL; + goto typfnd; + } + an = bp; +srch: + if (!an && !dn && !un) + goto any; + i = 1; + if (let) + i = bases[letindex(let)]; + while (i <= NROFOBJECTS && (!let || objects[i].oc_olet == let)) { + const char *zn = objects[i].oc_name; + + if (!zn) + goto nxti; + if (an && strcmp(an, zn)) + goto nxti; + if (dn && (!(zn = objects[i].oc_descr) || strcmp(dn, zn))) + goto nxti; + if (un && (!(zn = objects[i].oc_uname) || strcmp(un, zn))) + goto nxti; + typ = i; + goto typfnd; +nxti: + i++; + } +any: + if (!let) + let = wrpsym[rn2(sizeof(wrpsym))]; + typ = probtype(let); +typfnd: + { + struct obj *otmp; + let = objects[typ].oc_olet; + otmp = mksobj(typ); + if (heavy) + otmp->owt += 15; + if (cnt > 0 && strchr("%?!*)", let) && + (cnt < 4 || (let == WEAPON_SYM && typ <= ROCK && cnt < 20))) + otmp->quan = cnt; + + if (spe > 3 && spe > otmp->spe) + spe = 0; + else if (let == WAND_SYM) + spe = otmp->spe; + if (spe == 3 && u.uluck < 0) + spesgn = -1; + if (let != WAND_SYM && spesgn == -1) + spe = -spe; + if (let == BALL_SYM) + spe = 0; + else if (let == AMULET_SYM) + spe = -1; + else if (typ == WAN_WISHING && rn2(10)) + spe = (rn2(10) ? -1 : 0); + otmp->spe = spe; + + if (spesgn == -1) + otmp->cursed = 1; + + return (otmp); + } +} diff --git a/hack/hack.options.c b/hack/hack.options.c new file mode 100644 index 0000000..63a8c2a --- /dev/null +++ b/hack/hack.options.c @@ -0,0 +1,217 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.options.c - version 1.0.3 */ +/* $FreeBSD: src/games/hack/hack.options.c,v 1.5 1999/11/16 02:57:08 billf Exp $ */ + +#include "hack.h" + +static void parseoptions(char *, bool); + +void +initoptions(void) +{ + char *opts; + + flags.time = flags.nonews = flags.notombstone = flags.end_own = + flags.standout = flags.nonull = FALSE; + flags.no_rest_on_space = TRUE; + flags.invlet_constant = TRUE; + flags.end_top = 5; + flags.end_around = 4; + flags.female = FALSE; /* players are usually male */ + + if ((opts = getenv("HACKOPTIONS"))) + parseoptions(opts,TRUE); +} + +static void +parseoptions(char *opts, bool from_env) +{ + char *op, *op2; + unsigned num; + boolean negated; + + if ((op = strchr(opts, ',')) != NULL) { + *op++ = 0; + parseoptions(op, from_env); + } + if ((op = strchr(opts, ' ')) != NULL) { + op2 = op; + while (*op++) + if (*op != ' ') + *op2++ = *op; + } + if (!*opts) + return; + negated = FALSE; + while ((*opts == '!') || !strncmp(opts, "no", 2)) { + if (*opts == '!') + opts++; + else + opts += 2; + negated = !negated; + } + + if (!strncmp(opts, "standout", 8)) { + flags.standout = !negated; + return; + } + + if (!strncmp(opts, "null", 3)) { + flags.nonull = negated; + return; + } + + if (!strncmp(opts, "tombstone", 4)) { + flags.notombstone = negated; + return; + } + + if (!strncmp(opts, "news", 4)) { + flags.nonews = negated; + return; + } + + if (!strncmp(opts, "time", 4)) { + flags.time = !negated; + flags.botl = 1; + return; + } + + if (!strncmp(opts, "restonspace", 4)) { + flags.no_rest_on_space = negated; + return; + } + + if (!strncmp(opts, "fixinv", 4)) { + if (from_env) + flags.invlet_constant = !negated; + else + pline("The fixinvlet option must be in HACKOPTIONS."); + return; + } + + if (!strncmp(opts, "male", 4)) { + flags.female = negated; + return; + } + if (!strncmp(opts, "female", 6)) { + flags.female = !negated; + return; + } + + /* name:string */ + if (!strncmp(opts, "name", 4)) { + if (!from_env) { + pline("The playername can be set only from HACKOPTIONS."); + return; + } + op = strchr(opts, ':'); + if (!op) + goto bad; + strncpy(plname, op + 1, sizeof(plname) - 1); + return; + } + + /* endgame:5t[op] 5a[round] o[wn] */ + if (!strncmp(opts, "endgame", 3)) { + op = strchr(opts, ':'); + if (!op) + goto bad; + op++; + while (*op) { + num = 1; + if (digit(*op)) { + num = atoi(op); + while (digit(*op)) + op++; + } else if (*op == '!') { + negated = !negated; + op++; + } + switch (*op) { + case 't': + flags.end_top = num; + break; + case 'a': + flags.end_around = num; + break; + case 'o': + flags.end_own = !negated; + break; + default: + goto bad; + } + while (letter(*++op)) + ; /* nothing */ + if (*op == '/') + op++; + } + return; + } +bad: + if (!from_env) { + if (!strncmp(opts, "help", 4)) { + pline("%s%s%s", + "To set options use `HACKOPTIONS=\"<options>\"' in your environment, or ", + "give the command 'o' followed by the line `<options>' while playing. ", + "Here <options> is a list of <option>s separated by commas."); + pline("%s%s%s", + "Simple (boolean) options are rest_on_space, news, time, ", + "null, tombstone, (fe)male. ", + "These can be negated by prefixing them with '!' or \"no\"."); + pline("%s", + "A string option is name, as in HACKOPTIONS=\"name:Merlin-W\"."); + pline("%s%s%s", + "A compound option is endgame; it is followed by a description of what ", + "parts of the scorelist you want to see. You might for example say: ", + "`endgame:own scores/5 top scores/4 around my score'."); + return; + } + pline("Bad option: %s.", opts); + pline("Type `o help<cr>' for help."); + return; + } + puts("Bad syntax in HACKOPTIONS."); + puts("Use for example:"); + puts("HACKOPTIONS=\"!restonspace,notombstone,endgame:own/5 topscorers/4 around me\""); + getret(); +} + +int +doset(void) +{ + char buf[BUFSZ]; + + pline("What options do you want to set? "); + getlin(buf); + if (!buf[0] || buf[0] == '\033') { + strcpy(buf, "HACKOPTIONS="); + strcat(buf, flags.female ? "female," : "male,"); + if (flags.standout) + strcat(buf, "standout,"); + if (flags.nonull) + strcat(buf, "nonull,"); + if (flags.nonews) + strcat(buf, "nonews,"); + if (flags.time) + strcat(buf, "time,"); + if (flags.notombstone) + strcat(buf, "notombstone,"); + if (flags.no_rest_on_space) + strcat(buf, "!rest_on_space,"); + if (flags.end_top != 5 || flags.end_around != 4 || flags.end_own) { + sprintf(eos(buf), "endgame: %u topscores/%u around me", + flags.end_top, flags.end_around); + if (flags.end_own) + strcat(buf, "/own scores"); + } else { + char *eop = eos(buf); + if (*--eop == ',') + *eop = 0; + } + pline("%s", buf); + } else + parseoptions(buf, FALSE); + + return (0); +} diff --git a/hack/hack.pager.c b/hack/hack.pager.c new file mode 100644 index 0000000..0705233 --- /dev/null +++ b/hack/hack.pager.c @@ -0,0 +1,418 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.pager.c - version 1.0.3 */ +/* $FreeBSD: src/games/hack/hack.pager.c,v 1.7 1999/11/16 02:57:09 billf Exp $ */ + +/* This file contains the command routine dowhatis() and a pager. */ +/* + * Also readmail() and doshell(), and generally the things that contact the + * outside world. + */ + +#include <sys/types.h> +#include <signal.h> +#include "hack.h" +extern int CO, LI; /* usually COLNO and ROWNO+2 */ +extern char *CD; +extern char quitchars[]; + +static void intruph(int); +static void page_more(FILE *, int); + +int +dowhatis(void) +{ + FILE *fp; + char bufr[BUFSZ + 6]; + char *buf = &bufr[6], *ep, q; + + if (!(fp = fopen(DATAFILE, "r"))) + pline("Cannot open data file!"); + else { + pline("Specify what? "); + q = readchar(); + if (q != '\t') + while (fgets(buf, BUFSZ, fp)) + if (*buf == q) { + ep = strchr(buf, '\n'); + if (ep) + *ep = 0; + /* else: bad data file */ + /* Expand tab 'by hand' */ + if (buf[1] == '\t') { + buf = bufr; + buf[0] = q; + strncpy(buf + 1, " ", 7); + } + pline("%s", buf); + if (ep[-1] == ';') { + pline("More info? "); + if (readchar() == 'y') { + page_more(fp, 1); /* does fclose() */ + return (0); + } + } + fclose(fp); /* kopper@psuvax1 */ + return (0); + } + pline("I've never heard of such things."); + fclose(fp); + } + return (0); +} + +/* make the paging of a file interruptible */ +static int got_intrup; + +static void +intruph(int unused __attribute__((unused))) +{ + got_intrup++; +} + +/* simple pager, also used from dohelp() */ +/* strip: nr of chars to be stripped from each line (0 or 1) */ +static void +page_more(FILE *fp, int strip) +{ + char *bufr, *ep; + sig_t prevsig = signal(SIGINT, intruph); + + set_pager(0); + bufr = alloc((unsigned)CO); + bufr[CO - 1] = 0; + while (fgets(bufr, CO - 1, fp) && (!strip || *bufr == '\t') && !got_intrup) { + ep = strchr(bufr, '\n'); + if (ep) + *ep = 0; + if (page_line(bufr + strip)) { + set_pager(2); + goto ret; + } + } + set_pager(1); +ret: + free(bufr); + fclose(fp); + signal(SIGINT, prevsig); + got_intrup = 0; +} + +static boolean whole_screen = TRUE; +#define PAGMIN 12 /* minimum # of lines for page below level map */ + +void +set_whole_screen(void) /* called in termcap as soon as LI is known */ +{ + whole_screen = (LI - ROWNO - 2 <= PAGMIN || !CD); +} + +#ifdef NEWS +bool +readnews(void) +{ + int ret; + + whole_screen = TRUE; /* force a docrt(), our first */ + ret = page_file(NEWS, TRUE); + set_whole_screen(); + return (ret); /* report whether we did docrt() */ +} +#endif /* NEWS */ + +void +set_pager(int mode) /* 0: open 1: wait+close 2: close */ +{ + static boolean so; + + if (mode == 0) { + if (!whole_screen) { + /* clear topline */ + clrlin(); + /* use part of screen below level map */ + curs(1, ROWNO + 4); + } else { + cls(); + } + so = flags.standout; + flags.standout = 1; + } else { + if (mode == 1) { + curs(1, LI); + more(); + } + flags.standout = so; + if (whole_screen) + docrt(); + else { + curs(1, ROWNO + 4); + cl_eos(); + } + } +} + +bool +page_line(const char *s) /* returns 1 if we should quit */ +{ + if (cury == LI - 1) { + if (!*s) + return (0); /* suppress blank lines at top */ + putchar('\n'); + cury++; + cmore("q\033"); + if (morc) { + morc = 0; + return (1); + } + if (whole_screen) + cls(); + else { + curs(1, ROWNO + 4); + cl_eos(); + } + } + puts(s); + cury++; + return (0); +} + +/* + * Flexible pager: feed it with a number of lines and it will decide + * whether these should be fed to the pager above, or displayed in a + * corner. + * Call: + * cornline(0, title or 0) : initialize + * cornline(1, text) : add text to the chain of texts + * cornline(2, morcs) : output everything and cleanup + * cornline(3, 0) : cleanup + */ + +void +cornline(int mode, const char *text) +{ + static struct line { + struct line *next_line; + char *line_text; + } *texthead, *texttail; + static int maxlen; + static int linect; + struct line *tl; + + if (mode == 0) { + texthead = NULL; + maxlen = 0; + linect = 0; + if (text) { + cornline(1, text); /* title */ + cornline(1, ""); /* blank line */ + } + return; + } + + if (mode == 1) { + int len; + + if (!text) /* superfluous, just to be sure */ + return; + linect++; + len = strlen(text); + if (len > maxlen) + maxlen = len; + tl = alloc((unsigned)(len + sizeof(struct line) + 1)); + tl->next_line = NULL; + tl->line_text = (char *)(tl + 1); + strcpy(tl->line_text, text); + if (!texthead) + texthead = tl; + else + texttail->next_line = tl; + texttail = tl; + return; + } + + /* --- now we really do it --- */ + if (mode == 2 && linect == 1) /* topline only */ + pline("%s", texthead->line_text); + else if (mode == 2) { + int curline, lth; + + if (flags.toplin == 1) /* ab@unido */ + more(); + remember_topl(); + + lth = CO - maxlen - 2; /* Use full screen width */ + if (linect < LI && lth >= 10) { /* in a corner */ + home(); + cl_end(); + flags.toplin = 0; + curline = 1; + for (tl = texthead; tl; tl = tl->next_line) { + curs(lth, curline); + if (curline > 1) + cl_end(); + putsym(' '); + putstr(tl->line_text); + curline++; + } + curs(lth, curline); + cl_end(); + cmore(text); + home(); + cl_end(); + docorner(lth, curline - 1); + } else { /* feed to pager */ + set_pager(0); + for (tl = texthead; tl; tl = tl->next_line) { + if (page_line(tl->line_text)) { + set_pager(2); + goto cleanup; + } + } + if (text) { + cgetret(text); + set_pager(2); + } else + set_pager(1); + } + } + +cleanup: + while ((tl = texthead) != NULL) { + texthead = tl->next_line; + free(tl); + } +} + +int +dohelp(void) +{ + char c; + + pline("Long or short help? "); + while (((c = readchar()) != 'l') && (c != 's') && !strchr(quitchars, c)) + bell(); + if (!strchr(quitchars, c)) + page_file((c == 'l') ? HELP : SHELP, FALSE); + return (0); +} + +/* return: 0 - cannot open fnam; 1 - otherwise */ +bool +page_file(const char *fnam, bool silent) +{ +#ifdef DEF_PAGER /* this implies that UNIX is defined */ + /* use external pager; this may give security problems */ + int fd = open(fnam, O_RDONLY); + + if (fd < 0) { + if (!silent) + pline("Cannot open %s.", fnam); + return (0); + } + if (child(1)) { + extern char *catmore; + + /* + * Now that child() does a setuid(getuid()) and a + * chdir(), we may not be able to open file fnam + * anymore, so make it stdin. + */ + close(0); + if (dup(fd)) { + if (!silent) + printf("Cannot open %s as stdin.\n", fnam); + } else { + execl(catmore, "page", NULL); + if (!silent) + printf("Cannot exec %s.\n", catmore); + } + exit(1); + } + close(fd); +#else /* DEF_PAGER */ + FILE *f; /* free after Robert Viduya */ + + if ((f = fopen(fnam, "r")) == NULL) { + if (!silent) { + home(); + perror(fnam); + flags.toplin = 1; + pline("Cannot open %s.", fnam); + } + return (0); + } + page_more(f, 0); +#endif /* DEF_PAGER */ + + return (1); +} + +#ifdef UNIX +#ifdef SHELL +int +dosh(void) +{ + char *str; + + if (child(0)) { + if ((str = getenv("SHELL")) != NULL) + execl(str, str, NULL); + else + execl("/bin/sh", "sh", NULL); + pline("sh: cannot execute."); + exit(1); + } + return (0); +} +#endif /* SHELL */ + +#ifdef NOWAITINCLUDE +union wait { /* used only for the cast (union wait *)0 */ + int w_status; + struct { + unsigned short w_Termsig:7; + unsigned short w_Coredump:1; + unsigned short w_Retcode:8; + } w_T; +}; + +#else +#include <sys/wait.h> +#endif /* NOWAITINCLUDE */ + +bool +child(bool wt) +{ + int status; + int f; + + f = fork(); + if (f == 0) { /* child */ + settty(NULL); /* also calls end_screen() */ + /* revoke */ + setgid(getgid()); +#ifdef CHDIR + chdir(getenv("HOME")); +#endif /* CHDIR */ + return (1); + } + if (f == -1) { /* cannot fork */ + pline("Fork failed. Try again."); + return (0); + } + /* fork succeeded; wait for child to exit */ + signal(SIGINT, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + wait(&status); + gettty(); + setftty(); + signal(SIGINT, done1); +#ifdef WIZARD + if (wizard) + signal(SIGQUIT, SIG_DFL); +#endif /* WIZARD */ + if (wt) + getret(); + docrt(); + return (0); +} +#endif /* UNIX */ diff --git a/hack/hack.potion.c b/hack/hack.potion.c new file mode 100644 index 0000000..e7879db --- /dev/null +++ b/hack/hack.potion.c @@ -0,0 +1,407 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.potion.c - version 1.0.3 */ +/* $FreeBSD: src/games/hack/hack.potion.c,v 1.5 1999/11/16 10:26:37 marcel Exp $ */ + +#include "hack.h" +extern struct monst youmonst; + +static void ghost_from_bottle(void); + +int +dodrink(void) +{ + struct obj *otmp, *objs; + struct monst *mtmp; + int unkn = 0, nothing = 0; + + otmp = getobj("!", "drink"); + if (!otmp) + return (0); + if (!strcmp(objects[otmp->otyp].oc_descr, "smoky") && !rn2(13)) { + ghost_from_bottle(); + goto use_it; + } + switch (otmp->otyp) { + case POT_RESTORE_STRENGTH: + unkn++; + pline("Wow! This makes you feel great!"); + if (u.ustr < u.ustrmax) { + u.ustr = u.ustrmax; + flags.botl = 1; + } + break; + case POT_BOOZE: + unkn++; + pline("Ooph! This tastes like liquid fire!"); + Confusion += d(3, 8); + /* the whiskey makes us feel better */ + if (u.uhp < u.uhpmax) + losehp(-1, "bottle of whiskey"); + if (!rn2(4)) { + pline("You pass out."); + multi = -rnd(15); + nomovemsg = "You awake with a headache."; + } + break; + case POT_INVISIBILITY: + if (Invis || See_invisible) + nothing++; + else { + if (!Blind) + pline("Gee! All of a sudden, you can't see yourself."); + else + pline("You feel rather airy."), unkn++; + newsym(u.ux, u.uy); + } + Invis += rn1(15, 31); + break; + case POT_FRUIT_JUICE: + pline("This tastes like fruit juice."); + lesshungry(20); + break; + case POT_HEALING: + pline("You begin to feel better."); + flags.botl = 1; + u.uhp += rnd(10); + if (u.uhp > u.uhpmax) + u.uhp = ++u.uhpmax; + if (Blind) /* see on next move */ + Blind = 1; + if (Sick) + Sick = 0; + break; + case POT_PARALYSIS: + if (Levitation) + pline("You are motionlessly suspended."); + else + pline("Your feet are frozen to the floor!"); + nomul(-(rn1(10, 25))); + break; + case POT_MONSTER_DETECTION: + if (!fmon) { + strange_feeling(otmp, "You feel threatened."); + return (1); + } else { + cls(); + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) + if (mtmp->mx > 0) + at(mtmp->mx, mtmp->my, mtmp->data->mlet); + prme(); + pline("You sense the presence of monsters."); + more(); + docrt(); + } + break; + case POT_OBJECT_DETECTION: + if (!fobj) { + strange_feeling(otmp, "You feel a pull downward."); + return (1); + } else { + for (objs = fobj; objs; objs = objs->nobj) + if (objs->ox != u.ux || objs->oy != u.uy) + goto outobjmap; + pline("You sense the presence of objects close nearby."); + break; +outobjmap: + cls(); + for (objs = fobj; objs; objs = objs->nobj) + at(objs->ox, objs->oy, objs->olet); + prme(); + pline("You sense the presence of objects."); + more(); + docrt(); + } + break; + case POT_SICKNESS: + pline("Yech! This stuff tastes like poison."); + if (Poison_resistance) + pline("(But in fact it was biologically contaminated orange juice.)"); + losestr(rn1(4, 3)); + losehp(rnd(10), "contaminated potion"); + break; + case POT_CONFUSION: + if (!Confusion) + pline("Huh, What? Where am I?"); + else + nothing++; + Confusion += rn1(7, 16); + break; + case POT_GAIN_STRENGTH: + pline("Wow do you feel strong!"); + if (u.ustr >= 118) /* > 118 is impossible */ + break; + if (u.ustr > 17) + u.ustr += rnd(118 - u.ustr); + else + u.ustr++; + if (u.ustr > u.ustrmax) + u.ustrmax = u.ustr; + flags.botl = 1; + break; + case POT_SPEED: + if (Wounded_legs) { + heal_legs(); + unkn++; + break; + } + if (!(Fast & ~INTRINSIC)) + pline("You are suddenly moving much faster."); + else + pline("Your legs get new energy."), unkn++; + Fast += rn1(10, 100); + break; + case POT_BLINDNESS: + if (!Blind) + pline("A cloud of darkness falls upon you."); + else + nothing++; + Blind += rn1(100, 250); + seeoff(0); + break; + case POT_GAIN_LEVEL: + pluslvl(); + break; + case POT_EXTRA_HEALING: + pline("You feel much better."); + flags.botl = 1; + u.uhp += d(2, 20) + 1; + if (u.uhp > u.uhpmax) + u.uhp = (u.uhpmax += 2); + if (Blind) + Blind = 1; + if (Sick) + Sick = 0; + break; + case POT_LEVITATION: + if (!Levitation) + float_up(); + else + nothing++; + Levitation += rnd(100); + u.uprops[PROP(RIN_LEVITATION)].p_tofn = float_down; + break; + default: + impossible("What a funny potion! (%u)", otmp->otyp); + return (0); + } + if (nothing) { + unkn++; + pline("You have a peculiar feeling for a moment, then it passes."); + } + if (otmp->dknown && !objects[otmp->otyp].oc_name_known) { + if (!unkn) { + objects[otmp->otyp].oc_name_known = 1; + more_experienced(0, 10); + } else if (!objects[otmp->otyp].oc_uname) + docall(otmp); + } +use_it: + useup(otmp); + return (1); +} + +void +pluslvl(void) +{ + int num; + + pline("You feel more experienced."); + num = rnd(10); + u.uhpmax += num; + u.uhp += num; + if (u.ulevel < 14) { + u.uexp = newuexp() + 1; + pline("Welcome to experience level %u.", ++u.ulevel); + } + flags.botl = 1; +} + +void +strange_feeling(struct obj *obj, const char *txt) +{ + if (flags.beginner) + pline("You have a strange feeling for a moment, then it passes."); + else + pline("%s", txt); + if (!objects[obj->otyp].oc_name_known && !objects[obj->otyp].oc_uname) + docall(obj); + useup(obj); +} + +static const char *bottlenames[] = { + "bottle", "phial", "flagon", "carafe", "flask", "jar", "vial" +}; + +void +potionhit(struct monst *mon, struct obj *obj) +{ + const char *botlnam = bottlenames[rn2(SIZE(bottlenames))]; + boolean uclose, isyou = (mon == &youmonst); + + if (isyou) { + uclose = TRUE; + pline("The %s crashes on your head and breaks into shivers.", + botlnam); + losehp(rnd(2), "thrown potion"); + } else { + uclose = (dist(mon->mx, mon->my) < 3); + /* perhaps 'E' and 'a' have no head? */ + pline("The %s crashes on %s's head and breaks into shivers.", + botlnam, monnam(mon)); + if (rn2(5) && mon->mhp > 1) + mon->mhp--; + } + pline("The %s evaporates.", xname(obj)); + + if (!isyou && !rn2(3)) + switch (obj->otyp) { + case POT_RESTORE_STRENGTH: + case POT_GAIN_STRENGTH: + case POT_HEALING: + case POT_EXTRA_HEALING: + if (mon->mhp < mon->mhpmax) { + mon->mhp = mon->mhpmax; + pline("%s looks sound and hale again!", Monnam(mon)); + } + break; + case POT_SICKNESS: + if (mon->mhpmax > 3) + mon->mhpmax /= 2; + if (mon->mhp > 2) + mon->mhp /= 2; + break; + case POT_CONFUSION: + case POT_BOOZE: + mon->mconf = 1; + break; + case POT_INVISIBILITY: + unpmon(mon); + mon->minvis = 1; + pmon(mon); + break; + case POT_PARALYSIS: + mon->mfroz = 1; + break; + case POT_SPEED: + mon->mspeed = MFAST; + break; + case POT_BLINDNESS: + mon->mblinded |= 64 + rn2(64); + break; +/* + * case POT_GAIN_LEVEL: + * case POT_LEVITATION: + * case POT_FRUIT_JUICE: + * case POT_MONSTER_DETECTION: + * case POT_OBJECT_DETECTION: + * break; + */ + } + if (uclose && rn2(5)) + potionbreathe(obj); + obfree(obj, NULL); +} + +void +potionbreathe(struct obj *obj) +{ + switch (obj->otyp) { + case POT_RESTORE_STRENGTH: + case POT_GAIN_STRENGTH: + if (u.ustr < u.ustrmax) { + u.ustr++; + flags.botl = 1; + } + break; + case POT_HEALING: + case POT_EXTRA_HEALING: + if (u.uhp < u.uhpmax) { + u.uhp++; + flags.botl = 1; + } + break; + case POT_SICKNESS: + if (u.uhp <= 5) + u.uhp = 1; + else + u.uhp -= 5; + flags.botl = 1; + break; + case POT_CONFUSION: + case POT_BOOZE: + if (!Confusion) + pline("You feel somewhat dizzy."); + Confusion += rnd(5); + break; + case POT_INVISIBILITY: + pline("For an instant you couldn't see your right hand."); + break; + case POT_PARALYSIS: + pline("Something seems to be holding you."); + nomul(-rnd(5)); + break; + case POT_SPEED: + Fast += rnd(5); + pline("Your knees seem more flexible now."); + break; + case POT_BLINDNESS: + if (!Blind) + pline("It suddenly gets dark."); + Blind += rnd(5); + seeoff(0); + break; +/* + * case POT_GAIN_LEVEL: + * case POT_LEVITATION: + * case POT_FRUIT_JUICE: + * case POT_MONSTER_DETECTION: + * case POT_OBJECT_DETECTION: + * break; + */ + } + /* note: no obfree() */ +} + +/* + * -- rudimentary -- to do this correctly requires much more work + * -- all sharp weapons get one or more qualities derived from the potions + * -- texts on scrolls may be (partially) wiped out; do they become blank? + * -- or does their effect change, like under Confusion? + * -- all objects may be made invisible by POT_INVISIBILITY + * -- If the flask is small, can one dip a large object? Does it magically + * -- become a jug? Etc. + */ +int +dodip(void) +{ + struct obj *potion, *obj; + + if (!(obj = getobj("#", "dip"))) + return (0); + if (!(potion = getobj("!", "dip into"))) + return (0); + pline("Interesting..."); + if (obj->otyp == ARROW || obj->otyp == DART || + obj->otyp == CROSSBOW_BOLT) + if (potion->otyp == POT_SICKNESS) { + useup(potion); + if (obj->spe < 7) /* %% */ + obj->spe++; + } + return (1); +} + +static void +ghost_from_bottle(void) +{ + struct monst *mtmp; + + if (!(mtmp = makemon(PM_GHOST, u.ux, u.uy))) { + pline("This bottle turns out to be empty."); + return; + } + mnexto(mtmp); + pline("As you open the bottle, an enormous ghost emerges!"); + pline("You are frightened to death, and unable to move."); + nomul(-3); +} diff --git a/hack/hack.pri.c b/hack/hack.pri.c new file mode 100644 index 0000000..eed95b9 --- /dev/null +++ b/hack/hack.pri.c @@ -0,0 +1,721 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.pri.c - version 1.0.3 */ +/* $FreeBSD: src/games/hack/hack.pri.c,v 1.5 1999/11/16 10:26:37 marcel Exp $ */ +/* $DragonFly: src/games/hack/hack.pri.c,v 1.5 2006/08/21 19:45:32 pavalos Exp $ */ + +#include "hack.h" +#include <curses.h> +xchar scrlx, scrhx, scrly, scrhy; /* corners of new area on screen */ + +extern char *CD; + +#ifdef NEWSCR +static void pobj(struct obj *); +#endif +static void cornbot(int); + +void +swallowed(void) +{ + char ulook[] = { "|@|" }; + + ulook[1] = u.usym; + + cls(); + curs(u.ux - 1, u.uy + 1); + fputs("/-\\", stdout); + curx = u.ux + 2; + curs(u.ux - 1, u.uy + 2); + fputs(ulook, stdout); + curx = u.ux + 2; + curs(u.ux - 1, u.uy + 3); + fputs("\\-/", stdout); + curx = u.ux + 2; + u.udispl = 1; + u.udisx = u.ux; + u.udisy = u.uy; +} + +/* VARARGS1 */ +boolean panicking; + +void +panic(const char *str, ...) +{ + va_list ap; + + if (panicking++) /* avoid loops - this should never happen*/ + exit(1); + home(); + puts(" Suddenly, the dungeon collapses."); + fputs(" ERROR: ", stdout); + va_start(ap, str); + vprintf(str, ap); + va_end(ap); +#ifdef DEBUG +#ifdef UNIX + if (!fork()) + abort(); /* generate core dump */ +#endif /* UNIX */ +#endif /* DEBUG */ + more(); /* contains a fflush() */ + done("panicked"); +} + +void +atl(int x, int y, char ch) +{ + struct rm *crm = &levl[x][y]; + + if (x < 0 || x > COLNO - 1 || y < 0 || y > ROWNO - 1) { + impossible("atl(%d,%d,%c)", x, y, ch); + return; + } + if (crm->seen && crm->scrsym == ch) + return; + crm->scrsym = ch; + crm->new = 1; + on_scr(x, y); +} + +void +on_scr(int x, int y) +{ + if (x < scrlx) + scrlx = x; + if (x > scrhx) + scrhx = x; + if (y < scrly) + scrly = y; + if (y > scrhy) + scrhy = y; +} + +/* call: (x,y) - display + * (-1,0) - close (leave last symbol) + * (-1,-1)- close (undo last symbol) + * (-1,let)-open: initialize symbol + * (-2,let)-change let + */ + +void +tmp_at(schar x, schar y) +{ + static schar prevx, prevy; + static char let; + + if ((int)x == -2) { /* change let call */ + let = y; + return; + } + if ((int)x == -1 && (int)y >= 0) { /* open or close call */ + let = y; + prevx = -1; + return; + } + if (prevx >= 0 && cansee(prevx, prevy)) { +/* delay_output(50); 20150209 bkw: doesn't exist on linux */ + prl(prevx, prevy); /* in case there was a monster */ + at(prevx, prevy, levl[prevx][prevy].scrsym); + } + if (x >= 0) { /* normal call */ + if (cansee(x, y)) + at(x, y, let); + prevx = x; + prevy = y; + } else { /* close call */ + let = 0; + prevx = -1; + } +} + +/* like the previous, but the symbols are first erased on completion */ +void +Tmp_at(schar x, schar y) +{ + static char let; + static xchar cnt; + static coord tc[COLNO]; /* but watch reflecting beams! */ + int xx, yy; + + if ((int)x == -1) { + if (y > 0) { /* open call */ + let = y; + cnt = 0; + return; + } + /* close call (do not distinguish y==0 and y==-1) */ + while (cnt--) { + xx = tc[cnt].x; + yy = tc[cnt].y; + prl(xx, yy); + at(xx, yy, levl[xx][yy].scrsym); + } + cnt = let = 0; /* superfluous */ + return; + } + if ((int)x == -2) { /* change let call */ + let = y; + return; + } + /* normal call */ + if (cansee(x, y)) { + /* if (cnt) + delay_output(50); 20150209 bkw: doesn't exist on linux */ + at(x, y, let); + tc[cnt].x = x; + tc[cnt].y = y; + if (++cnt >= COLNO) + panic("Tmp_at overflow?"); + levl[x][y].new = 0; /* prevent pline-nscr erasing --- */ + } +} + +void +setclipped(void) +{ + error("Hack needs a screen of size at least %d by %d.\n", + ROWNO + 2, COLNO); +} + +void +at(xchar x, xchar y, char ch) +{ +#ifndef lint + /* if xchar is unsigned, lint will complain about if (x < 0) */ + if (x < 0 || x > COLNO - 1 || y < 0 || y > ROWNO - 1) { + impossible("At gets 0%o at %d %d.", ch, x, y); + return; + } +#endif /* lint */ + if (!ch) { + impossible("At gets null at %d %d.", x, y); + return; + } + y += 2; + curs(x, y); + putchar(ch); + curx++; +} + +void +prme(void) +{ + if (!Invisible) + at(u.ux, u.uy, u.usym); +} + +int +doredraw(void) +{ + docrt(); + return (0); +} + +void +docrt(void) +{ + int x, y; + struct rm *room; + struct monst *mtmp; + + if (u.uswallow) { + swallowed(); + return; + } + cls(); + +/* Some ridiculous code to get display of @ and monsters (almost) right */ + if (!Invisible) { + levl[(u.udisx = u.ux)][(u.udisy = u.uy)].scrsym = u.usym; + levl[u.udisx][u.udisy].seen = 1; + u.udispl = 1; + } else + u.udispl = 0; + + seemons(); /* reset old positions */ + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) + mtmp->mdispl = 0; + seemons(); /* force new positions to be shown */ +/* This nonsense should disappear soon --------------------------------- */ + + for (y = 0; y < ROWNO; y++) + for (x = 0; x < COLNO; x++) + if ((room = &levl[x][y])->new) { + room->new = 0; + at(x, y, room->scrsym); + } else if (room->seen) + at(x, y, room->scrsym); + scrlx = COLNO; + scrly = ROWNO; + scrhx = scrhy = 0; + flags.botlx = 1; + bot(); +} + +void +docorner(int xmin, int ymax) +{ + int x, y; + struct rm *room; + struct monst *mtmp; + + if (u.uswallow) { /* Can be done more efficiently */ + swallowed(); + return; + } + seemons(); /* reset old positions */ + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) + if (mtmp->mx >= xmin && mtmp->my < ymax) + mtmp->mdispl = 0; + seemons(); /* force new positions to be shown */ + + for (y = 0; y < ymax; y++) { + if (y > ROWNO && CD) + break; + curs(xmin, y + 2); + cl_end(); + if (y < ROWNO) { + for (x = xmin; x < COLNO; x++) { + if ((room = &levl[x][y])->new) { + room->new = 0; + at(x, y, room->scrsym); + } else if (room->seen) + at(x, y, room->scrsym); + } + } + } + if (ymax > ROWNO) { + cornbot(xmin - 1); + if (ymax > ROWNO + 1 && CD) { + curs(1, ROWNO + 3); + cl_eos(); + } + } +} + +void +curs_on_u(void) +{ + curs(u.ux, u.uy + 2); +} + +void +pru(void) +{ + if (u.udispl && (Invisible || u.udisx != u.ux || u.udisy != u.uy)) + if (!vism_at(u.udisx, u.udisy)) + newsym(u.udisx, u.udisy); + if (Invisible) { + u.udispl = 0; + prl(u.ux, u.uy); + } else if (!u.udispl || u.udisx != u.ux || u.udisy != u.uy) { + atl(u.ux, u.uy, u.usym); + u.udispl = 1; + u.udisx = u.ux; + u.udisy = u.uy; + } + levl[u.ux][u.uy].seen = 1; +} + +#ifndef NOWORM +extern struct wseg *m_atseg; +#endif /* NOWORM */ + +/* print a position that is visible for @ */ +void +prl(int x, int y) +{ + struct rm *room; + struct monst *mtmp; + struct obj *otmp; + + if (x == u.ux && y == u.uy && (!Invisible)) { + pru(); + return; + } + if (!isok(x, y)) + return; + room = &levl[x][y]; + if ((!room->typ) || + (IS_ROCK(room->typ) && levl[u.ux][u.uy].typ == CORR)) + return; + if ((mtmp = m_at(x, y)) && !mtmp->mhide && + (!mtmp->minvis || See_invisible)) { +#ifndef NOWORM + if (m_atseg) + pwseg(m_atseg); + else +#endif /* NOWORM */ + pmon(mtmp); + } else if ((otmp = o_at(x, y)) && room->typ != POOL) + atl(x, y, otmp->olet); + else if (mtmp && (!mtmp->minvis || See_invisible)) { + /* must be a hiding monster, but not hiding right now */ + /* assume for the moment that long worms do not hide */ + pmon(mtmp); + } else if (g_at(x, y) && room->typ != POOL) + atl(x, y, '$'); + else if (!room->seen || room->scrsym == ' ') { + room->new = room->seen = 1; + newsym(x, y); + on_scr(x, y); + } + room->seen = 1; +} + +char +news0(xchar x, xchar y) +{ + struct obj *otmp; + struct trap *ttmp; + struct rm *room; + char tmp; + + room = &levl[x][y]; + if (!room->seen) + tmp = ' '; + else if (room->typ == POOL) + tmp = POOL_SYM; + else if (!Blind && (otmp = o_at(x, y))) + tmp = otmp->olet; + else if (!Blind && g_at(x, y)) + tmp = '$'; + else if (x == xupstair && y == yupstair) + tmp = '<'; + else if (x == xdnstair && y == ydnstair) + tmp = '>'; + else if ((ttmp = t_at(x, y)) && ttmp->tseen) + tmp = '^'; + else + switch (room->typ) { + case SCORR: + case SDOOR: + tmp = room->scrsym; /* %% wrong after killing mimic ! */ + break; + case HWALL: + tmp = '-'; + break; + case VWALL: + tmp = '|'; + break; + case LDOOR: + case DOOR: + tmp = '+'; + break; + case CORR: + tmp = CORR_SYM; + break; + case ROOM: + if (room->lit || cansee(x, y) || Blind) + tmp = '.'; + else + tmp = ' '; + break; + default: + tmp = ERRCHAR; + } + return (tmp); +} + +void +newsym(int x, int y) +{ + atl(x, y, news0(x, y)); +} + +/* used with wand of digging (or pick-axe): fill scrsym and force display */ +/* also when a POOL evaporates */ +void +mnewsym(int x, int y) +{ + struct rm *room; + char newscrsym; + + if (!vism_at(x, y)) { + room = &levl[x][y]; + newscrsym = news0(x, y); + if (room->scrsym != newscrsym) { + room->scrsym = newscrsym; + room->seen = 0; + } + } +} + +void +nosee(int x, int y) +{ + struct rm *room; + + if (!isok(x, y)) + return; + room = &levl[x][y]; + if (room->scrsym == '.' && !room->lit && !Blind) { + room->scrsym = ' '; + room->new = 1; + on_scr(x, y); + } +} + +#ifndef QUEST +void +prl1(int x, int y) +{ + if (u.dx) { + if (u.dy) { + prl(x - (2 * u.dx), y); + prl(x - u.dx, y); + prl(x, y); + prl(x, y - u.dy); + prl(x, y - (2 * u.dy)); + } else { + prl(x, y - 1); + prl(x, y); + prl(x, y + 1); + } + } else { + prl(x - 1, y); + prl(x, y); + prl(x + 1, y); + } +} + +void +nose1(int x, int y) +{ + if (u.dx) { + if (u.dy) { + nosee(x, u.uy); + nosee(x, u.uy - u.dy); + nosee(x, y); + nosee(u.ux - u.dx, y); + nosee(u.ux, y); + } else { + nosee(x, y - 1); + nosee(x, y); + nosee(x, y + 1); + } + } else { + nosee(x - 1, y); + nosee(x, y); + nosee(x + 1, y); + } +} +#endif /* QUEST */ + +bool +vism_at(int x, int y) +{ + struct monst *mtmp; + + return ((x == u.ux && y == u.uy && !Invisible) + ? 1 : + (mtmp = m_at(x, y)) + ? ((Blind && Telepat) || canseemon(mtmp)) : + 0); +} + +#ifdef NEWSCR +static void +pobj(struct obj *obj) +{ + int show = (!obj->oinvis || See_invisible) && + cansee(obj->ox, obj->oy); + + if (obj->odispl) { + if (obj->odx != obj->ox || obj->ody != obj->oy || !show) + if (!vism_at(obj->odx, obj->ody)) { + newsym(obj->odx, obj->ody); + obj->odispl = 0; + } + } + if (show && !vism_at(obj->ox, obj->oy)) { + atl(obj->ox, obj->oy, obj->olet); + obj->odispl = 1; + obj->odx = obj->ox; + obj->ody = obj->oy; + } +} +#endif /* NEWSCR */ + +void +unpobj(struct obj *obj) +{ + if (!vism_at(obj->ox, obj->oy)) + newsym(obj->ox, obj->oy); +} + +void +seeobjs(void) +{ + struct obj *obj, *obj2; + + for (obj = fobj; obj; obj = obj2) { + obj2 = obj->nobj; + if (obj->olet == FOOD_SYM && obj->otyp >= CORPSE + && obj->age + 250 < moves) + delobj(obj); + } + for (obj = invent; obj; obj = obj2) { + obj2 = obj->nobj; + if (obj->olet == FOOD_SYM && obj->otyp >= CORPSE + && obj->age + 250 < moves) + useup(obj); + } +} + +void +seemons(void) +{ + struct monst *mtmp; + + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { + if (mtmp->data->mlet == ';') + mtmp->minvis = (u.ustuck != mtmp && + levl[mtmp->mx][mtmp->my].typ == POOL); + pmon(mtmp); +#ifndef NOWORM + if (mtmp->wormno) + wormsee(mtmp->wormno); +#endif /* NOWORM */ + } +} + +void +pmon(struct monst *mon) +{ + int show = (Blind && Telepat) || canseemon(mon); + + if (mon->mdispl) { + if (mon->mdx != mon->mx || mon->mdy != mon->my || !show) + unpmon(mon); + } + if (show && !mon->mdispl) { + atl(mon->mx, mon->my, + (!mon->mappearance + || u.uprops[PROP(RIN_PROTECTION_FROM_SHAPE_CHANGERS)].p_flgs + ) ? mon->data->mlet : mon->mappearance); + mon->mdispl = 1; + mon->mdx = mon->mx; + mon->mdy = mon->my; + } +} + +void +unpmon(struct monst *mon) +{ + if (mon->mdispl) { + newsym(mon->mdx, mon->mdy); + mon->mdispl = 0; + } +} + +void +nscr(void) +{ + int x, y; + struct rm *room; + + if (u.uswallow || u.ux == FAR || flags.nscrinh) + return; + pru(); + for (y = scrly; y <= scrhy; y++) + for (x = scrlx; x <= scrhx; x++) + if ((room = &levl[x][y])->new) { + room->new = 0; + at(x, y, room->scrsym); + } + scrhx = scrhy = 0; + scrlx = COLNO; + scrly = ROWNO; +} + +/* 100 suffices for bot(); no relation with COLNO */ +char oldbot[100], newbot[100]; + +static void +cornbot(int lth) +{ + if (lth < (int)sizeof(oldbot)) { + oldbot[lth] = 0; + flags.botl = 1; + } +} + +void +bot(void) +{ + char *ob = oldbot, *nb = newbot; + int i; + + if (flags.botlx) + *ob = 0; + flags.botl = flags.botlx = 0; +#ifdef GOLD_ON_BOTL + sprintf(newbot, + "Level %-2d Gold %-5lu Hp %3d(%d) Ac %-2d Str ", + dlevel, u.ugold, u.uhp, u.uhpmax, u.uac); +#else + sprintf(newbot, + "Level %-2d Hp %3d(%d) Ac %-2d Str ", + dlevel, u.uhp, u.uhpmax, u.uac); +#endif /* GOLD_ON_BOTL */ + if (u.ustr > 18) { + if (u.ustr > 117) + strcat(newbot, "18/**"); + else + sprintf(eos(newbot), "18/%02d", u.ustr - 18); + } else + sprintf(eos(newbot), "%-2d ", u.ustr); +#ifdef EXP_ON_BOTL + sprintf(eos(newbot), " Exp %2d/%-5lu ", u.ulevel, u.uexp); +#else + sprintf(eos(newbot), " Exp %2u ", u.ulevel); +#endif /* EXP_ON_BOTL */ + strcat(newbot, hu_stat[u.uhs]); + if (flags.time) + sprintf(eos(newbot), " %ld", moves); + if (strlen(newbot) >= COLNO) { + char *bp0, *bp1; + bp0 = bp1 = newbot; + do { + if (*bp0 != ' ' || bp0[1] != ' ' || bp0[2] != ' ') + *bp1++ = *bp0; + } while (*bp0++); + } + for (i = 1; i < COLNO; i++) { + if (*ob != *nb) { + curs(i, ROWNO + 2); + putchar(*nb ? *nb : ' '); + curx++; + } + if (*ob) + ob++; + if (*nb) + nb++; + } + strcpy(oldbot, newbot); +} + +#ifdef WAN_PROBING +void +mstatusline(struct monst *mtmp) +{ + pline("Status of %s: ", monnam(mtmp)); + pline("Level %-2d Gold %-5lu Hp %3d(%d) Ac %-2d Dam %d", + mtmp->data->mlevel, mtmp->mgold, mtmp->mhp, mtmp->mhpmax, + mtmp->data->ac, (mtmp->data->damn + 1) * (mtmp->data->damd + 1)); +} +#endif /* WAN_PROBING */ + +void +cls(void) +{ + if (flags.toplin == 1) + more(); + flags.toplin = 0; + + clear_screen(); + + flags.botlx = 1; +} diff --git a/hack/hack.read.c b/hack/hack.read.c new file mode 100644 index 0000000..6f74d26 --- /dev/null +++ b/hack/hack.read.c @@ -0,0 +1,572 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.read.c - version 1.0.3 */ +/* $FreeBSD: src/games/hack/hack.read.c,v 1.6 1999/11/16 10:26:37 marcel Exp $ */ +/* $DragonFly: src/games/hack/hack.read.c,v 1.4 2006/08/21 19:45:32 pavalos Exp $ */ + +#include "hack.h" + +static bool monstersym(char); + +int +doread(void) +{ + struct obj *scroll; + boolean confused = (Confusion != 0); + boolean known = FALSE; + + scroll = getobj("?", "read"); + if (!scroll) + return (0); + if (!scroll->dknown && Blind) { + pline("Being blind, you cannot read the formula on the scroll."); + return (0); + } + if (Blind) + pline("As you pronounce the formula on it, the scroll disappears."); + else + pline("As you read the scroll, it disappears."); + if (confused) + pline("Being confused, you mispronounce the magic words ... "); + + switch (scroll->otyp) { +#ifdef MAIL + case SCR_MAIL: + readmail(/* scroll */); + break; +#endif /* MAIL */ + case SCR_ENCHANT_ARMOR: + { + struct obj *otmp = some_armor(); + if (!otmp) { + strange_feeling(scroll, "Your skin glows then fades."); + return (1); + } + if (confused) { + pline("Your %s glows silver for a moment.", + objects[otmp->otyp].oc_name); + otmp->rustfree = 1; + break; + } + if (otmp->spe > 3 && rn2(otmp->spe)) { + pline("Your %s glows violently green for a while, then evaporates.", + objects[otmp->otyp].oc_name); + useup(otmp); + break; + } + pline("Your %s glows green for a moment.", + objects[otmp->otyp].oc_name); + otmp->cursed = 0; + otmp->spe++; + break; + } + case SCR_DESTROY_ARMOR: + if (confused) { + struct obj *otmp = some_armor(); + if (!otmp) { + strange_feeling(scroll, "Your bones itch."); + return (1); + } + pline("Your %s glows purple for a moment.", + objects[otmp->otyp].oc_name); + otmp->rustfree = 0; + break; + } + if (uarm) { + pline("Your armor turns to dust and falls to the floor!"); + useup(uarm); + } else if (uarmh) { + pline("Your helmet turns to dust and is blown away!"); + useup(uarmh); + } else if (uarmg) { + pline("Your gloves vanish!"); + useup(uarmg); + selftouch("You"); + } else { + strange_feeling(scroll, "Your skin itches."); + return (1); + } + break; + case SCR_CONFUSE_MONSTER: + if (confused) { + pline("Your hands begin to glow purple."); + Confusion += rnd(100); + } else { + pline("Your hands begin to glow blue."); + u.umconf = 1; + } + break; + case SCR_SCARE_MONSTER: + { + int ct = 0; + struct monst *mtmp; + + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) + if (cansee(mtmp->mx, mtmp->my)) { + if (confused) + mtmp->mflee = mtmp->mfroz = + mtmp->msleep = 0; + else + mtmp->mflee = 1; + ct++; + } + if (!ct) { + if (confused) + pline("You hear sad wailing in the distance."); + else + pline("You hear maniacal laughter in the distance."); + } + break; + } + case SCR_BLANK_PAPER: + if (confused) + pline("You see strange patterns on this scroll."); + else + pline("This scroll seems to be blank."); + break; + case SCR_REMOVE_CURSE: + { + struct obj *obj; + if (confused) + pline("You feel like you need some help."); + else + pline("You feel like someone is helping you."); + for (obj = invent; obj; obj = obj->nobj) + if (obj->owornmask) + obj->cursed = confused; + if (Punished && !confused) { + Punished = 0; + freeobj(uchain); + unpobj(uchain); + free(uchain); + uball->spe = 0; + uball->owornmask &= ~W_BALL; + uchain = uball = NULL; + } + break; + } + case SCR_CREATE_MONSTER: + { + int cnt = 1; + + if (!rn2(73)) + cnt += rnd(4); + if (confused) + cnt += 12; + while (cnt--) + makemon(confused ? PM_ACID_BLOB : + NULL, u.ux, u.uy); + break; + } + case SCR_ENCHANT_WEAPON: + if (uwep && confused) { + pline("Your %s glows silver for a moment.", + objects[uwep->otyp].oc_name); + uwep->rustfree = 1; + } else if (!chwepon(scroll, 1)) /* tests for !uwep */ + return (1); + break; + case SCR_DAMAGE_WEAPON: + if (uwep && confused) { + pline("Your %s glows purple for a moment.", + objects[uwep->otyp].oc_name); + uwep->rustfree = 0; + } else if (!chwepon(scroll, -1)) /* tests for !uwep */ + return (1); + break; + case SCR_TAMING: + { + int i, j; + int bd = confused ? 5 : 1; + struct monst *mtmp; + + for (i = -bd; i <= bd; i++) + for (j = -bd; j <= bd; j++) + if ((mtmp = m_at(u.ux + i, u.uy + j))) + tamedog(mtmp, NULL); + break; + } + case SCR_GENOCIDE: + { + char buf[BUFSZ]; + struct monst *mtmp, *mtmp2; + + pline("You have found a scroll of genocide!"); + known = TRUE; + if (confused) + *buf = u.usym; + else + do { + pline("What monster do you want to genocide (Type the letter)? "); + getlin(buf); + } while (strlen(buf) != 1 || !monstersym(*buf)); + if (!strchr(fut_geno, *buf)) + charcat(fut_geno, *buf); + if (!strchr(genocided, *buf)) + charcat(genocided, *buf); + else { + pline("Such monsters do not exist in this world."); + break; + } + for (mtmp = fmon; mtmp; mtmp = mtmp2) { + mtmp2 = mtmp->nmon; + if (mtmp->data->mlet == *buf) + mondead(mtmp); + } + pline("Wiped out all %c's.", *buf); + if (*buf == u.usym) { + killer = "scroll of genocide"; + u.uhp = -1; + } + break; + } + case SCR_LIGHT: + if (!Blind) + known = TRUE; + litroom(!confused); + break; + case SCR_TELEPORTATION: + if (confused) + level_tele(); + else { +#ifdef QUEST + int oux = u.ux, ouy = u.uy; + tele(); + if (dist(oux, ouy) > 100) + known = TRUE; +#else /* QUEST */ + int uroom = inroom(u.ux, u.uy); + tele(); + if (uroom != inroom(u.ux, u.uy)) + known = TRUE; +#endif /* QUEST */ + } + break; + case SCR_GOLD_DETECTION: + /* + * Unfortunately this code has become slightly less elegant, + * now that gold and traps no longer are of the same type. + */ + if (confused) { + struct trap *ttmp; + + if (!ftrap) { + strange_feeling(scroll, "Your toes stop itching."); + return (1); + } else { + for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap) + if (ttmp->tx != u.ux || ttmp->ty != u.uy) + goto outtrapmap; + /* only under me - no separate display required */ + pline("Your toes itch!"); + break; +outtrapmap: + cls(); + for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap) + at(ttmp->tx, ttmp->ty, '$'); + prme(); + pline("You feel very greedy!"); + } + } else { + struct gold *gtmp; + + if (!fgold) { + strange_feeling(scroll, "You feel materially poor."); + return (1); + } else { + known = TRUE; + for (gtmp = fgold; gtmp; gtmp = gtmp->ngold) + if (gtmp->gx != u.ux || gtmp->gy != u.uy) + goto outgoldmap; + /* only under me - no separate display required */ + pline("You notice some gold between your feet."); + break; +outgoldmap: + cls(); + for (gtmp = fgold; gtmp; gtmp = gtmp->ngold) + at(gtmp->gx, gtmp->gy, '$'); + prme(); + pline("You feel very greedy, and sense gold!"); + } + } + /* common sequel */ + more(); + docrt(); + break; + case SCR_FOOD_DETECTION: + { + int ct = 0, ctu = 0; + struct obj *obj; + char foodsym = confused ? POTION_SYM : FOOD_SYM; + + for (obj = fobj; obj; obj = obj->nobj) + if (obj->olet == FOOD_SYM) { + if (obj->ox == u.ux && obj->oy == u.uy) + ctu++; + else + ct++; + } + if (!ct && !ctu) { + strange_feeling(scroll, "Your nose twitches."); + return (1); + } else if (!ct) { + known = TRUE; + pline("You smell %s close nearby.", + confused ? "something" : "food"); + } else { + known = TRUE; + cls(); + for (obj = fobj; obj; obj = obj->nobj) + if (obj->olet == foodsym) + at(obj->ox, obj->oy, FOOD_SYM); + prme(); + pline("Your nose tingles and you smell %s!", + confused ? "something" : "food"); + more(); + docrt(); + } + break; + } + case SCR_IDENTIFY: + /* known = TRUE; */ + if (confused) + pline("You identify this as an identify scroll."); + else + pline("This is an identify scroll."); + useup(scroll); + objects[SCR_IDENTIFY].oc_name_known = 1; + if (!confused) + while (!ggetobj("identify", identify, + rn2(5) ? 1 : rn2(5)) && invent) + ; /* nothing */ + return (1); + case SCR_MAGIC_MAPPING: + { + struct rm *lev; + int num, zx, zy; + + known = TRUE; + pline("On this scroll %s a map!", + confused ? "was" : "is"); + for (zy = 0; zy < ROWNO; zy++) + for (zx = 0; zx < COLNO; zx++) { + if (confused && rn2(7)) + continue; + lev = &(levl[zx][zy]); + if ((num = lev->typ) == 0) + continue; + if (num == SCORR) { + lev->typ = CORR; + lev->scrsym = CORR_SYM; + } else if (num == SDOOR) { + lev->typ = DOOR; + lev->scrsym = '+'; + /* do sth in doors ? */ + } else if (lev->seen) + continue; +#ifndef QUEST + if (num != ROOM) +#endif /* QUEST */ + { + lev->seen = lev->new = 1; + if (lev->scrsym == ' ' || !lev->scrsym) + newsym(zx, zy); + else + on_scr(zx, zy); + } + } + break; + } + case SCR_AMNESIA: + { + int zx, zy; + + known = TRUE; + for (zx = 0; zx < COLNO; zx++) + for (zy = 0; zy < ROWNO; zy++) + if (!confused || rn2(7)) + if (!cansee(zx, zy)) + levl[zx][zy].seen = 0; + docrt(); + pline("Thinking of Maud you forget everything else."); + break; + } + case SCR_FIRE: + { + int num = 0; + struct monst *mtmp; + + known = TRUE; + if (confused) { + pline("The scroll catches fire and you burn your hands."); + losehp(1, "scroll of fire"); + } else { + pline("The scroll erupts in a tower of flame!"); + if (Fire_resistance) + pline("You are uninjured."); + else { + num = rnd(6); + u.uhpmax -= num; + losehp(num, "scroll of fire"); + } + } + num = (2 * num + 1) / 3; + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { + if (dist(mtmp->mx, mtmp->my) < 3) { + mtmp->mhp -= num; + if (strchr("FY", mtmp->data->mlet)) + mtmp->mhp -= 3 * num; /* this might well kill 'F's */ + if (mtmp->mhp < 1) { + killed(mtmp); + break; /* primitive */ + } + } + } + break; + } + case SCR_PUNISHMENT: + known = TRUE; + if (confused) { + pline("You feel guilty."); + break; + } + pline("You are being punished for your misbehaviour!"); + if (Punished) { + pline("Your iron ball gets heavier."); + uball->owt += 15; + break; + } + Punished = INTRINSIC; + setworn(mkobj_at(CHAIN_SYM, u.ux, u.uy), W_CHAIN); + setworn(mkobj_at(BALL_SYM, u.ux, u.uy), W_BALL); + uball->spe = 1; /* special ball (see save) */ + break; + default: + impossible("What weird language is this written in? (%u)", + scroll->otyp); + } + if (!objects[scroll->otyp].oc_name_known) { + if (known && !confused) { + objects[scroll->otyp].oc_name_known = 1; + more_experienced(0, 10); + } else if (!objects[scroll->otyp].oc_uname) + docall(scroll); + } + useup(scroll); + return (1); +} + +int +identify(struct obj *otmp) /* also called by newmail() */ +{ + objects[otmp->otyp].oc_name_known = 1; + otmp->known = otmp->dknown = 1; + prinv(otmp); + return (1); +} + +void +litroom(bool on) +{ +#ifndef QUEST + int num, zx, zy; +#endif + + /* first produce the text (provided he is not blind) */ + if (Blind) + goto do_it; + if (!on) { + if (u.uswallow || !xdnstair || levl[u.ux][u.uy].typ == CORR || + !levl[u.ux][u.uy].lit) { + pline("It seems even darker in here than before."); + return; + } else + pline("It suddenly becomes dark in here."); + } else { + if (u.uswallow) { + pline("%s's stomach is lit.", Monnam(u.ustuck)); + return; + } + if (!xdnstair) { + pline("Nothing Happens."); + return; + } +#ifdef QUEST + pline("The cave lights up around you, then fades."); + return; +#else /* QUEST */ + if (levl[u.ux][u.uy].typ == CORR) { + pline("The corridor lights up around you, then fades."); + return; + } else if (levl[u.ux][u.uy].lit) { + pline("The light here seems better now."); + return; + } else + pline("The room is lit."); +#endif /* QUEST */ + } + +do_it: +#ifdef QUEST + return; +#else /* QUEST */ + if (levl[u.ux][u.uy].lit == on) + return; + if (levl[u.ux][u.uy].typ == DOOR) { + if (IS_ROOM(levl[u.ux][u.uy + 1].typ)) + zy = u.uy + 1; + else if (IS_ROOM(levl[u.ux][u.uy - 1].typ)) + zy = u.uy - 1; + else + zy = u.uy; + if (IS_ROOM(levl[u.ux + 1][u.uy].typ)) + zx = u.ux + 1; + else if (IS_ROOM(levl[u.ux - 1][u.uy].typ)) + zx = u.ux - 1; + else + zx = u.ux; + } else { + zx = u.ux; + zy = u.uy; + } + for (seelx = u.ux; (num = levl[seelx - 1][zy].typ) != CORR && num != 0; + seelx--) ; + for (seehx = u.ux; (num = levl[seehx + 1][zy].typ) != CORR && num != 0; + seehx++) ; + for (seely = u.uy; (num = levl[zx][seely - 1].typ) != CORR && num != 0; + seely--) ; + for (seehy = u.uy; (num = levl[zx][seehy + 1].typ) != CORR && num != 0; + seehy++) ; + for (zy = seely; zy <= seehy; zy++) + for (zx = seelx; zx <= seehx; zx++) { + levl[zx][zy].lit = on; + if (!Blind && dist(zx, zy) > 2) { + if (on) + prl(zx, zy); + else + nosee(zx, zy); + } + } + if (!on) + seehx = 0; +#endif /* QUEST */ +} + +/* Test whether we may genocide all monsters with symbol ch */ +static bool +monstersym(char ch) /* arnold@ucsfcgl */ +{ + struct permonst *mp; + + /* + * can't genocide certain monsters + */ + if (strchr("12 &:", ch)) + return (FALSE); + + if (ch == pm_eel.mlet) + return (TRUE); + for (mp = mons; mp < &mons[CMNUM + 2]; mp++) + if (mp->mlet == ch) + return (TRUE); + return (FALSE); +} diff --git a/hack/hack.rip.c b/hack/hack.rip.c new file mode 100644 index 0000000..33f06b2 --- /dev/null +++ b/hack/hack.rip.c @@ -0,0 +1,89 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.rip.c - version 1.0.2 */ +/* $FreeBSD: src/games/hack/hack.rip.c,v 1.4 1999/11/16 10:26:37 marcel Exp $ */ +/* $DragonFly: src/games/hack/hack.rip.c,v 1.4 2006/08/21 19:45:32 pavalos Exp $ */ + +#include "hack.h" + +static void center(int, const char *); + +static char rip[][60] = { +" ----------", +" / \\", +" / REST \\", +" / IN \\", +" / PEACE \\", +" / \\", +" | |", +" | |", +" | |", +" | |", +" | |", +" | 1001 |", +" *| * * * | *", +" _________)/\\\\_//(\\/(/\\)/\\//\\/|_)_______\n", +}; +static const int n_rips = sizeof(rip) / sizeof(rip[0]); + +void +outrip(void) +{ + char *dpx; + char buf[BUFSZ]; + int j, x, y; + + cls(); + strcpy(buf, plname); + buf[16] = 0; + center(6, buf); + sprintf(buf, "%ld AU", u.ugold); + center(7, buf); + sprintf(buf, "killed by%s", + !strncmp(killer, "the ", 4) ? "" : + !strcmp(killer, "starvation") ? "" : + strchr(vowels, *killer) ? " an" : " a"); + center(8, buf); + strcpy(buf, killer); + if (strlen(buf) > 16) { + int i, i0, i1; + i0 = i1 = 0; + for (i = 0; i <= 16; i++) + if (buf[i] == ' ') + i0 = i, i1 = i + 1; + if (!i0) + i0 = i1 = 16; + buf[i1 + 16] = 0; + center(10, buf + i1); + buf[i0] = 0; + } + center(9, buf); + sprintf(buf, "%4d", getyear()); + center(11, buf); + for (y = 8, j = 0; j < n_rips; y++, j++) { + x = 0; + dpx = rip[j]; + while (dpx[x]) { + while (dpx[x] == ' ') + x++; + curs(x, y); + while (dpx[x] && dpx[x] != ' ') { + if (done_stopprint) + return; + curx++; + putchar(dpx[x++]); + } + } + } + getret(); +} + +static void +center(int line, const char *text) +{ + const char *ip = text; + char *op; + + op = &rip[line][28 - ((strlen(text) + 1) / 2)]; + while (*ip) + *op++ = *ip++; +} diff --git a/hack/hack.rumors.c b/hack/hack.rumors.c new file mode 100644 index 0000000..5453732 --- /dev/null +++ b/hack/hack.rumors.c @@ -0,0 +1,90 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.rumors.c - version 1.0.3 */ +/* $FreeBSD: src/games/hack/hack.rumors.c,v 1.3 1999/11/16 02:57:10 billf Exp $ */ + +#include "hack.h" /* for RUMORFILE and BSD (index) */ +#define CHARSZ 8 /* number of bits in a char */ +int n_rumors = 0; +int n_used_rumors = -1; +char *usedbits; + +static void init_rumors(FILE *); +static bool skipline(FILE *); +static void outline(FILE *); +static bool used(int); + +static void +init_rumors(FILE *rumf) +{ + int i; + + n_used_rumors = 0; + while (skipline(rumf)) + n_rumors++; + rewind(rumf); + i = n_rumors / CHARSZ; + usedbits = alloc((unsigned)(i + 1)); + for (; i >= 0; i--) + usedbits[i] = 0; +} + +static bool +skipline(FILE *rumf) +{ + char line[COLNO]; + + for (;;) { + if (!fgets(line, sizeof(line), rumf)) + return (0); + if (strchr(line, '\n')) + return (1); + } +} + +static void +outline(FILE *rumf) +{ + char line[COLNO]; + char *ep; + + if (!fgets(line, sizeof(line), rumf)) + return; + if ((ep = strchr(line, '\n')) != NULL) + *ep = 0; + pline("This cookie has a scrap of paper inside! It reads: "); + pline("%s", line); +} + +void +outrumor(void) +{ + int rn, i; + FILE *rumf; + + if (n_rumors <= n_used_rumors || + (rumf = fopen(RUMORFILE, "r")) == NULL) + return; + if (n_used_rumors < 0) + init_rumors(rumf); + if (!n_rumors) + goto none; + rn = rn2(n_rumors - n_used_rumors); + i = 0; + while (rn || used(i)) { + skipline(rumf); + if (!used(i)) + rn--; + i++; + } + usedbits[i / CHARSZ] |= (1 << (i % CHARSZ)); + n_used_rumors++; + outline(rumf); +none: + fclose(rumf); +} + +static bool +used(int i) +{ + return (usedbits[i / CHARSZ] & (1 << (i % CHARSZ))); +} diff --git a/hack/hack.save.c b/hack/hack.save.c new file mode 100644 index 0000000..4ee9ac1 --- /dev/null +++ b/hack/hack.save.c @@ -0,0 +1,240 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.save.c - version 1.0.3 */ +/* $FreeBSD: src/games/hack/hack.save.c,v 1.4 1999/11/16 10:26:37 marcel Exp $ */ + +#include "hack.h" + +extern char SAVEF[], nul[]; + +static bool dosave0(int); + +int +dosave(void) +{ + if (dosave0(0)) { + settty("Be seeing you ...\n"); + exit(0); + } + return (0); +} + +#ifndef NOSAVEONHANGUP +void +hangup(int n __attribute__((unused))) +{ + dosave0(1); + exit(1); +} +#endif /* NOSAVEONHANGUP */ + +/* returns 1 if save successful */ +static bool +dosave0(int hu) +{ + int fd, ofd; + int tmp; /* not ! */ + + signal(SIGHUP, SIG_IGN); + signal(SIGINT, SIG_IGN); + if ((fd = creat(SAVEF, FMASK)) < 0) { + if (!hu) + pline("Cannot open save file. (Continue or Quit)"); + unlink(SAVEF); /* ab@unido */ + return (0); + } + if (flags.moonphase == FULL_MOON) /* ut-sally!fletcher */ + u.uluck--; /* and unido!ab */ + savelev(fd, dlevel); + saveobjchn(fd, invent); + saveobjchn(fd, fcobj); + savemonchn(fd, fallen_down); + tmp = getuid(); + bwrite(fd, (char *)&tmp, sizeof(tmp)); + bwrite(fd, (char *)&flags, sizeof(struct flag)); + bwrite(fd, (char *)&dlevel, sizeof(dlevel)); + bwrite(fd, (char *)&maxdlevel, sizeof(maxdlevel)); + bwrite(fd, (char *)&moves, sizeof(moves)); + bwrite(fd, (char *)&u, sizeof(struct you)); + if (u.ustuck) + bwrite(fd, (char *)&(u.ustuck->m_id), sizeof(u.ustuck->m_id)); + bwrite(fd, (char *)pl_character, sizeof(pl_character)); + bwrite(fd, (char *)genocided, sizeof(genocided)); + bwrite(fd, (char *)fut_geno, sizeof(fut_geno)); + savenames(fd); + for (tmp = 1; tmp <= maxdlevel; tmp++) { + if (tmp == dlevel || !level_exists[tmp]) + continue; + glo(tmp); + if ((ofd = open(lock, O_RDONLY)) < 0) { + if (!hu) + pline("Error while saving: cannot read %s.", lock); + close(fd); + unlink(SAVEF); + if (!hu) + done("tricked"); + return (0); + } + getlev(ofd, hackpid, tmp); + close(ofd); + bwrite(fd, (char *)&tmp, sizeof(tmp)); /* level number */ + savelev(fd, tmp); /* actual level */ + unlink(lock); + } + close(fd); + glo(dlevel); + unlink(lock); /* get rid of current level --jgm */ + glo(0); + unlink(lock); + return (1); +} + +bool +dorecover(int fd) +{ + int nfd; + int tmp; /* not a ! */ + unsigned mid; /* idem */ + struct obj *otmp; + + restoring = TRUE; + getlev(fd, 0, 0); + invent = restobjchn(fd); + for (otmp = invent; otmp; otmp = otmp->nobj) + if (otmp->owornmask) + setworn(otmp, otmp->owornmask); + fcobj = restobjchn(fd); + fallen_down = restmonchn(fd); + mread(fd, (char *)&tmp, sizeof(tmp)); + if (tmp != (int)getuid()) { /* strange ... */ + close(fd); + unlink(SAVEF); + puts("Saved game was not yours."); + restoring = FALSE; + return (0); + } + mread(fd, (char *)&flags, sizeof(struct flag)); + mread(fd, (char *)&dlevel, sizeof(dlevel)); + mread(fd, (char *)&maxdlevel, sizeof(maxdlevel)); + mread(fd, (char *)&moves, sizeof(moves)); + mread(fd, (char *)&u, sizeof(struct you)); + if (u.ustuck) + mread(fd, (char *)&mid, sizeof(mid)); + mread(fd, (char *)pl_character, sizeof(pl_character)); + mread(fd, (char *)genocided, sizeof(genocided)); + mread(fd, (char *)fut_geno, sizeof(fut_geno)); + restnames(fd); + for (;;) { + if (read(fd, (char *)&tmp, sizeof(tmp)) != sizeof(tmp)) + break; + getlev(fd, 0, tmp); + glo(tmp); + if ((nfd = creat(lock, FMASK)) < 0) + panic("Cannot open temp file %s!\n", lock); + savelev(nfd, tmp); + close(nfd); + } + lseek(fd, (off_t)0, SEEK_SET); + getlev(fd, 0, 0); + close(fd); + unlink(SAVEF); + if (Punished) { + for (otmp = fobj; otmp; otmp = otmp->nobj) + if (otmp->olet == CHAIN_SYM) + goto chainfnd; + panic("Cannot find the iron chain?"); +chainfnd: + uchain = otmp; + if (!uball) { + for (otmp = fobj; otmp; otmp = otmp->nobj) + if (otmp->olet == BALL_SYM && otmp->spe) + goto ballfnd; + panic("Cannot find the iron ball?"); +ballfnd: + uball = otmp; + } + } + if (u.ustuck) { + struct monst *mtmp; + + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) + if (mtmp->m_id == mid) + goto monfnd; + panic("Cannot find the monster ustuck."); +monfnd: + u.ustuck = mtmp; + } +#ifndef QUEST + setsee(); /* only to recompute seelx etc. - these weren't saved */ +#endif /* QUEST */ + docrt(); + restoring = FALSE; + return (1); +} + +struct obj * +restobjchn(int fd) +{ + struct obj *otmp, *otmp2; + struct obj *first = NULL; + int xl; + + /* suppress "used before set" warning from lint */ + otmp2 = NULL; + for (;;) { + mread(fd, (char *)&xl, sizeof(xl)); + if (xl == -1) + break; + otmp = newobj(xl); + if (!first) + first = otmp; + else + otmp2->nobj = otmp; + mread(fd, (char *)otmp, (unsigned)xl + sizeof(struct obj)); + if (!otmp->o_id) otmp->o_id = flags.ident++; + otmp2 = otmp; + } + if (first && otmp2->nobj) { + impossible("Restobjchn: error reading objchn."); + otmp2->nobj = 0; + } + return (first); +} + +struct monst * +restmonchn(int fd) +{ + struct monst *mtmp, *mtmp2; + struct monst *first = NULL; + int xl; + struct permonst *monbegin; + long differ; + + mread(fd, (char *)&monbegin, sizeof(monbegin)); + differ = (char *)(&mons[0]) - (char *)(monbegin); + + /* suppress "used before set" warning from lint */ + mtmp2 = NULL; + for (;;) { + mread(fd, (char *)&xl, sizeof(xl)); + if (xl == -1) + break; + mtmp = newmonst(xl); + if (!first) + first = mtmp; + else + mtmp2->nmon = mtmp; + mread(fd, (char *)mtmp, (unsigned)xl + sizeof(struct monst)); + if (!mtmp->m_id) + mtmp->m_id = flags.ident++; + mtmp->data = (struct permonst *) + ((char *)mtmp->data + differ); + if (mtmp->minvent) + mtmp->minvent = restobjchn(fd); + mtmp2 = mtmp; + } + if (first && mtmp2->nmon) { + impossible("Restmonchn: error reading monchn."); + mtmp2->nmon = 0; + } + return (first); +} diff --git a/hack/hack.search.c b/hack/hack.search.c new file mode 100644 index 0000000..b1dd291 --- /dev/null +++ b/hack/hack.search.c @@ -0,0 +1,147 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.search.c - version 1.0.3 */ +/* $FreeBSD: src/games/hack/hack.search.c,v 1.3 1999/11/16 02:57:11 billf Exp $ */ +/* $DragonFly: src/games/hack/hack.search.c,v 1.3 2006/08/21 19:45:32 pavalos Exp $ */ + +#include "hack.h" + +int +findit(void) /* returns number of things found */ +{ + int num; + xchar zx, zy; + struct trap *ttmp; + struct monst *mtmp; + xchar lx, hx, ly, hy; + + if (u.uswallow) + return (0); + for (lx = u.ux; (num = levl[lx - 1][u.uy].typ) && num != CORR; lx--) ; + for (hx = u.ux; (num = levl[hx + 1][u.uy].typ) && num != CORR; hx++) ; + for (ly = u.uy; (num = levl[u.ux][ly - 1].typ) && num != CORR; ly--) ; + for (hy = u.uy; (num = levl[u.ux][hy + 1].typ) && num != CORR; hy++) ; + num = 0; + for (zy = ly; zy <= hy; zy++) + for (zx = lx; zx <= hx; zx++) { + if (levl[zx][zy].typ == SDOOR) { + levl[zx][zy].typ = DOOR; + atl(zx, zy, '+'); + num++; + } else if (levl[zx][zy].typ == SCORR) { + levl[zx][zy].typ = CORR; + atl(zx, zy, CORR_SYM); + num++; + } else if ((ttmp = t_at(zx, zy)) != NULL) { + if (ttmp->ttyp == PIERC) { + makemon(PM_PIERCER, zx, zy); + num++; + deltrap(ttmp); + } else if (!ttmp->tseen) { + ttmp->tseen = 1; + if (!vism_at(zx, zy)) + atl(zx, zy, '^'); + num++; + } + } else if ((mtmp = m_at(zx, zy)) != NULL) + if (mtmp->mimic) { + seemimic(mtmp); + num++; + } + } + return (num); +} + +int +dosearch(void) +{ + xchar x, y; + struct trap *trap; + struct monst *mtmp; + + if (u.uswallow) + pline("What are you looking for? The exit?"); + else + for (x = u.ux - 1; x < u.ux + 2; x++) + for (y = u.uy - 1; y < u.uy + 2; y++) + if (x != u.ux || y != u.uy) { + if (levl[x][y].typ == SDOOR) { + if (rn2(7)) + continue; + levl[x][y].typ = DOOR; + levl[x][y].seen = 0; /* force prl */ + prl(x, y); + nomul(0); + } else if (levl[x][y].typ == SCORR) { + if (rn2(7)) + continue; + levl[x][y].typ = CORR; + levl[x][y].seen = 0; /* force prl */ + prl(x, y); + nomul(0); + } else { + /* Be careful not to find anything in an SCORR or SDOOR */ + if ((mtmp = m_at(x, y))) + if (mtmp->mimic) { + seemimic(mtmp); + pline("You find a mimic."); + return (1); + } + for (trap = ftrap; trap; trap = trap->ntrap) + if (trap->tx == x && trap->ty == y && + !trap->tseen && !rn2(8)) { + nomul(0); + pline("You find a%s.", traps[trap->ttyp]); + if (trap->ttyp == PIERC) { + deltrap(trap); + makemon(PM_PIERCER, x, y); + return (1); + } + trap->tseen = 1; + if (!vism_at(x, y)) + atl(x, y, '^'); + } + } + } + return (1); +} + +int +doidtrap(void) +{ + struct trap *trap; + int x, y; + + if (!getdir(1)) + return (0); + x = u.ux + u.dx; + y = u.uy + u.dy; + for (trap = ftrap; trap; trap = trap->ntrap) + if (trap->tx == x && trap->ty == y && trap->tseen) { + if (u.dz) + if ((u.dz < 0) != (!xdnstair && trap->ttyp == TRAPDOOR)) + continue; + pline("That is a%s.", traps[trap->ttyp]); + return (0); + } + pline("I can't see a trap there."); + return (0); +} + +void +wakeup(struct monst *mtmp) +{ + mtmp->msleep = 0; + setmangry(mtmp); + if (mtmp->mimic) + seemimic(mtmp); +} + +/* NOTE: we must check if (mtmp->mimic) before calling this routine */ +void +seemimic(struct monst *mtmp) +{ + mtmp->mimic = 0; + mtmp->mappearance = 0; + unpmon(mtmp); + pmon(mtmp); +} diff --git a/hack/hack.sh b/hack/hack.sh new file mode 100644 index 0000000..8136ec4 --- /dev/null +++ b/hack/hack.sh @@ -0,0 +1,14 @@ +#!/bin/sh +HACKDIR=/usr/games/lib/hackdir +HACK=$HACKDIR/hack +MAXNROFPLAYERS=4 + +cd $HACKDIR +case $1 in + -s*) + exec $HACK $@ + ;; + *) + exec $HACK $@ $MAXNROFPLAYERS + ;; +esac diff --git a/hack/hack.shk.c b/hack/hack.shk.c new file mode 100644 index 0000000..eea0558 --- /dev/null +++ b/hack/hack.shk.c @@ -0,0 +1,1085 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.shk.c - version 1.0.3 */ +/* $FreeBSD: src/games/hack/hack.shk.c,v 1.5 1999/11/16 10:26:37 marcel Exp $ */ + +#include "hack.h" +#ifdef QUEST +int shlevel = 0; +struct monst *shopkeeper = NULL; +struct obj *billobjs = NULL; + +void +obfree(struct obj *obj, struct obj *merge) +{ + free(obj); +} + +int +inshop(void) +{ + return (0); +} + +void +shopdig(void) +{ +} + +void +addtobill(void) +{ +} + +void +subfrombill(void) +{ +} + +void +splitbill(void) +{ +} + +int +dopay(void) +{ + return (0); +} + +void +paybill(void) +{ +} + +int +doinvbill(void) +{ + return (0); +} + +void +shkdead(void) +{ +} + +int +shkcatch(void) +{ + return (0); +} + +int +shk_move(void) +{ + return (0); +} + +void +replshk(struct monst *mtmp, struct monst *mtmp2) +{ +} + +const char * +shkname(void) +{ + return (""); +} + +#else /* QUEST */ +#include "hack.mfndpos.h" +#include "def.eshk.h" + +#define ESHK(mon) ((struct eshk *)(&(mon->mextra[0]))) +#define NOTANGRY(mon) mon->mpeaceful +#define ANGRY(mon) !NOTANGRY(mon) + +/* Descriptor of current shopkeeper. Note that the bill need not be + * per-shopkeeper, since it is valid only when in a shop. */ +static struct monst *shopkeeper = NULL; +static struct bill_x *bill; +static int shlevel = 0; /* level of this shopkeeper */ +struct obj *billobjs; /* objects on bill with bp->useup */ + /* only accessed here and by save & restore */ +static long int total; /* filled by addupbill() */ +static long int followmsg; /* last time of follow message */ + +/* + * invariants: obj->unpaid iff onbill(obj) [unless bp->useup] + * obj->quan <= bp->bquan + */ + +char shtypes[] = { /* 8 shoptypes: 7 specialized, 1 mixed */ + RING_SYM, WAND_SYM, WEAPON_SYM, FOOD_SYM, SCROLL_SYM, + POTION_SYM, ARMOR_SYM, 0 +}; + +static const char *shopnam[] = { + "engagement ring", "walking cane", "antique weapon", + "delicatessen", "second hand book", "liquor", + "used armor", "assorted antiques" +}; + +static void setpaid(void); +static void addupbill(void); +static void findshk(int); +static struct bill_x *onbill(struct obj *); +static void pay(long, struct monst *); +static int dopayobj(struct bill_x *); +static struct obj *bp_to_obj(struct bill_x *); +static int getprice(struct obj *); +static int realhunger(void); + +char * +shkname(struct monst *mtmp) /* called in do_name.c */ +{ + return (ESHK(mtmp)->shknam); +} + +void +shkdead(struct monst *mtmp) /* called in mon.c */ +{ + struct eshk *eshk = ESHK(mtmp); + + if (eshk->shoplevel == dlevel) + rooms[eshk->shoproom].rtype = 0; + if (mtmp == shopkeeper) { + setpaid(); + shopkeeper = NULL; + bill = (struct bill_x *) - 1000; /* dump core when referenced */ + } +} + +void +replshk(struct monst *mtmp, struct monst *mtmp2) +{ + if (mtmp == shopkeeper) { + shopkeeper = mtmp2; + bill = &(ESHK(shopkeeper)->bill[0]); + } +} + +static void +setpaid(void) /* caller has checked that shopkeeper exists */ + /* either we paid or left the shop or he just died */ +{ + struct obj *obj; + struct monst *mtmp; + + for (obj = invent; obj; obj = obj->nobj) + obj->unpaid = 0; + for (obj = fobj; obj; obj = obj->nobj) + obj->unpaid = 0; + for (obj = fcobj; obj; obj = obj->nobj) + obj->unpaid = 0; + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) + for (obj = mtmp->minvent; obj; obj = obj->nobj) + obj->unpaid = 0; + for (mtmp = fallen_down; mtmp; mtmp = mtmp->nmon) + for (obj = mtmp->minvent; obj; obj = obj->nobj) + obj->unpaid = 0; + while ((obj = billobjs) != NULL) { + billobjs = obj->nobj; + free(obj); + } + ESHK(shopkeeper)->billct = 0; +} + +static void +addupbill(void) /* delivers result in total */ + /* caller has checked that shopkeeper exists */ +{ + int ct = ESHK(shopkeeper)->billct; + struct bill_x *bp = bill; + + total = 0; + while (ct--) { + total += bp->price * bp->bquan; + bp++; + } +} + +int +inshop(void) +{ + int roomno = inroom(u.ux, u.uy); + + /* Did we just leave a shop? */ + if (u.uinshop && + (u.uinshop != roomno + 1 || shlevel != dlevel || !shopkeeper)) { + if (shopkeeper) { + if (ESHK(shopkeeper)->billct) { + if (inroom(shopkeeper->mx, shopkeeper->my) + == u.uinshop - 1) /* ab@unido */ + pline("Somehow you escaped the shop without paying!"); + addupbill(); + pline("You stole for a total worth of %ld zorkmids.", + total); + ESHK(shopkeeper)->robbed += total; + setpaid(); + if ((rooms[ESHK(shopkeeper)->shoproom].rtype == GENERAL) + == (rn2(3) == 0)) + ESHK(shopkeeper)->following = 1; + } + shopkeeper = NULL; + shlevel = 0; + } + u.uinshop = 0; + } + + /* Did we just enter a zoo of some kind? */ + if (roomno >= 0) { + int rt = rooms[roomno].rtype; + struct monst *mtmp; + if (rt == ZOO) + pline("Welcome to David's treasure zoo!"); + else if (rt == SWAMP) + pline("It looks rather muddy down here."); + else if (rt == MORGUE) { + if (midnight()) + pline("Go away! Go away!"); + else + pline("You get an uncanny feeling ..."); + } else + rt = 0; + if (rt != 0) { + rooms[roomno].rtype = 0; + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) + if (rt != ZOO || !rn2(3)) + mtmp->msleep = 0; + } + } + + /* Did we just enter a shop? */ + if (roomno >= 0 && rooms[roomno].rtype >= 8) { + if (shlevel != dlevel || !shopkeeper + || ESHK(shopkeeper)->shoproom != roomno) + findshk(roomno); + if (!shopkeeper) { + rooms[roomno].rtype = 0; + u.uinshop = 0; + } else if (!u.uinshop) { + if (!ESHK(shopkeeper)->visitct || + strncmp(ESHK(shopkeeper)->customer, plname, PL_NSIZ)) { + /* He seems to be new here */ + ESHK(shopkeeper)->visitct = 0; + ESHK(shopkeeper)->following = 0; + strncpy(ESHK(shopkeeper)->customer, plname, PL_NSIZ); + NOTANGRY(shopkeeper) = 1; + } + if (!ESHK(shopkeeper)->following) { + boolean box, pick; + + pline("Hello %s! Welcome%s to %s's %s shop!", + plname, + ESHK(shopkeeper)->visitct++ ? " again" : "", + shkname(shopkeeper), + shopnam[rooms[ESHK(shopkeeper)->shoproom].rtype - 8]); + box = carrying(ICE_BOX); + pick = carrying(PICK_AXE); + if (box || pick) { + if (dochug(shopkeeper)) { + u.uinshop = 0; /* he died moving */ + return (0); + } + pline("Will you please leave your %s outside?", + (box && pick) ? "box and pick-axe" : + box ? "box" : "pick-axe"); + } + } + u.uinshop = roomno + 1; + } + } + return (u.uinshop); +} + +static void +findshk(int roomno) +{ + struct monst *mtmp; + + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) + if (mtmp->isshk && ESHK(mtmp)->shoproom == roomno + && ESHK(mtmp)->shoplevel == dlevel) { + shopkeeper = mtmp; + bill = &(ESHK(shopkeeper)->bill[0]); + shlevel = dlevel; + if (ANGRY(shopkeeper) && + strncmp(ESHK(shopkeeper)->customer, plname, PL_NSIZ)) + NOTANGRY(shopkeeper) = 1; + /* billobjs = 0; -- this is wrong if we save in a shop */ + /* (and it is harmless to have too many things in billobjs) */ + return; + } + shopkeeper = NULL; + shlevel = 0; + bill = (struct bill_x *) - 1000; /* dump core when referenced */ +} + +static struct bill_x * +onbill(struct obj *obj) +{ + struct bill_x *bp; + + if (!shopkeeper) + return (0); + for (bp = bill; bp < &bill[ESHK(shopkeeper)->billct]; bp++) + if (bp->bo_id == obj->o_id) { + if (!obj->unpaid) + pline("onbill: paid obj on bill?"); + return (bp); + } + if (obj->unpaid) + pline("onbill: unpaid obj not on bill?"); + return (0); +} + +/* called with two args on merge */ +void +obfree(struct obj *obj, struct obj *merge) +{ + struct bill_x *bp = onbill(obj); + struct bill_x *bpm; + + if (bp) { + if (!merge) { + bp->useup = 1; + obj->unpaid = 0; /* only for doinvbill */ + obj->nobj = billobjs; + billobjs = obj; + return; + } + bpm = onbill(merge); + if (!bpm) { + /* this used to be a rename */ + impossible("obfree: not on bill??"); + return; + } else { + /* this was a merger */ + bpm->bquan += bp->bquan; + ESHK(shopkeeper)->billct--; + *bp = bill[ESHK(shopkeeper)->billct]; + } + } + free(obj); +} + +static void +pay(long tmp, struct monst *shkp) +{ + long robbed = ESHK(shkp)->robbed; + + u.ugold -= tmp; + shkp->mgold += tmp; + flags.botl = 1; + if (robbed) { + robbed -= tmp; + if (robbed < 0) + robbed = 0; + ESHK(shkp)->robbed = robbed; + } +} + +int +dopay(void) +{ + long ltmp; + struct bill_x *bp; + struct monst *shkp; + int pass, tmp; + + multi = 0; + inshop(); + for (shkp = fmon; shkp; shkp = shkp->nmon) + if (shkp->isshk && dist(shkp->mx, shkp->my) < 3) + break; + if (!shkp && u.uinshop && + inroom(shopkeeper->mx, shopkeeper->my) == ESHK(shopkeeper)->shoproom) + shkp = shopkeeper; + + if (!shkp) { + pline("There is nobody here to receive your payment."); + return (0); + } + ltmp = ESHK(shkp)->robbed; + if (shkp != shopkeeper && NOTANGRY(shkp)) { + if (!ltmp) + pline("You do not owe %s anything.", monnam(shkp)); + else if (!u.ugold) + pline("You have no money."); + else { + long ugold = u.ugold; + + if (u.ugold > ltmp) { + pline("You give %s the %ld gold pieces he asked for.", + monnam(shkp), ltmp); + pay(ltmp, shkp); + } else { + pline("You give %s all your gold.", monnam(shkp)); + pay(u.ugold, shkp); + } + if (ugold < ltmp / 2) + pline("Unfortunately, he doesn't look satisfied."); + else { + ESHK(shkp)->robbed = 0; + ESHK(shkp)->following = 0; + if (ESHK(shkp)->shoplevel != dlevel) { + /* For convenience's sake, let him disappear */ + shkp->minvent = 0; /* %% */ + shkp->mgold = 0; + mondead(shkp); + } + } + } + return (1); + } + + if (!ESHK(shkp)->billct) { + pline("You do not owe %s anything.", monnam(shkp)); + if (!u.ugold) { + pline("Moreover, you have no money."); + return (1); + } + if (ESHK(shkp)->robbed) { +#define min(a, b) ((a < b) ? a : b) + pline("But since his shop has been robbed recently,"); + pline("you %srepay %s's expenses.", + (u.ugold < ESHK(shkp)->robbed) ? "partially " : "", + monnam(shkp)); + pay(min(u.ugold, ESHK(shkp)->robbed), shkp); + ESHK(shkp)->robbed = 0; + return (1); + } + if (ANGRY(shkp)) { + pline("But in order to appease %s,", + amonnam(shkp, "angry")); + if (u.ugold >= 1000) { + ltmp = 1000; + pline(" you give him 1000 gold pieces."); + } else { + ltmp = u.ugold; + pline(" you give him all your money."); + } + pay(ltmp, shkp); + if (strncmp(ESHK(shkp)->customer, plname, PL_NSIZ) + || rn2(3)) { + pline("%s calms down.", Monnam(shkp)); + NOTANGRY(shkp) = 1; + } else + pline("%s is as angry as ever.", Monnam(shkp)); + } + return (1); + } + if (shkp != shopkeeper) { + impossible("dopay: not to shopkeeper?"); + if (shopkeeper) + setpaid(); + return (0); + } + for (pass = 0; pass <= 1; pass++) { + tmp = 0; + while (tmp < ESHK(shopkeeper)->billct) { + bp = &bill[tmp]; + if (!pass && !bp->useup) { + tmp++; + continue; + } + if (!dopayobj(bp)) + return (1); + bill[tmp] = bill[--ESHK(shopkeeper)->billct]; + } + } + pline("Thank you for shopping in %s's %s store!", + shkname(shopkeeper), + shopnam[rooms[ESHK(shopkeeper)->shoproom].rtype - 8]); + NOTANGRY(shopkeeper) = 1; + return (1); +} + +/* return 1 if paid successfully */ +/* 0 if not enough money */ +/* -1 if object could not be found (but was paid) */ +static int +dopayobj(struct bill_x *bp) +{ + struct obj *obj; + long ltmp; + + /* find the object on one of the lists */ + obj = bp_to_obj(bp); + + if (!obj) { + impossible("Shopkeeper administration out of order."); + setpaid(); /* be nice to the player */ + return (0); + } + + if (!obj->unpaid && !bp->useup) { + impossible("Paid object on bill??"); + return (1); + } + obj->unpaid = 0; + ltmp = bp->price * bp->bquan; + if (ANGRY(shopkeeper)) + ltmp += ltmp / 3; + if (u.ugold < ltmp) { + pline("You don't have gold enough to pay %s.", + doname(obj)); + obj->unpaid = 1; + return (0); + } + pay(ltmp, shopkeeper); + pline("You bought %s for %ld gold piece%s.", + doname(obj), ltmp, plur(ltmp)); + if (bp->useup) { + struct obj *otmp = billobjs; + if (obj == billobjs) + billobjs = obj->nobj; + else { + while (otmp && otmp->nobj != obj) + otmp = otmp->nobj; + if (otmp) + otmp->nobj = obj->nobj; + else + pline("Error in shopkeeper administration."); + } + free(obj); + } + return (1); +} + +/* routine called after dying (or quitting) with nonempty bill */ +void +paybill(void) +{ + if (shlevel == dlevel && shopkeeper && ESHK(shopkeeper)->billct) { + addupbill(); + if (total > u.ugold) { + shopkeeper->mgold += u.ugold; + u.ugold = 0; + pline("%s comes and takes all your possessions.", + Monnam(shopkeeper)); + } else { + u.ugold -= total; + shopkeeper->mgold += total; + pline("%s comes and takes the %ld zorkmids you owed him.", + Monnam(shopkeeper), total); + } + setpaid(); /* in case we create bones */ + } +} + +/* find obj on one of the lists */ +static struct obj * +bp_to_obj(struct bill_x *bp) +{ + struct obj *obj; + struct monst *mtmp; + unsigned id = bp->bo_id; + + if (bp->useup) + obj = o_on(id, billobjs); + else if (!(obj = o_on(id, invent)) && + !(obj = o_on(id, fobj)) && + !(obj = o_on(id, fcobj))) { + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) + if ((obj = o_on(id, mtmp->minvent)) != NULL) + break; + for (mtmp = fallen_down; mtmp; mtmp = mtmp->nmon) + if ((obj = o_on(id, mtmp->minvent)) != NULL) + break; + } + return (obj); +} + +/* called in hack.c when we pickup an object */ +void +addtobill(struct obj *obj) +{ + struct bill_x *bp; + + if (!inshop() || + (u.ux == ESHK(shopkeeper)->shk.x && u.uy == ESHK(shopkeeper)->shk.y) || + (u.ux == ESHK(shopkeeper)->shd.x && u.uy == ESHK(shopkeeper)->shd.y) || + onbill(obj) /* perhaps we threw it away earlier */ + ) + return; + if (ESHK(shopkeeper)->billct == BILLSZ) { + pline("You got that for free!"); + return; + } + bp = &bill[ESHK(shopkeeper)->billct]; + bp->bo_id = obj->o_id; + bp->bquan = obj->quan; + bp->useup = 0; + bp->price = getprice(obj); + ESHK(shopkeeper)->billct++; + obj->unpaid = 1; +} + +void +splitbill(struct obj *obj, struct obj *otmp) +{ + /* otmp has been split off from obj */ + struct bill_x *bp; + int tmp; + + bp = onbill(obj); + if (!bp) { + impossible("splitbill: not on bill?"); + return; + } + if (bp->bquan < otmp->quan) + impossible("Negative quantity on bill??"); + if (bp->bquan == otmp->quan) + impossible("Zero quantity on bill??"); + bp->bquan -= otmp->quan; + + if (ESHK(shopkeeper)->billct == BILLSZ) + otmp->unpaid = 0; + else { + tmp = bp->price; + bp = &bill[ESHK(shopkeeper)->billct]; + bp->bo_id = otmp->o_id; + bp->bquan = otmp->quan; + bp->useup = 0; + bp->price = tmp; + ESHK(shopkeeper)->billct++; + } +} + +void +subfrombill(struct obj *obj) +{ + long ltmp; + int tmp; + struct obj *otmp; + struct bill_x *bp; + + if (!inshop() || + (u.ux == ESHK(shopkeeper)->shk.x && u.uy == ESHK(shopkeeper)->shk.y) || + (u.ux == ESHK(shopkeeper)->shd.x && u.uy == ESHK(shopkeeper)->shd.y)) + return; + if ((bp = onbill(obj)) != NULL) { + obj->unpaid = 0; + if (bp->bquan > obj->quan) { + otmp = newobj(0); + *otmp = *obj; + bp->bo_id = otmp->o_id = flags.ident++; + otmp->quan = (bp->bquan -= obj->quan); + otmp->owt = 0; /* superfluous */ + otmp->onamelth = 0; + bp->useup = 1; + otmp->nobj = billobjs; + billobjs = otmp; + return; + } + ESHK(shopkeeper)->billct--; + *bp = bill[ESHK(shopkeeper)->billct]; + return; + } + if (obj->unpaid) { + pline("%s didn't notice.", Monnam(shopkeeper)); + obj->unpaid = 0; + return; /* %% */ + } + /* he dropped something of his own - probably wants to sell it */ + if (shopkeeper->msleep || shopkeeper->mfroz || + inroom(shopkeeper->mx, shopkeeper->my) != ESHK(shopkeeper)->shoproom) + return; + if (ESHK(shopkeeper)->billct == BILLSZ || + ((tmp = shtypes[rooms[ESHK(shopkeeper)->shoproom].rtype - 8]) && + tmp != obj->olet) || strchr("_0", obj->olet)) { + pline("%s seems not interested.", Monnam(shopkeeper)); + return; + } + ltmp = getprice(obj) * obj->quan; + if (ANGRY(shopkeeper)) { + ltmp /= 3; + NOTANGRY(shopkeeper) = 1; + } else + ltmp /= 2; + if (ESHK(shopkeeper)->robbed) { + if ((ESHK(shopkeeper)->robbed -= ltmp) < 0) + ESHK(shopkeeper)->robbed = 0; + pline("Thank you for your contribution to restock this recently plundered shop."); + return; + } + if (ltmp > shopkeeper->mgold) + ltmp = shopkeeper->mgold; + pay(-ltmp, shopkeeper); + if (!ltmp) + pline("%s gladly accepts %s but cannot pay you at present.", + Monnam(shopkeeper), doname(obj)); + else + pline("You sold %s and got %ld gold piece%s.", doname(obj), ltmp, + plur(ltmp)); +} + +int +doinvbill(int mode) /* 0: deliver count 1: paged */ +{ + struct bill_x *bp; + struct obj *obj; + long totused, thisused; + char buf[BUFSZ]; + + if (mode == 0) { + int cnt = 0; + + if (shopkeeper) + for (bp = bill; bp - bill < ESHK(shopkeeper)->billct; bp++) + if (bp->useup || + ((obj = bp_to_obj(bp)) && obj->quan < bp->bquan)) + cnt++; + return (cnt); + } + + if (!shopkeeper) { + impossible("doinvbill: no shopkeeper?"); + return (0); + } + + set_pager(0); + if (page_line("Unpaid articles already used up:") || page_line("")) + goto quit; + + totused = 0; + for (bp = bill; bp - bill < ESHK(shopkeeper)->billct; bp++) { + obj = bp_to_obj(bp); + if (!obj) { + impossible("Bad shopkeeper administration."); + goto quit; + } + if (bp->useup || bp->bquan > obj->quan) { + int cnt, oquan, uquan; + + oquan = obj->quan; + uquan = (bp->useup ? bp->bquan : bp->bquan - oquan); + thisused = bp->price * uquan; + totused += thisused; + obj->quan = uquan; /* cheat doname */ + sprintf(buf, "x - %s", doname(obj)); + obj->quan = oquan; /* restore value */ + for (cnt = 0; buf[cnt]; cnt++) + ; /* nothing */ + while (cnt < 50) + buf[cnt++] = ' '; + sprintf(&buf[cnt], " %5ld zorkmids", thisused); + if (page_line(buf)) + goto quit; + } + } + sprintf(buf, "Total:%50ld zorkmids", totused); + if (page_line("") || page_line(buf)) + goto quit; + set_pager(1); + return (0); +quit: + set_pager(2); + return (0); +} + +static int +getprice(struct obj *obj) +{ + int tmp, ac; + + switch (obj->olet) { + case AMULET_SYM: + tmp = 10 * rnd(500); + break; + case TOOL_SYM: + tmp = 10 * rnd((obj->otyp == EXPENSIVE_CAMERA) ? 150 : 30); + break; + case RING_SYM: + tmp = 10 * rnd(100); + break; + case WAND_SYM: + tmp = 10 * rnd(100); + break; + case SCROLL_SYM: + tmp = 10 * rnd(50); +#ifdef MAIL + if (obj->otyp == SCR_MAIL) + tmp = rnd(5); +#endif /* MAIL */ + break; + case POTION_SYM: + tmp = 10 * rnd(50); + break; + case FOOD_SYM: + tmp = 10 * rnd(5 + (2000 / realhunger())); + break; + case GEM_SYM: + tmp = 10 * rnd(20); + break; + case ARMOR_SYM: + ac = ARM_BONUS(obj); + if (ac <= -10) /* probably impossible */ + ac = -9; + tmp = 100 + ac * ac * rnd(10 + ac); + break; + case WEAPON_SYM: + if (obj->otyp < BOOMERANG) + tmp = 5 * rnd(10); + else if (obj->otyp == LONG_SWORD || + obj->otyp == TWO_HANDED_SWORD) + tmp = 10 * rnd(150); + else + tmp = 10 * rnd(75); + break; + case CHAIN_SYM: + pline("Strange ..., carrying a chain?"); + case BALL_SYM: + tmp = 10; + break; + default: + tmp = 10000; + } + return (tmp); +} + +static int +realhunger(void) /* not completely foolproof */ +{ + int tmp = u.uhunger; + struct obj *otmp = invent; + + while (otmp) { + if (otmp->olet == FOOD_SYM && !otmp->unpaid) + tmp += objects[otmp->otyp].nutrition; + otmp = otmp->nobj; + } + return ((tmp <= 0) ? 1 : tmp); +} + +bool +shkcatch(struct obj *obj) +{ + struct monst *shkp = shopkeeper; + + if (u.uinshop && shkp && !shkp->mfroz && !shkp->msleep && + u.dx && u.dy && + inroom(u.ux + u.dx, u.uy + u.dy) + 1 == u.uinshop && + shkp->mx == ESHK(shkp)->shk.x && shkp->my == ESHK(shkp)->shk.y && + u.ux == ESHK(shkp)->shd.x && u.uy == ESHK(shkp)->shd.y) { + pline("%s nimbly catches the %s.", Monnam(shkp), xname(obj)); + obj->nobj = shkp->minvent; + shkp->minvent = obj; + return (1); + } + return (0); +} + +/* + * shk_move: return 1: he moved 0: he didnt -1: let m_move do it + */ +int +shk_move(struct monst *shkp) +{ + struct monst *mtmp; + struct permonst *mdat = shkp->data; + xchar gx, gy, omx, omy, nx, ny, nix, niy; + schar appr, i; + int udist; + int z; + schar shkroom, chi, chcnt, cnt; + boolean uondoor = 0, satdoor, avoid = 0, badinv; + coord poss[9]; + int info[9]; + struct obj *ib = NULL; + + omx = shkp->mx; + omy = shkp->my; + + if ((udist = dist(omx, omy)) < 3) { + if (ANGRY(shkp)) { + hitu(shkp, d(mdat->damn, mdat->damd) + 1); + return (0); + } + if (ESHK(shkp)->following) { + if (strncmp(ESHK(shkp)->customer, plname, PL_NSIZ)) { + pline("Hello %s! I was looking for %s.", + plname, ESHK(shkp)->customer); + ESHK(shkp)->following = 0; + return (0); + } + if (!ESHK(shkp)->robbed) { /* impossible? */ + ESHK(shkp)->following = 0; + return (0); + } + if (moves > followmsg + 4) { + pline("Hello %s! Didn't you forget to pay?", + plname); + followmsg = moves; + } + if (udist < 2) + return (0); + } + } + + shkroom = inroom(omx, omy); + appr = 1; + gx = ESHK(shkp)->shk.x; + gy = ESHK(shkp)->shk.y; + satdoor = (gx == omx && gy == omy); + if (ESHK(shkp)->following || ((z = holetime()) >= 0 && z * z <= udist)) { + gx = u.ux; + gy = u.uy; + if (shkroom < 0 || shkroom != inroom(u.ux, u.uy)) + if (udist > 4) + return (-1); /* leave it to m_move */ + } else if (ANGRY(shkp)) { + long saveBlind = Blind; + Blind = 0; + if (shkp->mcansee && !Invis && cansee(omx, omy)) { + gx = u.ux; + gy = u.uy; + } + Blind = saveBlind; + avoid = FALSE; + } else { +#define GDIST(x, y) ((x - gx) * (x - gx) + (y - gy) * (y - gy)) + if (Invis) + avoid = FALSE; + else { + uondoor = (u.ux == ESHK(shkp)->shd.x && + u.uy == ESHK(shkp)->shd.y); + if (uondoor) { + if (ESHK(shkp)->billct) + pline("Hello %s! Will you please pay before leaving?", + plname); + badinv = (carrying(PICK_AXE) || carrying(ICE_BOX)); + if (satdoor && badinv) + return (0); + avoid = !badinv; + } else { + avoid = (u.uinshop && dist(gx, gy) > 8); + badinv = FALSE; + } + + if (((!ESHK(shkp)->robbed && !ESHK(shkp)->billct) || avoid) + && GDIST(omx, omy) < 3) { + if (!badinv && !online(omx, omy)) + return (0); + if (satdoor) + appr = gx = gy = 0; + } + } + } + if (omx == gx && omy == gy) + return (0); + if (shkp->mconf) { + avoid = FALSE; + appr = 0; + } + nix = omx; + niy = omy; + cnt = mfndpos(shkp, poss, info, ALLOW_SSM); + if (avoid && uondoor) { /* perhaps we cannot avoid him */ + for (i = 0; i < cnt; i++) + if (!(info[i] & NOTONL)) + goto notonl_ok; + avoid = FALSE; +notonl_ok: + ; + } + chi = -1; + chcnt = 0; + for (i = 0; i < cnt; i++) { + nx = poss[i].x; + ny = poss[i].y; + if (levl[nx][ny].typ == ROOM + || shkroom != ESHK(shkp)->shoproom + || ESHK(shkp)->following) { +#ifdef STUPID + /* cater for stupid compilers */ + int zz; +#endif /* STUPID */ + if (uondoor && (ib = sobj_at(ICE_BOX, nx, ny))) { + nix = nx; + niy = ny; + chi = i; break; + } + if (avoid && (info[i] & NOTONL)) + continue; + if ((!appr && !rn2(++chcnt)) || +#ifdef STUPID + (appr && (zz = GDIST(nix, niy)) && zz > GDIST(nx, ny)) +#else + (appr && GDIST(nx, ny) < GDIST(nix, niy)) +#endif /* STUPID */ + ) { + nix = nx; + niy = ny; + chi = i; + } + } + } + if (nix != omx || niy != omy) { + if (info[chi] & ALLOW_M) { + mtmp = m_at(nix, niy); + if (hitmm(shkp, mtmp) == 1 && rn2(3) && + hitmm(mtmp, shkp) == 2) + return (2); + return (0); + } else if (info[chi] & ALLOW_U) { + hitu(shkp, d(mdat->damn, mdat->damd) + 1); + return (0); + } + shkp->mx = nix; + shkp->my = niy; + pmon(shkp); + if (ib) { + freeobj(ib); + mpickobj(shkp, ib); + } + return (1); + } + return (0); +} + +/* He is digging in the shop. */ +void +shopdig(int fall) +{ + if (!fall) { + if (u.utraptype == TT_PIT) + pline("\"Be careful, sir, or you might fall through the floor.\""); + else + pline("\"Please, do not damage the floor here.\""); + } else if (dist(shopkeeper->mx, shopkeeper->my) < 3) { + struct obj *obj, *obj2; + + pline("%s grabs your backpack!", shkname(shopkeeper)); + for (obj = invent; obj; obj = obj2) { + obj2 = obj->nobj; + if (obj->owornmask) + continue; + freeinv(obj); + obj->nobj = shopkeeper->minvent; + shopkeeper->minvent = obj; + if (obj->unpaid) + subfrombill(obj); + } + } +} +#endif /* QUEST */ + +bool +online(int x, int y) +{ + return (x == u.ux || y == u.uy || + (x - u.ux) * (x - u.ux) == (y - u.uy) * (y - u.uy)); +} + +/* Does this monster follow me downstairs? */ +bool +follower(struct monst *mtmp) +{ + return (mtmp->mtame || strchr("1TVWZi&, ", mtmp->data->mlet) +#ifndef QUEST + || (mtmp->isshk && ESHK(mtmp)->following) +#endif /* QUEST */ + ); +} diff --git a/hack/hack.shknam.c b/hack/hack.shknam.c new file mode 100644 index 0000000..2f6052a --- /dev/null +++ b/hack/hack.shknam.c @@ -0,0 +1,149 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.shknam.c - version 1.0.2 */ +/* $FreeBSD: src/games/hack/hack.shknam.c,v 1.3 1999/11/16 02:57:11 billf Exp $ */ +/* $DragonFly: src/games/hack/hack.shknam.c,v 1.4 2006/08/21 19:45:32 pavalos Exp $ */ + +#include "hack.h" + +static const char *shkliquors[] = { + /* Ukraine */ + "Njezjin", "Tsjernigof", "Gomel", "Ossipewsk", "Gorlowka", + /* N. Russia */ + "Konosja", "Weliki Oestjoeg", "Syktywkar", "Sablja", + "Narodnaja", "Kyzyl", + /* Silezie */ + "Walbrzych", "Swidnica", "Klodzko", "Raciborz", "Gliwice", + "Brzeg", "Krnov", "Hradec Kralove", + /* Schweiz */ + "Leuk", "Brig", "Brienz", "Thun", "Sarnen", "Burglen", "Elm", + "Flims", "Vals", "Schuls", "Zum Loch", + 0 +}; + +static const char *shkbooks[] = { + /* Eire */ + "Skibbereen", "Kanturk", "Rath Luirc", "Ennistymon", "Lahinch", + "Loughrea", "Croagh", "Maumakeogh", "Ballyjamesduff", + "Kinnegad", "Lugnaquillia", "Enniscorthy", "Gweebarra", + "Kittamagh", "Nenagh", "Sneem", "Ballingeary", "Kilgarvan", + "Cahersiveen", "Glenbeigh", "Kilmihil", "Kiltamagh", + "Droichead Atha", "Inniscrone", "Clonegal", "Lisnaskea", + "Culdaff", "Dunfanaghy", "Inishbofin", "Kesh", + 0 +}; + +static const char *shkarmors[] = { + /* Turquie */ + "Demirci", "Kalecik", "Boyabai", "Yildizeli", "Gaziantep", + "Siirt", "Akhalataki", "Tirebolu", "Aksaray", "Ermenak", + "Iskenderun", "Kadirli", "Siverek", "Pervari", "Malasgirt", + "Bayburt", "Ayancik", "Zonguldak", "Balya", "Tefenni", + "Artvin", "Kars", "Makharadze", "Malazgirt", "Midyat", + "Birecik", "Kirikkale", "Alaca", "Polatli", "Nallihan", + 0 +}; + +static const char *shkwands[] = { + /* Wales */ + "Yr Wyddgrug", "Trallwng", "Mallwyd", "Pontarfynach", + "Rhaeader", "Llandrindod", "Llanfair-ym-muallt", + "Y-Fenni", "Measteg", "Rhydaman", "Beddgelert", + "Curig", "Llanrwst", "Llanerchymedd", "Caergybi", + /* Scotland */ + "Nairn", "Turriff", "Inverurie", "Braemar", "Lochnagar", + "Kerloch", "Beinn a Ghlo", "Drumnadrochit", "Morven", + "Uist", "Storr", "Sgurr na Ciche", "Cannich", "Gairloch", + "Kyleakin", "Dunvegan", + 0 +}; + +static const char *shkrings[] = { + /* Hollandse familienamen */ + "Feyfer", "Flugi", "Gheel", "Havic", "Haynin", "Hoboken", + "Imbyze", "Juyn", "Kinsky", "Massis", "Matray", "Moy", + "Olycan", "Sadelin", "Svaving", "Tapper", "Terwen", "Wirix", + "Ypey", + /* Skandinaviske navne */ + "Rastegaisa", "Varjag Njarga", "Kautekeino", "Abisko", + "Enontekis", "Rovaniemi", "Avasaksa", "Haparanda", + "Lulea", "Gellivare", "Oeloe", "Kajaani", "Fauske", + 0 +}; + +static const char *shkfoods[] = { + /* Indonesia */ + "Djasinga", "Tjibarusa", "Tjiwidej", "Pengalengan", + "Bandjar", "Parbalingga", "Bojolali", "Sarangan", + "Ngebel", "Djombang", "Ardjawinangun", "Berbek", + "Papar", "Baliga", "Tjisolok", "Siboga", "Banjoewangi", + "Trenggalek", "Karangkobar", "Njalindoeng", "Pasawahan", + "Pameunpeuk", "Patjitan", "Kediri", "Pemboeang", "Tringanoe", + "Makin", "Tipor", "Semai", "Berhala", "Tegal", "Samoe", + 0 +}; + +static const char *shkweapons[] = { + /* Perigord */ + "Voulgezac", "Rouffiac", "Lerignac", "Touverac", "Guizengeard", + "Melac", "Neuvicq", "Vanzac", "Picq", "Urignac", "Corignac", + "Fleac", "Lonzac", "Vergt", "Queyssac", "Liorac", "Echourgnac", + "Cazelon", "Eypau", "Carignan", "Monbazillac", "Jonzac", + "Pons", "Jumilhac", "Fenouilledes", "Laguiolet", "Saujon", + "Eymoutiers", "Eygurande", "Eauze", "Labouheyre", + 0 +}; + +static const char *shkgeneral[] = { + /* Suriname */ + "Hebiwerie", "Possogroenoe", "Asidonhopo", "Manlobbi", + "Adjama", "Pakka Pakka", "Kabalebo", "Wonotobo", + "Akalapi", "Sipaliwini", + /* Greenland */ + "Annootok", "Upernavik", "Angmagssalik", + /* N. Canada */ + "Aklavik", "Inuvik", "Tuktoyaktuk", + "Chicoutimi", "Ouiatchouane", "Chibougamau", + "Matagami", "Kipawa", "Kinojevis", + "Abitibi", "Maganasipi", + /* Iceland */ + "Akureyri", "Kopasker", "Budereyri", "Akranes", "Bordeyri", + "Holmavik", + 0 +}; + +struct shk_nx { + char x; + const char **xn; +} shk_nx[] = { + { POTION_SYM, shkliquors }, + { SCROLL_SYM, shkbooks }, + { ARMOR_SYM, shkarmors }, + { WAND_SYM, shkwands }, + { RING_SYM, shkrings }, + { FOOD_SYM, shkfoods }, + { WEAPON_SYM, shkweapons }, + { 0, shkgeneral } +}; + +void +findname(char *nampt, char let) +{ + struct shk_nx *p = shk_nx; + const char **q; + int i; + + while (p->x && p->x != let) + p++; + q = p->xn; + for (i = 0; i < dlevel; i++) + if (!q[i]) { + /* Not enough names, try general name */ + if (let) + findname(nampt, 0); + else + strcpy(nampt, "Dirk"); + return; + } + strncpy(nampt, q[i], PL_NSIZ); + nampt[PL_NSIZ - 1] = 0; +} diff --git a/hack/hack.steal.c b/hack/hack.steal.c new file mode 100644 index 0000000..84111bb --- /dev/null +++ b/hack/hack.steal.c @@ -0,0 +1,210 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.steal.c - version 1.0.3 */ +/* $FreeBSD: src/games/hack/hack.steal.c,v 1.4 1999/11/16 10:26:38 marcel Exp $ */ +/* $DragonFly: src/games/hack/hack.steal.c,v 1.4 2006/08/21 19:45:32 pavalos Exp $ */ + +#include "hack.h" + +static void stealarm(void); + +/* actually returns something that fits in an int */ +long +somegold(void) +{ + return ((u.ugold < 100) ? u.ugold : + (u.ugold > 10000) ? rnd(10000) : rnd((int)u.ugold)); +} + +void +stealgold(struct monst *mtmp) +{ + struct gold *gold = g_at(u.ux, u.uy); + long tmp; + + if (gold && (!u.ugold || gold->amount > u.ugold || !rn2(5))) { + mtmp->mgold += gold->amount; + freegold(gold); + if (Invisible) + newsym(u.ux, u.uy); + pline("%s quickly snatches some gold from between your feet!", + Monnam(mtmp)); + if (!u.ugold || !rn2(5)) { + rloc(mtmp); + mtmp->mflee = 1; + } + } else if (u.ugold) { + u.ugold -= (tmp = somegold()); + pline("Your purse feels lighter."); + mtmp->mgold += tmp; + rloc(mtmp); + mtmp->mflee = 1; + flags.botl = 1; + } +} + +/* steal armor after he finishes taking it off */ +unsigned stealoid; /* object to be stolen */ +unsigned stealmid; /* monster doing the stealing */ + +static void +stealarm(void) +{ + struct monst *mtmp; + struct obj *otmp; + + for (otmp = invent; otmp; otmp = otmp->nobj) + if (otmp->o_id == stealoid) { + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) + if (mtmp->m_id == stealmid) { + if (dist(mtmp->mx, mtmp->my) < 3) { + freeinv(otmp); + pline("%s steals %s!", Monnam(mtmp), doname(otmp)); + mpickobj(mtmp, otmp); + mtmp->mflee = 1; + rloc(mtmp); + } + break; + } + break; + } + stealoid = 0; +} + +/* returns 1 when something was stolen */ +/* (or at least, when N should flee now) */ +/* avoid stealing the object stealoid */ +bool +steal(struct monst *mtmp) +{ + struct obj *otmp; + int tmp; + int named = 0; + + if (!invent) { + if (Blind) + pline("Somebody tries to rob you, but finds nothing to steal."); + else + pline("%s tries to rob you, but she finds nothing to steal!", + Monnam(mtmp)); + return (1); /* let her flee */ + } + tmp = 0; + for (otmp = invent; otmp; otmp = otmp->nobj) + if (otmp != uarm2) + tmp += ((otmp->owornmask & (W_ARMOR | W_RING)) ? 5 : 1); + tmp = rn2(tmp); + for (otmp = invent; otmp; otmp = otmp->nobj) + if (otmp != uarm2) + if ((tmp -= ((otmp->owornmask & (W_ARMOR | W_RING)) ? 5 : 1)) + < 0) + break; + if (!otmp) { + impossible("Steal fails!"); + return (0); + } + if (otmp->o_id == stealoid) + return (0); + if ((otmp->owornmask & (W_ARMOR | W_RING))) { + switch (otmp->olet) { + case RING_SYM: + ringoff(otmp); + break; + case ARMOR_SYM: + if (multi < 0 || otmp == uarms) { + setworn(NULL, otmp->owornmask & W_ARMOR); + break; + } + { + int curssv = otmp->cursed; + otmp->cursed = 0; + stop_occupation(); + pline("%s seduces you and %s off your %s.", + Amonnam(mtmp, Blind ? "gentle" : "beautiful"), + otmp->cursed ? "helps you to take" + : "you start taking", + (otmp == uarmg) ? "gloves" : + (otmp == uarmh) ? "helmet" : "armor"); + named++; + armoroff(otmp); + otmp->cursed = curssv; + if (multi < 0) { + stealoid = otmp->o_id; + stealmid = mtmp->m_id; + afternmv = stealarm; + return (0); + } + break; + } + default: + impossible("Tried to steal a strange worn thing."); + } + } else if (otmp == uwep) + setuwep(NULL); + if (otmp->olet == CHAIN_SYM) + impossible("How come you are carrying that chain?"); + if (Punished && otmp == uball) { + Punished = 0; + freeobj(uchain); + free(uchain); + uchain = NULL; + uball->spe = 0; + uball = NULL; /* superfluous */ + } + freeinv(otmp); + pline("%s stole %s.", named ? "She" : Monnam(mtmp), doname(otmp)); + mpickobj(mtmp, otmp); + return ((multi < 0) ? 0 : 1); +} + +void +mpickobj(struct monst *mtmp, struct obj *otmp) +{ + otmp->nobj = mtmp->minvent; + mtmp->minvent = otmp; +} + +bool +stealamulet(struct monst *mtmp) +{ + struct obj *otmp; + + for (otmp = invent; otmp; otmp = otmp->nobj) { + if (otmp->olet == AMULET_SYM) { + /* might be an imitation one */ + if (otmp == uwep) + setuwep(NULL); + freeinv(otmp); + mpickobj(mtmp, otmp); + pline("%s stole %s!", Monnam(mtmp), doname(otmp)); + return (1); + } + } + return (0); +} + +/* release the objects the killed animal has stolen */ +void +relobj(struct monst *mtmp, int show) +{ + struct obj *otmp, *otmp2; + + for (otmp = mtmp->minvent; otmp; otmp = otmp2) { + otmp->ox = mtmp->mx; + otmp->oy = mtmp->my; + otmp2 = otmp->nobj; + otmp->nobj = fobj; + fobj = otmp; + stackobj(fobj); + if (show & cansee(mtmp->mx, mtmp->my)) + atl(otmp->ox, otmp->oy, otmp->olet); + } + mtmp->minvent = NULL; + if (mtmp->mgold || mtmp->data->mlet == 'L') { + long tmp; + + tmp = (mtmp->mgold > 10000) ? 10000 : mtmp->mgold; + mkgold((long)(tmp + d(dlevel, 30)), mtmp->mx, mtmp->my); + if (show & cansee(mtmp->mx, mtmp->my)) + atl(mtmp->mx, mtmp->my, '$'); + } +} diff --git a/hack/hack.termcap.c b/hack/hack.termcap.c new file mode 100644 index 0000000..e2b8679 --- /dev/null +++ b/hack/hack.termcap.c @@ -0,0 +1,268 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.termcap.c - version 1.0.3 */ +/* $FreeBSD: src/games/hack/hack.termcap.c,v 1.10 1999/11/16 10:26:38 marcel Exp $ */ + +#include <termcap.h> +#include "hack.h" + +static char tbuf[512]; +static char *HO, *CL, *CE, *tcUP, *CM, *ND, *XD, *tcBC, *SO, *SE, *TI, *TE; +static char *VS, *VE; +static int SG; +static char tcPC = '\0'; +char *CD; /* tested in pri.c: docorner() */ +int CO, LI; /* used in pri.c and whatis.c */ + +static void nocmov(int, int); +static void cmov(int, int); +static int xputc(int); +static void xputs(char *); /* 20150209 bkw: change return type int => void */ + +void +startup(void) +{ + char *term; + char *tptr; + char *tbufptr, *pc; + + tptr = alloc(1024); + + tbufptr = tbuf; + if (!(term = getenv("TERM"))) + error("Can't get TERM."); + if (tgetent(tptr, term) < 1) + error("Unknown terminal type: %s.", term); + if (tgetflag(__DECONST(char *, "NP")) || + tgetflag(__DECONST(char *, "nx"))) + flags.nonull = 1; + if ((pc = tgetstr(__DECONST(char *, "pc"), &tbufptr))) + tcPC = *pc; + if (!(tcBC = tgetstr(__DECONST(char *, "bc"), &tbufptr)) + && !(tcBC = tgetstr(__DECONST(char *, "le"), &tbufptr))) { + if (!tgetflag(__DECONST(char *, "bs"))) + error("Terminal must backspace."); + tcBC = tbufptr; + tbufptr += 2; + *tcBC = '\b'; + } + HO = tgetstr(__DECONST(char *, "ho"), &tbufptr); + CO = tgetnum(__DECONST(char *, "co")); + LI = tgetnum(__DECONST(char *, "li")); + if (CO < COLNO || LI < ROWNO + 2) + setclipped(); + if (!(CL = tgetstr(__DECONST(char *, "cl"), &tbufptr))) + error("Hack needs CL."); + ND = tgetstr(__DECONST(char *, "nd"), &tbufptr); + if (tgetflag(__DECONST(char *, "os"))) + error("Hack can't have OS."); + CE = tgetstr(__DECONST(char *, "ce"), &tbufptr); + tcUP = tgetstr(__DECONST(char *, "up"), &tbufptr); + /* It seems that xd is no longer supported, and we should use + * a linefeed instead; unfortunately this requires resetting + * CRMOD, and many output routines will have to be modified + * slightly. Let's leave that till the next release. */ + XD = tgetstr(__DECONST(char *, "xd"), &tbufptr); +/* not: XD = tgetstr("do", &tbufptr); */ + if (!(CM = tgetstr(__DECONST(char *, "cm"), &tbufptr))) { + if (!tcUP && !HO) + error("Hack needs CM or UP or HO."); + printf("Playing hack on terminals without cm is suspect...\n"); + getret(); + } + SO = tgetstr(__DECONST(char *, "so"), &tbufptr); + SE = tgetstr(__DECONST(char *, "se"), &tbufptr); + SG = tgetnum(__DECONST(char *, "sg")); + if (!SO || !SE || (SG > 0)) SO = SE = NULL; + CD = tgetstr(__DECONST(char *, "cd"), &tbufptr); + set_whole_screen(); /* uses LI and CD */ + if (tbufptr - tbuf > (int)sizeof(tbuf)) error( + "TERMCAP entry too big...\n"); + free(tptr); +} + +void +start_screen(void) +{ + xputs(TI); + xputs(VS); +} + +void +end_screen(void) +{ + xputs(VE); + xputs(TE); +} + +/* not xchar: perhaps xchar is unsigned and curx-x would be unsigned as well */ +void +curs(int x, int y) +{ + if (y == cury && x == curx) + return; + if (!ND && (curx != x || x <= 3)) { /* Extremely primitive */ + cmov(x, y); /* bunker!wtm */ + return; + } + if (abs(cury - y) <= 3 && abs(curx - x) <= 3) + nocmov(x, y); + else if ((x <= 3 && abs(cury - y) <= 3) || (!CM && x < abs(curx - x))) { + putchar('\r'); + curx = 1; + nocmov(x, y); + } else if (!CM) + nocmov(x, y); + else + cmov(x, y); +} + +static void +nocmov(int x, int y) +{ + if (cury > y) { + if (tcUP) + while (cury > y) { /* Go up. */ + xputs(tcUP); + cury--; + } + else if (CM) + cmov(x, y); + else if (HO) { + home(); + curs(x, y); + } /* else impossible("..."); */ + } else if (cury < y) { + if (XD) { + while (cury < y) { + xputs(XD); + cury++; + } + } else if (CM) { + cmov(x, y); + } else { + while (cury < y) { + xputc('\n'); + curx = 1; + cury++; + } + } + } + if (curx < x) { /* Go to the right. */ + if (!ND) + cmov(x, y); + else /* bah */ + /* should instead print what is there already */ + while (curx < x) { + xputs(ND); + curx++; + } + } else if (curx > x) { + while (curx > x) { /* Go to the left. */ + xputs(tcBC); + curx--; + } + } +} + +static void +cmov(int x, int y) +{ + xputs(tgoto(CM, x - 1, y - 1)); + cury = y; + curx = x; +} + +static int +xputc(int c) +{ + return (fputc(c, stdout)); +} + +static void /* 20150209 bkw: change return type int => void */ +xputs(char *s) +{ + return (tputs(s, 1, xputc)); +} + +void +cl_end(void) +{ + if (CE) + xputs(CE); + else { /* no-CE fix - free after Harold Rynes */ + /* this looks terrible, especially on a slow terminal + * but is better than nothing */ + int cx = curx, cy = cury; + + while (curx < COLNO) { + xputc(' '); + curx++; + } + curs(cx, cy); + } +} + +void +clear_screen(void) +{ + xputs(CL); + curx = cury = 1; +} + +void +home(void) +{ + if (HO) + xputs(HO); + else if (CM) + xputs(tgoto(CM, 0, 0)); + else + curs(1, 1); /* using tcUP ... */ + curx = cury = 1; +} + +void +standoutbeg(void) +{ + if (SO) + xputs(SO); +} + +void +standoutend(void) +{ + if (SE) + xputs(SE); +} + +void +backsp(void) +{ + xputs(tcBC); + curx--; +} + +void +bell(void) +{ + putchar('\007'); /* curx does not change */ + fflush(stdout); +} + +void +cl_eos(void) /* free after Robert Viduya */ +{ /* must only be called with curx = 1 */ + if (CD) + xputs(CD); + else { + int cx = curx, cy = cury; + while (cury <= LI - 2) { + cl_end(); + xputc('\n'); + curx = 1; + cury++; + } + cl_end(); + curs(cx, cy); + } +} diff --git a/hack/hack.timeout.c b/hack/hack.timeout.c new file mode 100644 index 0000000..005e000 --- /dev/null +++ b/hack/hack.timeout.c @@ -0,0 +1,72 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.timeout.c - version 1.0.3 */ +/* $FreeBSD: src/games/hack/hack.timeout.c,v 1.3 1999/11/16 02:57:12 billf Exp $ */ + +#include "hack.h" + +static void stoned_dialogue(void); + +void +p_timeout(void) +{ + struct prop *upp; + + if (Stoned) + stoned_dialogue(); + for (upp = u.uprops; upp < u.uprops + SIZE(u.uprops); upp++) + if ((upp->p_flgs & TIMEOUT) && !--upp->p_flgs) { + if (upp->p_tofn) + (*upp->p_tofn)(); + else + switch (upp - u.uprops) { + case STONED: + killer = "cockatrice"; + done("died"); + break; + case SICK: + pline("You die because of food poisoning."); + killer = u.usick_cause; + done("died"); + break; + case FAST: + pline("You feel yourself slowing down."); + break; + case CONFUSION: + pline("You feel less confused now."); + break; + case BLIND: + pline("You can see again."); + setsee(); + break; + case INVIS: + on_scr(u.ux, u.uy); + pline("You are no longer invisible."); + break; + case WOUNDED_LEGS: + heal_legs(); + break; + } + } +} + +/* He is being petrified - dialogue by inmet!tower */ +static const char *stoned_texts[] = { + "You are slowing down.", /* 5 */ + "Your limbs are stiffening.", /* 4 */ + "Your limbs have turned to stone.", /* 3 */ + "You have turned to stone.", /* 2 */ + "You are a statue." /* 1 */ +}; + +static void +stoned_dialogue(void) +{ + long i = (Stoned & TIMEOUT); + + if (i > 0 && i <= SIZE(stoned_texts)) + pline("%s", stoned_texts[SIZE(stoned_texts) - i]); + if (i == 5) + Fast = 0; + if (i == 3) + nomul(-3); +} diff --git a/hack/hack.topl.c b/hack/hack.topl.c new file mode 100644 index 0000000..75d612a --- /dev/null +++ b/hack/hack.topl.c @@ -0,0 +1,236 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.topl.c - version 1.0.2 */ +/* $FreeBSD: src/games/hack/hack.topl.c,v 1.3 1999/11/16 02:57:12 billf Exp $ */ + +#include "hack.h" +extern int CO; + +char toplines[BUFSZ]; +xchar tlx, tly; /* set by pline; used by addtopl */ + +struct topl { + struct topl *next_topl; + char *topl_text; +} *old_toplines, *last_redone_topl; +#define OTLMAX 20 /* max nr of old toplines remembered */ + +static void redotoplin(void); +static void xmore(const char *); + +int +doredotopl(void) +{ + if (last_redone_topl) + last_redone_topl = last_redone_topl->next_topl; + if (!last_redone_topl) + last_redone_topl = old_toplines; + if (last_redone_topl) + strcpy(toplines, last_redone_topl->topl_text); + redotoplin(); + return (0); +} + +static void +redotoplin(void) +{ + home(); + if (strchr(toplines, '\n')) + cl_end(); + putstr(toplines); + cl_end(); + tlx = curx; + tly = cury; + flags.toplin = 1; + if (tly > 1) + more(); +} + +void +remember_topl(void) +{ + struct topl *tl; + int cnt = OTLMAX; + + if (last_redone_topl && + !strcmp(toplines, last_redone_topl->topl_text)) + return; + if (old_toplines && + !strcmp(toplines, old_toplines->topl_text)) + return; + last_redone_topl = NULL; + tl = alloc((unsigned)(strlen(toplines) + sizeof(struct topl) + 1)); + tl->next_topl = old_toplines; + tl->topl_text = (char *)(tl + 1); + strcpy(tl->topl_text, toplines); + old_toplines = tl; + while (cnt && tl) { + cnt--; + tl = tl->next_topl; + } + if (tl && tl->next_topl) { + free(tl->next_topl); + tl->next_topl = NULL; + } +} + +void +addtopl(const char *s) +{ + curs(tlx, tly); + if (tlx + (int)strlen(s) > CO) + putsym('\n'); + putstr(s); + tlx = curx; + tly = cury; + flags.toplin = 1; +} + +static void +xmore(const char *s) /* allowed chars besides space/return */ +{ + if (flags.toplin) { + curs(tlx, tly); + if (tlx + 8 > CO) + putsym('\n'), tly++; + } + + if (flags.standout) + standoutbeg(); + putstr("--More--"); + if (flags.standout) + standoutend(); + + xwaitforspace(s); + if (flags.toplin && tly > 1) { + home(); + cl_end(); + docorner(1, tly - 1); + } + flags.toplin = 0; +} + +void +more(void) +{ + xmore(""); +} + +void +cmore(const char *s) +{ + xmore(s); +} + +void +clrlin(void) +{ + if (flags.toplin) { + home(); + cl_end(); + if (tly > 1) + docorner(1, tly - 1); + remember_topl(); + } + flags.toplin = 0; +} + +void +pline(const char *line, ...) +{ + va_list ap; + va_start(ap, line); + vpline(line, ap); + va_end(ap); +} + +/*VARARGS1*/ +void +vpline(const char *line, va_list ap) +{ + char pbuf[BUFSZ]; + char *bp = pbuf, *tl; + int n, n0; + + if (!line || !*line) + return; + if (!strchr(line, '%')) + strcpy(pbuf, line); + else + vsprintf(pbuf, line, ap); + if (flags.toplin == 1 && !strcmp(pbuf, toplines)) + return; + nscr(); /* %% */ + + /* If there is room on the line, print message on same line */ + /* But messages like "You die..." deserve their own line */ + n0 = strlen(bp); + if (flags.toplin == 1 && tly == 1 && + n0 + (int)strlen(toplines) + 3 < CO - 8 && /* room for --More-- */ + strncmp(bp, "You ", 4)) { + strcat(toplines, " "); + strcat(toplines, bp); + tlx += 2; + addtopl(bp); + return; + } + if (flags.toplin == 1) + more(); + remember_topl(); + toplines[0] = 0; + while (n0) { + if (n0 >= CO) { + /* look for appropriate cut point */ + n0 = 0; + for (n = 0; n < CO; n++) + if (bp[n] == ' ') + n0 = n; + if (!n0) + for (n = 0; n < CO - 1; n++) + if (!letter(bp[n])) + n0 = n; + if (!n0) + n0 = CO - 2; + } + strncpy((tl = eos(toplines)), bp, n0); + tl[n0] = 0; + bp += n0; + + /* remove trailing spaces, but leave one */ + while (n0 > 1 && tl[n0 - 1] == ' ' && tl[n0 - 2] == ' ') + tl[--n0] = 0; + + n0 = strlen(bp); + if (n0 && tl[0]) + strcat(tl, "\n"); + } + redotoplin(); +} + +void +putsym(char c) +{ + switch (c) { + case '\b': + backsp(); + return; + case '\n': + curx = 1; + cury++; + if (cury > tly) + tly = cury; + break; + default: + if (curx == CO) + putsym('\n'); /* 1 <= curx <= CO; avoid CO */ + else + curx++; + } + putchar(c); +} + +void +putstr(const char *s) +{ + while (*s) + putsym(*s++); +} diff --git a/hack/hack.track.c b/hack/hack.track.c new file mode 100644 index 0000000..76a398a --- /dev/null +++ b/hack/hack.track.c @@ -0,0 +1,49 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.track.c - version 1.0.2 */ +/* $FreeBSD: src/games/hack/hack.track.c,v 1.4 1999/11/16 10:26:38 marcel Exp $ */ +/* $DragonFly: src/games/hack/hack.track.c,v 1.3 2006/08/21 19:45:32 pavalos Exp $ */ + +#include "hack.h" + +#define UTSZ 50 + +coord utrack[UTSZ]; +int utcnt = 0; +int utpnt = 0; + +void +initrack(void) +{ + utcnt = utpnt = 0; +} + +/* add to track */ +void +settrack(void) +{ + if (utcnt < UTSZ) + utcnt++; + if (utpnt == UTSZ) + utpnt = 0; + utrack[utpnt].x = u.ux; + utrack[utpnt].y = u.uy; + utpnt++; +} + +coord * +gettrack(int x, int y) +{ + int i, cnt, dst; + coord tc; + + cnt = utcnt; + for (i = utpnt - 1; cnt--; i--) { + if (i == -1) + i = UTSZ - 1; + tc = utrack[i]; + dst = (x - tc.x) * (x - tc.x) + (y - tc.y) * (y - tc.y); + if (dst < 3) + return (dst ? &(utrack[i]) : 0); + } + return (0); +} diff --git a/hack/hack.trap.c b/hack/hack.trap.c new file mode 100644 index 0000000..cb41c67 --- /dev/null +++ b/hack/hack.trap.c @@ -0,0 +1,488 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.trap.c - version 1.0.3 */ +/* $FreeBSD: src/games/hack/hack.trap.c,v 1.5 1999/11/16 10:26:38 marcel Exp $ */ +/* $DragonFly: src/games/hack/hack.trap.c,v 1.5 2006/08/21 19:45:32 pavalos Exp $ */ + +#include "hack.h" + +char vowels[] = "aeiou"; + +const char *traps[] = { + " bear trap", + "n arrow trap", + " dart trap", + " trapdoor", + " teleportation trap", + " pit", + " sleeping gas trap", + " piercer", + " mimic" +}; + +static void vtele(void); +static void teleds(int, int); +static bool teleok(int, int); + +struct trap * +maketrap(int x, int y, int typ) +{ + struct trap *ttmp; + + ttmp = newtrap(); + ttmp->ttyp = typ; + ttmp->tseen = 0; + ttmp->once = 0; + ttmp->tx = x; + ttmp->ty = y; + ttmp->ntrap = ftrap; + ftrap = ttmp; + return (ttmp); +} + +void +dotrap(struct trap *trap) +{ + int ttype = trap->ttyp; + + nomul(0); + if (trap->tseen && !rn2(5) && ttype != PIT) + pline("You escape a%s.", traps[ttype]); + else { + trap->tseen = 1; + switch (ttype) { + case SLP_GAS_TRAP: + pline("A cloud of gas puts you to sleep!"); + nomul(-rnd(25)); + break; + case BEAR_TRAP: + if (Levitation) { + pline("You float over a bear trap."); + break; + } + u.utrap = 4 + rn2(4); + u.utraptype = TT_BEARTRAP; + pline("A bear trap closes on your foot!"); + break; + case PIERC: + deltrap(trap); + if (makemon(PM_PIERCER, u.ux, u.uy)) { + pline("A piercer suddenly drops from the ceiling!"); + if (uarmh) + pline("Its blow glances off your helmet."); + else + thitu(3, d(4, 6), "falling piercer"); + } + break; + case ARROW_TRAP: + pline("An arrow shoots out at you!"); + if (!thitu(8, rnd(6), "arrow")) { + mksobj_at(ARROW, u.ux, u.uy); + fobj->quan = 1; + } + break; + case TRAPDOOR: + if (!xdnstair) { + pline("A trap door in the ceiling opens and a rock falls on your head!"); + if (uarmh) + pline("Fortunately, you are wearing a helmet!"); + losehp(uarmh ? 2 : d(2, 10), "falling rock"); + mksobj_at(ROCK, u.ux, u.uy); + fobj->quan = 1; + stackobj(fobj); + if (Invisible) + newsym(u.ux, u.uy); + } else { + int newlevel = dlevel + 1; + while (!rn2(4) && newlevel < 29) + newlevel++; + pline("A trap door opens up under you!"); + if (Levitation || u.ustuck) { + pline("For some reason you don't fall in."); + break; + } + + goto_level(newlevel, FALSE); + } + break; + case DART_TRAP: + pline("A little dart shoots out at you!"); + if (thitu(7, rnd(3), "little dart")) { + if (!rn2(6)) + poisoned("dart", "poison dart"); + } else { + mksobj_at(DART, u.ux, u.uy); + fobj->quan = 1; + } + break; + case TELEP_TRAP: + if (trap->once) { + deltrap(trap); + newsym(u.ux, u.uy); + vtele(); + } else { + newsym(u.ux, u.uy); + tele(); + } + break; + case PIT: + if (Levitation) { + pline("A pit opens up under you!"); + pline("You don't fall in!"); + break; + } + pline("You fall into a pit!"); + u.utrap = rn1(6, 2); + u.utraptype = TT_PIT; + losehp(rnd(6), "fall into a pit"); + selftouch("Falling, you"); + break; + default: + impossible("You hit a trap of type %u", trap->ttyp); + } + } +} + +int +mintrap(struct monst *mtmp) +{ + struct trap *trap = t_at(mtmp->mx, mtmp->my); + int wasintrap = mtmp->mtrapped; + + if (!trap) + mtmp->mtrapped = 0; /* perhaps teleported? */ + else if (wasintrap) { + if (!rn2(40)) + mtmp->mtrapped = 0; + } else { + int tt = trap->ttyp; + int in_sight = cansee(mtmp->mx, mtmp->my); + + if (mtmp->mtrapseen & (1 << tt)) { + /* he has been in such a trap - perhaps he escapes */ + if (rn2(4)) + return (0); + } + mtmp->mtrapseen |= (1 << tt); + switch (tt) { + case BEAR_TRAP: + if (strchr(mlarge, mtmp->data->mlet)) { + if (in_sight) + pline("%s is caught in a bear trap!", + Monnam(mtmp)); + else if (mtmp->data->mlet == 'o') + pline("You hear the roaring of an angry bear!"); + mtmp->mtrapped = 1; + } + break; + case PIT: + /* there should be a mtmp/data -> floating */ + if (!strchr("EywBfk'& ", mtmp->data->mlet)) { /* ab */ + mtmp->mtrapped = 1; + if (in_sight) + pline("%s falls in a pit!", Monnam(mtmp)); + } + break; + case SLP_GAS_TRAP: + if (!mtmp->msleep && !mtmp->mfroz) { + mtmp->msleep = 1; + if (in_sight) + pline("%s suddenly falls asleep!", + Monnam(mtmp)); + } + break; + case TELEP_TRAP: + rloc(mtmp); + if (in_sight && !cansee(mtmp->mx, mtmp->my)) + pline("%s suddenly disappears!", + Monnam(mtmp)); + break; + case ARROW_TRAP: + if (in_sight) { + pline("%s is hit by an arrow!", + Monnam(mtmp)); + } + mtmp->mhp -= 3; + break; + case DART_TRAP: + if (in_sight) { + pline("%s is hit by a dart!", + Monnam(mtmp)); + } + mtmp->mhp -= 2; + /* not mondied here !! */ + break; + case TRAPDOOR: + if (!xdnstair) { + mtmp->mhp -= 10; + if (in_sight) + pline("A trap door in the ceiling opens and a rock hits %s!", monnam(mtmp)); + break; + } + if (mtmp->data->mlet != 'w') { + fall_down(mtmp); + if (in_sight) + pline("Suddenly, %s disappears out of sight.", monnam(mtmp)); + return (2); /* no longer on this level */ + } + break; + case PIERC: + break; + default: + impossible("Some monster encountered a strange trap."); + } + } + return (mtmp->mtrapped); +} + +void +selftouch(const char *arg) +{ + if (uwep && uwep->otyp == DEAD_COCKATRICE) { + pline("%s touch the dead cockatrice.", arg); + pline("You turn to stone."); + killer = objects[uwep->otyp].oc_name; + done("died"); + } +} + +void +float_up(void) +{ + if (u.utrap) { + if (u.utraptype == TT_PIT) { + u.utrap = 0; + pline("You float up, out of the pit!"); + } else { + pline("You float up, only your leg is still stuck."); + } + } else + pline("You start to float in the air!"); +} + +void +float_down(void) +{ + struct trap *trap; + + pline("You float gently to the ground."); + if ((trap = t_at(u.ux, u.uy)) != NULL) + switch (trap->ttyp) { + case PIERC: + break; + case TRAPDOOR: + if (!xdnstair || u.ustuck) + break; + /* fall into next case */ + default: + dotrap(trap); + } + pickup(1); +} + +static void +vtele(void) +{ + struct mkroom *croom; + + for (croom = &rooms[0]; croom->hx >= 0; croom++) + if (croom->rtype == VAULT) { + int x, y; + + x = rn2(2) ? croom->lx : croom->hx; + y = rn2(2) ? croom->ly : croom->hy; + if (teleok(x, y)) { + teleds(x, y); + return; + } + } + tele(); +} + +void +tele(void) +{ + coord cc; + int nux, nuy; + + if (Teleport_control) { + pline("To what position do you want to be teleported?"); + cc = getpos(1, "the desired position"); /* 1: force valid */ + /* + * possible extensions: introduce a small error if magic + * power is low; allow transfer to solid rock + */ + if (teleok(cc.x, cc.y)) { + teleds(cc.x, cc.y); + return; + } + pline("Sorry ..."); + } + do { + nux = rnd(COLNO - 1); + nuy = rn2(ROWNO); + } while (!teleok(nux, nuy)); + teleds(nux, nuy); +} + +static void +teleds(int nux, int nuy) +{ + if (Punished) + unplacebc(); + unsee(); + u.utrap = 0; + u.ustuck = 0; + u.ux = nux; + u.uy = nuy; + setsee(); + if (Punished) + placebc(1); + if (u.uswallow) { + u.uswldtim = u.uswallow = 0; + docrt(); + } + nomul(0); + if (levl[nux][nuy].typ == POOL && !Levitation) + drown(); + inshop(); + pickup(1); + if (!Blind) + read_engr_at(u.ux, u.uy); +} + +static bool +teleok(int x, int y) /* might throw him into a POOL */ +{ + return (isok(x, y) && !IS_ROCK(levl[x][y].typ) && !m_at(x, y) && + !sobj_at(ENORMOUS_ROCK, x, y) && !t_at(x, y) + ); + /* Note: gold is permitted (because of vaults) */ +} + +int +dotele(void) +{ + if ( +#ifdef WIZARD + !wizard && +#endif /* WIZARD */ + (!Teleportation || u.ulevel < 6 || + (pl_character[0] != 'W' && u.ulevel < 10))) { + pline("You are not able to teleport at will."); + return (0); + } + if (u.uhunger <= 100 || u.ustr < 6) { + pline("You miss the strength for a teleport spell."); + return (1); + } + tele(); + morehungry(100); + return (1); +} + +void +placebc(int attach) +{ + if (!uchain || !uball) { + impossible("Where are your chain and ball??"); + return; + } + uball->ox = uchain->ox = u.ux; + uball->oy = uchain->oy = u.uy; + if (attach) { + uchain->nobj = fobj; + fobj = uchain; + if (!carried(uball)) { + uball->nobj = fobj; + fobj = uball; + } + } +} + +void +unplacebc(void) +{ + if (!carried(uball)) { + freeobj(uball); + unpobj(uball); + } + freeobj(uchain); + unpobj(uchain); +} + +void +level_tele(void) +{ + int newlevel; + + if (Teleport_control) { + char buf[BUFSZ]; + + do { + pline("To what level do you want to teleport? [type a number] "); + getlin(buf); + } while (!digit(buf[0]) && (buf[0] != '-' || !digit(buf[1]))); + newlevel = atoi(buf); + } else { + newlevel = 5 + rn2(20); /* 5 - 24 */ + if (dlevel == newlevel) { + if (!xdnstair) + newlevel--; + else + newlevel++; + } + } + if (newlevel >= 30) { + if (newlevel > MAXLEVEL) + newlevel = MAXLEVEL; + pline("You arrive at the center of the earth ..."); + pline("Unfortunately it is here that hell is located."); + if (Fire_resistance) { + pline("But the fire doesn't seem to harm you."); + } else { + pline("You burn to a crisp."); + dlevel = maxdlevel = newlevel; + killer = "visit to the hell"; + done("burned"); + } + } + if (newlevel < 0) { + newlevel = 0; + pline("You are now high above the clouds ..."); + if (Levitation) { + pline("You float gently down to earth."); + done("escaped"); + } + pline("Unfortunately, you don't know how to fly."); + pline("You fall down a few thousand feet and break your neck."); + dlevel = 0; + killer = "fall"; + done("died"); + } + + goto_level(newlevel, FALSE); /* calls done("escaped") if newlevel==0 */ +} + +void +drown(void) +{ + pline("You fall into a pool!"); + pline("You can't swim!"); + if (rn2(3) < u.uluck + 2) { + /* most scrolls become unreadable */ + struct obj *obj; + + for (obj = invent; obj; obj = obj->nobj) + if (obj->olet == SCROLL_SYM && rn2(12) > u.uluck) + obj->otyp = SCR_BLANK_PAPER; + /* we should perhaps merge these scrolls ? */ + + pline("You attempt a teleport spell."); /* utcsri!carroll */ + dotele(); + if (levl[u.ux][u.uy].typ != POOL) + return; + } + pline("You drown ..."); + killer = "pool of water"; + done("drowned"); +} diff --git a/hack/hack.tty.c b/hack/hack.tty.c new file mode 100644 index 0000000..37f12bf --- /dev/null +++ b/hack/hack.tty.c @@ -0,0 +1,313 @@ +/*- + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)hack.tty.c 8.1 (Berkeley) 5/31/93 + * $FreeBSD: src/games/hack/hack.tty.c,v 1.6.2.1 2000/07/20 10:35:07 kris Exp $ + * $DragonFly: src/games/hack/hack.tty.c,v 1.4 2006/08/21 19:45:32 pavalos Exp $ + */ + +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.tty.c - version 1.0.3 */ +/* + * With thanks to the people who sent code for SYSV - hpscdi!jon, + * arnold@ucsf-cgl, wcs@bo95b, cbcephus!pds and others. + */ + +#include <termios.h> +#include "hack.h" + +/* + * Some systems may have getchar() return EOF for various reasons, and + * we should not quit before seeing at least NR_OF_EOFS consecutive EOFs. + * FIXME: is this still valid nowadays? + */ +#define NR_OF_EOFS 20 + +static char erase_char, kill_char; +static boolean settty_needed = FALSE; +struct termios inittyb, curttyb; + +static void setctty(void); + +/* + * Get initial state of terminal, set ospeed (for termcap routines) + * and switch off tab expansion if necessary. + * Called by startup() in termcap.c and after returning from ! or ^Z + */ +void +gettty(void) +{ + if (tcgetattr(fileno(stdin), &inittyb) < 0) + perror("Hack (gettty)"); + curttyb = inittyb; + erase_char = inittyb.c_cc[VERASE]; + kill_char = inittyb.c_cc[VKILL]; + getioctls(); + + /* do not expand tabs - they might be needed inside a cm sequence */ + if (curttyb.c_oflag & OXTABS) { + curttyb.c_oflag &= ~OXTABS; + setctty(); + } + settty_needed = TRUE; +} + +/* reset terminal to original state */ +void +settty(const char *s) +{ + clear_screen(); + end_screen(); + if (s) + printf("%s", s); + fflush(stdout); + if (tcsetattr(fileno(stdin), TCSANOW, &inittyb) < 0) + perror("Hack (settty)"); + flags.echo = (inittyb.c_lflag & ECHO) ? ON : OFF; + flags.cbreak = (inittyb.c_lflag & ICANON) ? OFF : ON; + setioctls(); +} + +static void +setctty(void) +{ + if (tcsetattr(fileno(stdin), TCSANOW, &curttyb) < 0) + perror("Hack (setctty)"); +} + +void +setftty(void) +{ + u_long ef = 0; /* desired value of flags & ECHO */ + u_long cf = !(ICANON); /* desired value of flags & CBREAK */ + int change = 0; + + flags.cbreak = ON; + flags.echo = OFF; + /* Should use (ECHO|CRMOD) here instead of ECHO */ + if ((curttyb.c_lflag & ECHO) != ef) { + curttyb.c_lflag &= ~ECHO; + change++; + } + if ((curttyb.c_lflag & ICANON) != cf) { + curttyb.c_lflag &= ~ICANON; + curttyb.c_lflag |= cf; + /* be satisfied with one character; no timeout */ + curttyb.c_cc[VMIN] = 1; /* was VEOF */ + curttyb.c_cc[VTIME] = 0; /* was VEOL */ + change++; + } + if (change) + setctty(); + start_screen(); +} + +/* fatal error */ +/* VARARGS1 */ +void +error(const char *s, ...) +{ + va_list ap; + + if (settty_needed) + settty(NULL); + va_start(ap, s); + vprintf(s, ap); + va_end(ap); + putchar('\n'); + exit(1); +} + +/* + * Read a line closed with '\n' into the array char bufp[BUFSZ]. + * (The '\n' is not stored. The string is closed with a '\0'.) + * Reading can be interrupted by an escape ('\033') - now the + * resulting string is "\033". + */ +void +getlin(char *bufp) +{ + char *obufp = bufp; + int c; + + flags.toplin = 2; /* nonempty, no --More-- required */ + for (;;) { + fflush(stdout); + if ((c = getchar()) == EOF) { + *bufp = 0; + return; + } + if (c == '\033') { + *obufp = c; + obufp[1] = 0; + return; + } + if (c == erase_char || c == '\b') { + if (bufp != obufp) { + bufp--; + putstr("\b \b"); /* putsym converts \b */ + } else + bell(); + } else if (c == '\n') { + *bufp = 0; + return; + } else if (' ' <= c && c < '\177') { + /* + * avoid isprint() - some people don't have it ' ' is + * not always a printing char + */ + *bufp = c; + bufp[1] = 0; + putstr(bufp); + if (bufp - obufp < BUFSZ - 1 && bufp - obufp < COLNO) + bufp++; + } else if (c == kill_char || c == '\177') { /* Robert Viduya */ + /* this test last - @ might be the kill_char */ + while (bufp != obufp) { + bufp--; + putstr("\b \b"); + } + } else + bell(); + } +} + +void +getret(void) +{ + cgetret(""); +} + +void +cgetret(const char *s) +{ + putsym('\n'); + if (flags.standout) + standoutbeg(); + putstr("Hit "); + putstr(flags.cbreak ? "space" : "return"); + putstr(" to continue: "); + if (flags.standout) + standoutend(); + xwaitforspace(s); +} + +char morc; /* tell the outside world what char he used */ + +void +xwaitforspace(const char *s) /* chars allowed besides space or return */ +{ + int c; + + morc = 0; + while ((c = readchar()) != '\n') { + if (flags.cbreak) { + if (c == ' ') + break; + if (s && strchr(s, c)) { + morc = c; + break; + } + bell(); + } + } +} + +char * +parse(void) +{ + static char inputline[COLNO]; + int foo; + + flags.move = 1; + if (!Invisible) + curs_on_u(); + else + home(); + while ((foo = readchar()) >= '0' && foo <= '9') + multi = 10 * multi + foo - '0'; + if (multi) { + multi--; + save_cm = inputline; + } + inputline[0] = foo; + inputline[1] = 0; + if (foo == 'f' || foo == 'F') { + inputline[1] = getchar(); +#ifdef QUEST + if (inputline[1] == foo) + inputline[2] = getchar(); + else +#endif /* QUEST */ + inputline[2] = 0; + } + if (foo == 'm' || foo == 'M') { + inputline[1] = getchar(); + inputline[2] = 0; + } + clrlin(); + return (inputline); +} + +char +readchar(void) +{ + int sym; + + fflush(stdout); + if ((sym = getchar()) == EOF) +#ifdef NR_OF_EOFS + { /* + * Some SYSV systems seem to return EOFs for various reasons + * (?like when one hits break or for interrupted systemcalls?), + * and we must see several before we quit. + */ + int cnt = NR_OF_EOFS; + while (cnt--) { + clearerr(stdin); /* omit if clearerr is undefined */ + if ((sym = getchar()) != EOF) + goto noteof; + } + end_of_input(); +noteof:; + } +#else + end_of_input(); +#endif /* NR_OF_EOFS */ + if (flags.toplin == 1) + flags.toplin = 2; + return ((char)sym); +} + +void +end_of_input(void) +{ + settty("End of input?\n"); + clearlocks(); + exit(0); +} diff --git a/hack/hack.u_init.c b/hack/hack.u_init.c new file mode 100644 index 0000000..4bb25cd --- /dev/null +++ b/hack/hack.u_init.c @@ -0,0 +1,385 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.u_init.c - version 1.0.3 */ +/* $FreeBSD: src/games/hack/hack.u_init.c,v 1.4 1999/11/16 02:57:13 billf Exp $ */ +/* $DragonFly: src/games/hack/hack.u_init.c,v 1.5 2006/08/21 19:45:32 pavalos Exp $ */ + +#include "hack.h" +#define Strcpy (void) strcpy +#define Strcat (void) strcat +#define UNDEF_TYP 0 +#define UNDEF_SPE '\177' + +struct you zerou; +char pl_character[PL_CSIZ]; +/* + * must all have distinct first letter + * roles[4] may be changed to -man + */ +char roles[][12 + 1] = { + "Tourist", "Speleologist", "Fighter", "Knight", + "Cave-man", "Wizard" +}; +#define NR_OF_ROLES SIZE(roles) +char rolesyms[NR_OF_ROLES + 1]; /* filled by u_init() */ + +struct trobj { + uchar trotyp; + schar trspe; + char trolet; + Bitfield(trquan,6); + Bitfield(trknown,1); +}; + +#ifdef WIZARD +struct trobj Extra_objs[] = { + { 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0 } +}; +#endif /* WIZARD */ + +struct trobj Cave_man[] = { + { MACE, 1, WEAPON_SYM, 1, 1 }, + { BOW, 1, WEAPON_SYM, 1, 1 }, + { ARROW, 0, WEAPON_SYM, 25, 1 }, /* quan is variable */ + { LEATHER_ARMOR, 0, ARMOR_SYM, 1, 1 }, + { 0, 0, 0, 0, 0} +}; + +struct trobj Fighter[] = { + { TWO_HANDED_SWORD, 0, WEAPON_SYM, 1, 1 }, + { RING_MAIL, 0, ARMOR_SYM, 1, 1 }, + { 0, 0, 0, 0, 0 } +}; + +struct trobj Knight[] = { + { LONG_SWORD, 0, WEAPON_SYM, 1, 1 }, + { SPEAR, 2, WEAPON_SYM, 1, 1 }, + { RING_MAIL, 1, ARMOR_SYM, 1, 1 }, + { HELMET, 0, ARMOR_SYM, 1, 1 }, + { SHIELD, 0, ARMOR_SYM, 1, 1 }, + { PAIR_OF_GLOVES, 0, ARMOR_SYM, 1, 1 }, + { 0, 0, 0, 0, 0 } +}; + +struct trobj Speleologist[] = { + { STUDDED_LEATHER_ARMOR, 0, ARMOR_SYM, 1, 1 }, + { UNDEF_TYP, 0, POTION_SYM, 2, 0 }, + { FOOD_RATION, 0, FOOD_SYM, 3, 1 }, + { PICK_AXE, UNDEF_SPE, TOOL_SYM, 1, 0 }, + { ICE_BOX, 0, TOOL_SYM, 1, 0 }, + { 0, 0, 0, 0, 0} +}; + +struct trobj Tinopener[] = { + { CAN_OPENER, 0, TOOL_SYM, 1, 1 }, + { 0, 0, 0, 0, 0 } +}; + +struct trobj Tourist[] = { + { UNDEF_TYP, 0, FOOD_SYM, 10, 1 }, + { POT_EXTRA_HEALING, 0, POTION_SYM, 2, 0 }, + { EXPENSIVE_CAMERA, 0, TOOL_SYM, 1, 1 }, + { DART, 2, WEAPON_SYM, 25, 1 }, /* quan is variable */ + { 0, 0, 0, 0, 0 } +}; + +struct trobj Wizard[] = { + { ELVEN_CLOAK, 0, ARMOR_SYM, 1, 1 }, + { UNDEF_TYP, UNDEF_SPE, WAND_SYM, 2, 0 }, + { UNDEF_TYP, UNDEF_SPE, RING_SYM, 2, 0 }, + { UNDEF_TYP, UNDEF_SPE, POTION_SYM, 2, 0 }, + { UNDEF_TYP, UNDEF_SPE, SCROLL_SYM, 3, 0 }, + { 0, 0, 0, 0, 0 } +}; + +static void ini_inv(struct trobj *); +#ifdef WIZARD +static void wiz_inv(void); +#endif +static int role_index(char); + +void +u_init(void) +{ + int i; + char exper = 'y', pc; + + if (flags.female) /* should have been set in HACKOPTIONS */ + strlcpy(roles[4], "Cave-woman", sizeof(roles[4])); + for (i = 0; i < NR_OF_ROLES; i++) + rolesyms[i] = roles[i][0]; + rolesyms[i] = 0; + + if ((pc = pl_character[0]) != '\0') { + if ('a' <= pc && pc <= 'z') + pc += 'A' - 'a'; + if ((i = role_index(pc)) >= 0) + goto got_suffix; /* implies experienced */ + printf("\nUnknown role: %c\n", pc); + pl_character[0] = pc = 0; + } + + printf("\nAre you an experienced player? [ny] "); + + while (!strchr("ynYN \n\004", (exper = readchar()))) + bell(); + if (exper == '\004') /* Give him an opportunity to get out */ + end_of_input(); + printf("%c\n", exper); /* echo */ + if (strchr("Nn \n", exper)) { + exper = 0; + goto beginner; + } + + printf("\nTell me what kind of character you are:\n"); + printf("Are you"); + for (i = 0; i < NR_OF_ROLES; i++) { + printf(" a %s", roles[i]); + if (i == 2) /* %% */ + printf(",\n\t"); + else if (i < NR_OF_ROLES - 2) + printf(","); + else if (i == NR_OF_ROLES - 2) + printf(" or"); + } + printf("? [%s] ", rolesyms); + + while ((pc = readchar()) != '\0') { + if ('a' <= pc && pc <= 'z') + pc += 'A' - 'a'; + if ((i = role_index(pc)) >= 0) { + printf("%c\n", pc); /* echo */ + fflush(stdout); /* should be seen */ + break; + } + if (pc == '\n') + break; + if (pc == '\004') /* Give him the opportunity to get out */ + end_of_input(); + bell(); + } + if (pc == '\n') + pc = 0; + +beginner: + if (!pc) { + printf("\nI'll choose a character for you.\n"); + i = rn2(NR_OF_ROLES); + pc = rolesyms[i]; + printf("This game you will be a%s %s.\n", + exper ? "n experienced" : "", + roles[i]); + getret(); + /* give him some feedback in case mklev takes much time */ + putchar('\n'); + fflush(stdout); + } + if (exper) + roles[i][0] = pc; + +got_suffix: + + strncpy(pl_character, roles[i], PL_CSIZ - 1); + pl_character[PL_CSIZ - 1] = 0; + flags.beginner = 1; + u = zerou; + u.usym = '@'; + u.ulevel = 1; + init_uhunger(); +#ifdef QUEST + u.uhorizon = 6; +#endif /* QUEST */ + uarm = uarm2 = uarmh = uarms = uarmg = uwep = uball = uchain = + uleft = uright = 0; + + switch (pc) { + case 'c': + case 'C': + Cave_man[2].trquan = 12 + rnd(9) * rnd(9); + u.uhp = u.uhpmax = 16; + u.ustr = u.ustrmax = 18; + ini_inv(Cave_man); + break; + case 't': + case 'T': + Tourist[3].trquan = 20 + rnd(20); + u.ugold = u.ugold0 = rnd(1000); + u.uhp = u.uhpmax = 10; + u.ustr = u.ustrmax = 8; + ini_inv(Tourist); + if (!rn2(25)) + ini_inv(Tinopener); + break; + case 'w': + case 'W': + for (i = 1; i <= 4; i++) + if (!rn2(5)) + Wizard[i].trquan += rn2(3) - 1; + u.uhp = u.uhpmax = 15; + u.ustr = u.ustrmax = 16; + ini_inv(Wizard); + break; + case 's': + case 'S': + Fast = INTRINSIC; + Stealth = INTRINSIC; + u.uhp = u.uhpmax = 12; + u.ustr = u.ustrmax = 10; + ini_inv(Speleologist); + if (!rn2(10)) + ini_inv(Tinopener); + break; + case 'k': + case 'K': + u.uhp = u.uhpmax = 12; + u.ustr = u.ustrmax = 10; + ini_inv(Knight); + break; + case 'f': + case 'F': + u.uhp = u.uhpmax = 14; + u.ustr = u.ustrmax = 17; + ini_inv(Fighter); + break; + default: /* impossible */ + u.uhp = u.uhpmax = 12; + u.ustr = u.ustrmax = 16; + } + find_ac(); + if (!rn2(20)) { + int d1 = rn2(7) - 2; /* biased variation */ + u.ustr += d1; + u.ustrmax += d1; + } + +#ifdef WIZARD + if (wizard) + wiz_inv(); +#endif /* WIZARD */ + + /* make sure he can carry all he has - especially for T's */ + while (inv_weight() > 0 && u.ustr < 118) + u.ustr++, u.ustrmax++; +} + +static void +ini_inv(struct trobj *trop) +{ + struct obj *obj; + + while (trop->trolet) { + obj = mkobj(trop->trolet); + obj->known = trop->trknown; + /* not obj->dknown = 1; - let him look at it at least once */ + obj->cursed = 0; + if (obj->olet == WEAPON_SYM) { + obj->quan = trop->trquan; + trop->trquan = 1; + } + if (trop->trspe != UNDEF_SPE) + obj->spe = trop->trspe; + if (trop->trotyp != UNDEF_TYP) + obj->otyp = trop->trotyp; + else if (obj->otyp == WAN_WISHING) /* gitpyr!robert */ + obj->otyp = WAN_DEATH; + obj->owt = weight(obj); /* defined after setting otyp+quan */ + obj = addinv(obj); + if (obj->olet == ARMOR_SYM) { + switch (obj->otyp) { + case SHIELD: + if (!uarms) + setworn(obj, W_ARMS); + break; + case HELMET: + if (!uarmh) + setworn(obj, W_ARMH); + break; + case PAIR_OF_GLOVES: + if (!uarmg) + setworn(obj, W_ARMG); + break; + case ELVEN_CLOAK: + if (!uarm2) + setworn(obj, W_ARM); + break; + default: + if (!uarm) + setworn(obj, W_ARM); + } + } + if (obj->olet == WEAPON_SYM) + if (!uwep) + setuwep(obj); +#ifndef PYRAMID_BUG + if (--trop->trquan) /* make a similar object */ + continue; +#else + if (trop->trquan) { /* check if zero first */ + --trop->trquan; + if (trop->trquan) + continue; /* make a similar object */ + } +#endif /* PYRAMID_BUG */ + trop++; + } +} + +#ifdef WIZARD +static void +wiz_inv(void) +{ + struct trobj *trop = &Extra_objs[0]; + char *ep = getenv("INVENT"); + int type; + + while (ep && *ep) { + type = atoi(ep); + ep = strchr(ep, ','); + if (ep) + while (*ep == ',' || *ep == ' ') + ep++; + if (type <= 0 || type > NROFOBJECTS) + continue; + trop->trotyp = type; + trop->trolet = objects[type].oc_olet; + trop->trspe = 4; + trop->trknown = 1; + trop->trquan = 1; + ini_inv(trop); + } + /* give him a wand of wishing by default */ + trop->trotyp = WAN_WISHING; + trop->trolet = WAND_SYM; + trop->trspe = 20; + trop->trknown = 1; + trop->trquan = 1; + ini_inv(trop); +} +#endif /* WIZARD */ + +void +plnamesuffix(void) +{ + char *p; + + if ((p = strrchr(plname, '-')) != NULL) { + *p = 0; + pl_character[0] = p[1]; + pl_character[1] = 0; + if (!plname[0]) { + askname(); + plnamesuffix(); + } + } +} + +static int +role_index(char pc) +{ /* must be called only from u_init() */ + /* so that rolesyms[] is defined */ + char *cp; + + if ((cp = strchr(rolesyms, pc)) != NULL) + return (cp - rolesyms); + return (-1); +} diff --git a/hack/hack.unix.c b/hack/hack.unix.c new file mode 100644 index 0000000..abb0981 --- /dev/null +++ b/hack/hack.unix.c @@ -0,0 +1,421 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.unix.c - version 1.0.3 */ +/* $FreeBSD: src/games/hack/hack.unix.c,v 1.8 1999/11/16 02:57:13 billf Exp $ */ + +/* This file collects some Unix dependencies; hack.pager.c contains some more */ + +/* + * The time is used for: + * - seed for random() + * - year on tombstone and yymmdd in record file + * - phase of the moon (various monsters react to NEW_MOON or FULL_MOON) + * - night and midnight (the undead are dangerous at midnight) + * - determination of what files are "very old" + */ + +#include <errno.h> +#include "hack.h" + +#include <sys/types.h> /* for time_t and stat */ +#include <sys/stat.h> +#include <time.h> + +static struct tm *getlt(void); +static bool veryold(int); +#ifdef MAIL +static void newmail(void); +static void mdrush(struct monst *, bool); +#endif + +void +setrandom(void) +{ + srandomdev(); +} + +static struct tm * +getlt(void) +{ + time_t date; + + time(&date); + return (localtime(&date)); +} + +int +getyear(void) +{ + return (1900 + getlt()->tm_year); +} + +char * +getdate(void) +{ + static char datestr[7]; + struct tm *lt = getlt(); + + snprintf(datestr, sizeof(datestr), "%02d%02d%02d", + lt->tm_year % 100, lt->tm_mon + 1, lt->tm_mday); + return (datestr); +} + +int +phase_of_the_moon(void) /* 0-7, with 0: new, 4: full */ +{ /* moon period: 29.5306 days */ + /* year: 365.2422 days */ + struct tm *lt = getlt(); + int epact, diy, golden; + + diy = lt->tm_yday; + golden = (lt->tm_year % 19) + 1; + epact = (11 * golden + 18) % 30; + if ((epact == 25 && golden > 11) || epact == 24) + epact++; + + return ((((((diy + epact) * 6) + 11) % 177) / 22) & 7); +} + +bool +night(void) +{ + int hour = getlt()->tm_hour; + + return (hour < 6 || hour > 21); +} + +bool +midnight(void) +{ + return (getlt()->tm_hour == 0); +} + +struct stat buf, hbuf; + +void +gethdate(const char *name) +{ +/* old version - for people short of space */ + char *np; + + /* 20150213 bkw: try /usr/games/hack if no /usr/games/hide/hack */ + name = "/usr/games/hide/hack"; + if (stat(name, &hbuf)) name = "/usr/games/hack"; + if (stat(name, &hbuf)) + error("Cannot get status of %s.", + (np = strrchr(name, '/')) ? np + 1 : name); +} + +bool +uptodate(int fd) +{ + if (fstat(fd, &buf)) { + pline("Cannot get status of saved level? "); + return (0); + } + if (buf.st_mtime < hbuf.st_mtime) { + pline("Saved level is out of date. "); + return (0); + } + return (1); +} + +/* see whether we should throw away this xlock file */ +static bool +veryold(int fd) +{ + int i; + time_t date; + + if (fstat(fd, &buf)) /* cannot get status */ + return (0); + if (buf.st_size != sizeof(int)) /* not an xlock file */ + return (0); + time(&date); + if (date - buf.st_mtime < 3L * 24L * 60L * 60L) { /* recent */ + int lockedpid; /* should be the same size as hackpid */ + + if (read(fd, (char *)&lockedpid, sizeof(lockedpid)) != + sizeof(lockedpid)) + /* strange ... */ + return (0); + + /* From: Rick Adams <seismo!rick> */ + /* This will work on 4.1cbsd, 4.2bsd and system 3? & 5. */ + /* It will do nothing on V7 or 4.1bsd. */ + if (!(kill(lockedpid, 0) == -1 && errno == ESRCH)) + return (0); + } + close(fd); + for (i = 1; i <= MAXLEVEL; i++) { /* try to remove all */ + glo(i); + unlink(lock); + } + glo(0); + if (unlink(lock)) /* cannot remove it */ + return (0); + return (1); /* success! */ +} + +void +getlock(void) +{ + int i = 0, fd; + + fflush(stdout); + + /* we ignore QUIT and INT at this point */ + if (link(HLOCK, LLOCK) == -1) { + int errnosv = errno; + + perror(HLOCK); + printf("Cannot link %s to %s\n", LLOCK, HLOCK); + switch (errnosv) { + case ENOENT: + printf("Perhaps there is no (empty) file %s ?\n", HLOCK); + break; + case EACCES: + printf("It seems you don't have write permission here.\n"); + break; + case EEXIST: + printf("(Try again or rm %s.)\n", LLOCK); + break; + default: + printf("I don't know what is wrong."); + } + getret(); + error("%s", ""); + /* NOTREACHED */ + } + + regularize(lock); + glo(0); + if (locknum > 25) + locknum = 25; + + do { + if (locknum) + lock[0] = 'a' + i++; + + if ((fd = open(lock, O_RDONLY)) == -1) { + if (errno == ENOENT) /* no such file */ + goto gotlock; + perror(lock); + unlink(LLOCK); + error("Cannot open %s", lock); + } + + if (veryold(fd)) /* if true, this closes fd and unlinks lock */ + goto gotlock; + close(fd); + } while (i < locknum); + + unlink(LLOCK); + error(locknum ? "Too many hacks running now." + : "There is a game in progress under your name."); +gotlock: + fd = creat(lock, FMASK); + if (unlink(LLOCK) == -1) + error("Cannot unlink %s.", LLOCK); + if (fd == -1) { + error("cannot creat lock file."); + } else { + if (write(fd, (char *)&hackpid, sizeof(hackpid)) + != sizeof(hackpid)) + error("cannot write lock"); + if (close(fd) == -1) + error("cannot close lock"); + } +} + +#ifdef MAIL + +/* + * Notify user when new mail has arrived. [Idea from Merlyn Leroy, but + * I don't know the details of his implementation.] + * { Later note: he disliked my calling a general mailreader and felt that + * hack should do the paging itself. But when I get mail, I want to put it + * in some folder, reply, etc. - it would be unreasonable to put all these + * functions in hack. } + * The mail daemon '2' is at present not a real monster, but only a visual + * effect. Thus, makemon() is superfluous. This might become otherwise, + * however. The motion of '2' is less restrained than usual: diagonal moves + * from a DOOR are possible. He might also use SDOOR's. Also, '2' is visible + * in a ROOM, even when you are Blind. + * Its path should be longer when you are Telepat-hic and Blind. + * + * Interesting side effects: + * - You can get rich by sending yourself a lot of mail and selling + * it to the shopkeeper. Unfortunately mail isn't very valuable. + * - You might die in case '2' comes along at a critical moment during + * a fight and delivers a scroll the weight of which causes you to + * collapse. + * + * Possible extensions: + * - Open the file MAIL and do fstat instead of stat for efficiency. + * (But sh uses stat, so this cannot be too bad.) + * - Examine the mail and produce a scroll of mail called "From somebody". + * - Invoke MAILREADER in such a way that only this single letter is read. + * + * - Make him lose his mail when a Nymph steals the letter. + * - Do something to the text when the scroll is enchanted or cancelled. + */ +#include "def.mkroom.h" +static struct stat omstat, nmstat; +static char *mailbox; +static long laststattime; + +void +getmailstatus(void) +{ + if (!(mailbox = getenv("MAIL"))) + return; + if (stat(mailbox, &omstat)) { +#ifdef PERMANENT_MAILBOX + pline("Cannot get status of MAIL=%s .", mailbox); + mailbox = NULL; +#else + omstat.st_mtime = 0; +#endif /* PERMANENT_MAILBOX */ + } +} + +void +ckmailstatus(void) +{ + if (!mailbox +#ifdef MAILCKFREQ + || moves < laststattime + MAILCKFREQ +#endif /* MAILCKFREQ */ + ) + return; + laststattime = moves; + if (stat(mailbox, &nmstat)) { +#ifdef PERMANENT_MAILBOX + pline("Cannot get status of MAIL=%s anymore.", mailbox); + mailbox = NULL; +#else + nmstat.st_mtime = 0; +#endif /* PERMANENT_MAILBOX */ + } else if (nmstat.st_mtime > omstat.st_mtime) { + if (nmstat.st_size) + newmail(); + getmailstatus(); /* might be too late ... */ + } +} + +static void +newmail(void) +{ + /* produce a scroll of mail */ + struct obj *obj; + struct monst *md; + extern struct permonst pm_mail_daemon; + + obj = mksobj(SCR_MAIL); + if (md = makemon(&pm_mail_daemon, u.ux, u.uy)) /* always succeeds */ + mdrush(md, 0); + + pline("\"Hello, %s! I have some mail for you.\"", plname); + if (md) { + if (dist(md->mx, md->my) > 2) + pline("\"Catch!\""); + more(); + + /* let him disappear again */ + mdrush(md, 1); + mondead(md); + } + + obj = addinv(obj); + identify(obj); /* set known and do prinv() */ +} + +/* make md run through the cave */ +static void +mdrush(struct monst *md, bool away) +{ + int uroom = inroom(u.ux, u.uy); + + if (uroom >= 0) { + int tmp = rooms[uroom].fdoor; + int cnt = rooms[uroom].doorct; + int fx = u.ux, fy = u.uy; + while (cnt--) { + if (dist(fx, fy) < dist(doors[tmp].x, doors[tmp].y)) { + fx = doors[tmp].x; + fy = doors[tmp].y; + } + tmp++; + } + tmp_at(-1, md->data->mlet); /* open call */ + if (away) { /* interchange origin and destination */ + unpmon(md); + tmp = fx; + fx = md->mx; + md->mx = tmp; + tmp = fy; + fy = md->my; + md->my = tmp; + } + while (fx != md->mx || fy != md->my) { + int dx, dy, nfx = fx, nfy = fy, d1, d2; + + tmp_at(fx, fy); + d1 = DIST(fx, fy, md->mx, md->my); + for (dx = -1; dx <= 1; dx++) + for (dy = -1; dy <= 1; dy++) + if (dx || dy) { + d2 = DIST(fx + dx, fy + dy, md->mx, md->my); + if (d2 < d1) { + d1 = d2; + nfx = fx + dx; + nfy = fy + dy; + } + } + if (nfx != fx || nfy != fy) { + fx = nfx; + fy = nfy; + } else { + if (!away) { + md->mx = fx; + md->my = fy; + } + break; + } + } + tmp_at(-1, -1); /* close call */ + } + if (!away) + pmon(md); +} + +void +readmail(void) +{ +#ifdef DEF_MAILREADER /* This implies that UNIX is defined */ + char *mr = NULL; + + more(); + if (!(mr = getenv("MAILREADER"))) + mr = DEF_MAILREADER; + if (child(1)) { + execl(mr, mr, NULL); + exit(1); + } +#else /* DEF_MAILREADER */ + page_file(mailbox, FALSE); +#endif /* DEF_MAILREADER */ + /* get new stat; not entirely correct: there is a small time + * window where we do not see new mail */ + getmailstatus(); +} +#endif /* MAIL */ + +void +regularize(char *s) /* normalize file name - we don't like ..'s or /'s */ +{ + char *lp; + + while ((lp = strchr(s, '.')) || (lp = strchr(s, '/'))) + *lp = '_'; +} diff --git a/hack/hack.vault.c b/hack/hack.vault.c new file mode 100644 index 0000000..a02d024 --- /dev/null +++ b/hack/hack.vault.c @@ -0,0 +1,310 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.vault.c - version 1.0.2 */ +/* $FreeBSD: src/games/hack/hack.vault.c,v 1.4 1999/11/16 10:26:38 marcel Exp $ */ + +#include "hack.h" +#ifdef QUEST +void +setgd(void) +{ +} + +int +gd_move(void) +{ + return (2); +} + +void +gddead(struct monst *mtmp __attribute__((unused))) +{ +} + +void +replgd(struct monst *mtmp __attribute__((unused)), struct monst *mtmp2 __unused) +{ +} + +void +invault(void) +{ +} + +#else + +#define FCSIZ (ROWNO + COLNO) +struct fakecorridor { + xchar fx, fy, ftyp; +}; + +struct egd { + int fcbeg, fcend; /* fcend: first unused pos */ + xchar gdx, gdy; /* goal of guard's walk */ + unsigned gddone:1; + struct fakecorridor fakecorr[FCSIZ]; +}; + +static struct permonst pm_guard = +{ "guard", '@', 12, 12, -1, 4, 10, sizeof(struct egd) }; + +static struct monst *guard; +static int gdlevel; +#define EGD ((struct egd *)(&(guard->mextra[0]))) + +static void restfakecorr(void); +static bool goldincorridor(void); + +static void +restfakecorr(void) +{ + int fcx, fcy, fcbeg; + struct rm *crm; + + while ((fcbeg = EGD->fcbeg) < EGD->fcend) { + fcx = EGD->fakecorr[fcbeg].fx; + fcy = EGD->fakecorr[fcbeg].fy; + if ((u.ux == fcx && u.uy == fcy) || cansee(fcx, fcy) || + m_at(fcx, fcy)) + return; + crm = &levl[fcx][fcy]; + crm->typ = EGD->fakecorr[fcbeg].ftyp; + if (!crm->typ) + crm->seen = 0; + newsym(fcx, fcy); + EGD->fcbeg++; + } + /* it seems he left the corridor - let the guard disappear */ + mondead(guard); + guard = NULL; +} + +static bool +goldincorridor(void) +{ + int fci; + + for (fci = EGD->fcbeg; fci < EGD->fcend; fci++) + if (g_at(EGD->fakecorr[fci].fx, EGD->fakecorr[fci].fy)) + return (1); + return (0); +} + +void +setgd(void) +{ + struct monst *mtmp; + + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) + if (mtmp->isgd) { + guard = mtmp; + gdlevel = dlevel; + return; + } + guard = NULL; +} + +void +invault(void) +{ + int tmp = inroom(u.ux, u.uy); + + if (tmp < 0 || rooms[tmp].rtype != VAULT) { + u.uinvault = 0; + return; + } + if (++u.uinvault % 50 == 0 && (!guard || gdlevel != dlevel)) { + char buf[BUFSZ]; + int x, y, dd, gx, gy; + + /* first find the goal for the guard */ + for (dd = 1; (dd < ROWNO || dd < COLNO); dd++) { + for (y = u.uy - dd; y <= u.uy + dd; y++) { + if (y < 0 || y > ROWNO - 1) + continue; + for (x = u.ux - dd; x <= u.ux + dd; x++) { + if (y != u.uy - dd && y != u.uy + dd && x != u.ux - dd) + x = u.ux + dd; + if (x < 0 || x > COLNO - 1) + continue; + if (levl[x][y].typ == CORR) + goto fnd; + } + } + } + impossible("Not a single corridor on this level??"); + tele(); + return; +fnd: + gx = x; + gy = y; + + /* next find a good place for a door in the wall */ + x = u.ux; + y = u.uy; + while (levl[x][y].typ == ROOM) { + int dx, dy; + + dx = (gx > x) ? 1 : (gx < x) ? -1 : 0; + dy = (gy > y) ? 1 : (gy < y) ? -1 : 0; + if (abs(gx - x) >= abs(gy - y)) + x += dx; + else + y += dy; + } + + /* make something interesting happen */ + if (!(guard = makemon(&pm_guard, x, y))) + return; + guard->isgd = guard->mpeaceful = 1; + EGD->gddone = 0; + gdlevel = dlevel; + if (!cansee(guard->mx, guard->my)) { + mondead(guard); + guard = NULL; + return; + } + + pline("Suddenly one of the Vault's guards enters!"); + pmon(guard); + do { + pline("\"Hello stranger, who are you?\" - "); + getlin(buf); + } while (!letter(buf[0])); + + if (!strcmp(buf, "Croesus") || !strcmp(buf, "Kroisos")) { + pline("\"Oh, yes - of course. Sorry to have disturbed you.\""); + mondead(guard); + guard = NULL; + return; + } + clrlin(); + pline("\"I don't know you.\""); + if (!u.ugold) + pline("\"Please follow me.\""); + else { + pline("\"Most likely all that gold was stolen from this vault.\""); + pline("\"Please drop your gold (say d$ ) and follow me.\""); + } + EGD->gdx = gx; + EGD->gdy = gy; + EGD->fcbeg = 0; + EGD->fakecorr[0].fx = x; + EGD->fakecorr[0].fy = y; + EGD->fakecorr[0].ftyp = levl[x][y].typ; + levl[x][y].typ = DOOR; + EGD->fcend = 1; + } +} + +int +gd_move(void) +{ + int x, y, dx, dy, gx, gy, nx, ny, typ; + struct fakecorridor *fcp; + struct rm *crm; + + if (!guard || gdlevel != dlevel) { + impossible("Where is the guard?"); + return (2); /* died */ + } + if (u.ugold || goldincorridor()) + return (0); /* didnt move */ + if (dist(guard->mx, guard->my) > 1 || EGD->gddone) { + restfakecorr(); + return (0); /* didnt move */ + } + x = guard->mx; + y = guard->my; + /* look around (hor & vert only) for accessible places */ + for (nx = x - 1; nx <= x + 1; nx++) + for (ny = y - 1; ny <= y + 1; ny++) { + if (nx == x || ny == y) + if (nx != x || ny != y) + if (isok(nx, ny)) + if (!IS_WALL(typ = (crm = &levl[nx][ny])->typ) && typ != POOL) { + int i; + for (i = EGD->fcbeg; i < EGD->fcend; i++) + if (EGD->fakecorr[i].fx == nx && + EGD->fakecorr[i].fy == ny) + goto nextnxy; + if ((i = inroom(nx, ny)) >= 0 && rooms[i].rtype == VAULT) + goto nextnxy; + /* seems we found a good place to leave him alone */ + EGD->gddone = 1; + if (ACCESSIBLE(typ)) + goto newpos; + crm->typ = (typ == SCORR) ? CORR : DOOR; + goto proceed; + } +nextnxy: ; + } + nx = x; + ny = y; + gx = EGD->gdx; + gy = EGD->gdy; + dx = (gx > x) ? 1 : (gx < x) ? -1 : 0; + dy = (gy > y) ? 1 : (gy < y) ? -1 : 0; + if (abs(gx - x) >= abs(gy - y)) + nx += dx; + else + ny += dy; + + while ((typ = (crm = &levl[nx][ny])->typ) != 0) { + /* in view of the above we must have IS_WALL(typ) or typ == POOL */ + /* must be a wall here */ + if (isok(nx + nx - x, ny + ny - y) && typ != POOL && + ZAP_POS(levl[nx + nx - x][ny + ny - y].typ)) { + crm->typ = DOOR; + goto proceed; + } + if (dy && nx != x) { + nx = x; + ny = y + dy; + continue; + } + if (dx && ny != y) { + ny = y; + nx = x + dx; + dy = 0; + continue; + } + /* I don't like this, but ... */ + crm->typ = DOOR; + goto proceed; + } + crm->typ = CORR; +proceed: + if (cansee(nx, ny)) { + mnewsym(nx, ny); + prl(nx, ny); + } + fcp = &(EGD->fakecorr[EGD->fcend]); + if (EGD->fcend++ == FCSIZ) + panic("fakecorr overflow"); + fcp->fx = nx; + fcp->fy = ny; + fcp->ftyp = typ; +newpos: + if (EGD->gddone) + nx = ny = 0; + guard->mx = nx; + guard->my = ny; + pmon(guard); + restfakecorr(); + return (1); +} + +void +gddead(void) +{ + guard = NULL; +} + +void +replgd(struct monst *mtmp, struct monst *mtmp2) +{ + if (mtmp == guard) + guard = mtmp2; +} + +#endif /* QUEST */ diff --git a/hack/hack.version.c b/hack/hack.version.c new file mode 100644 index 0000000..84e93af --- /dev/null +++ b/hack/hack.version.c @@ -0,0 +1,20 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.version.c - version 1.0.3 */ +/* $FreeBSD: src/games/hack/hack.version.c,v 1.3 1999/08/27 23:29:05 peter Exp $ */ +/* $DragonFly: src/games/hack/hack.version.c,v 1.4 2006/08/21 19:45:32 pavalos Exp $ */ + +#include "date.h" +#include "hack.h" + +int +doversion(void) +{ + pline("%s 1.0.3 - last edit %s.", ( +#ifdef QUEST + "Quest" +#else + "Hack" +#endif /* QUEST */ + ), datestring); + return (0); +} diff --git a/hack/hack.wield.c b/hack/hack.wield.c new file mode 100644 index 0000000..1cf5ea7 --- /dev/null +++ b/hack/hack.wield.c @@ -0,0 +1,111 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.wield.c - version 1.0.3 */ +/* $FreeBSD: src/games/hack/hack.wield.c,v 1.4 1999/11/16 10:26:38 marcel Exp $ */ +/* $DragonFly: src/games/hack/hack.wield.c,v 1.4 2006/08/21 19:45:32 pavalos Exp $ */ + +#include "hack.h" +extern struct obj zeroobj; + +void +setuwep(struct obj *obj) +{ + setworn(obj, W_WEP); +} + +int +dowield(void) +{ + struct obj *wep; + int res = 0; + + multi = 0; + if (!(wep = getobj("#-)", "wield"))) + ; /* nothing */ + else if (uwep == wep) + pline("You are already wielding that!"); + else if (uwep && uwep->cursed) + pline("The %s welded to your hand!", + aobjnam(uwep, "are")); + else if (wep == &zeroobj) { + if (uwep == 0) { + pline("You are already empty handed."); + } else { + setuwep(NULL); + res++; + pline("You are empty handed."); + } + } else if (uarms && wep->otyp == TWO_HANDED_SWORD) + pline("You cannot wield a two-handed sword and wear a shield."); + else if (wep->owornmask & (W_ARMOR | W_RING)) + pline("You cannot wield that!"); + else { + setuwep(wep); + res++; + if (uwep->cursed) + pline("The %s %s to your hand!", + aobjnam(uwep, "weld"), + (uwep->quan == 1) ? "itself" : "themselves"); /* a3 */ + else + prinv(uwep); + } + return (res); +} + +void +corrode_weapon(void) +{ + if (!uwep || uwep->olet != WEAPON_SYM) /* %% */ + return; + if (uwep->rustfree) + pline("Your %s not affected.", aobjnam(uwep, "are")); + else { + pline("Your %s!", aobjnam(uwep, "corrode")); + uwep->spe--; + } +} + +bool +chwepon(struct obj *otmp, int amount) +{ + const char *color = (amount < 0) ? "black" : "green"; + const char *ltime; + + if (!uwep || uwep->olet != WEAPON_SYM) { + strange_feeling(otmp, + (amount > 0) ? "Your hands twitch." + : "Your hands itch."); + return (0); + } + + if (uwep->otyp == WORM_TOOTH && amount > 0) { + uwep->otyp = CRYSKNIFE; + pline("Your weapon seems sharper now."); + uwep->cursed = 0; + return (1); + } + + if (uwep->otyp == CRYSKNIFE && amount < 0) { + uwep->otyp = WORM_TOOTH; + pline("Your weapon looks duller now."); + return (1); + } + + /* there is a (soft) upper limit to uwep->spe */ + if (amount > 0 && uwep->spe > 5 && rn2(3)) { + pline("Your %s violently green for a while and then evaporate%s.", + aobjnam(uwep, "glow"), plur(uwep->quan)); + while (uwep) /* let all of them disappear */ + /* note: uwep->quan = 1 is nogood if unpaid */ + useup(uwep); + return (1); + } + if (!rn2(6)) + amount *= 2; + ltime = (amount * amount == 1) ? "moment" : "while"; + pline("Your %s %s for a %s.", + aobjnam(uwep, "glow"), color, ltime); + uwep->spe += amount; + if (amount > 0) + uwep->cursed = 0; + return (1); +} diff --git a/hack/hack.wizard.c b/hack/hack.wizard.c new file mode 100644 index 0000000..a99db3f --- /dev/null +++ b/hack/hack.wizard.c @@ -0,0 +1,198 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.wizard.c - version 1.0.3 */ +/* $FreeBSD: src/games/hack/hack.wizard.c,v 1.3 1999/11/16 02:57:14 billf Exp $ */ +/* $DragonFly: src/games/hack/hack.wizard.c,v 1.3 2006/08/21 19:45:32 pavalos Exp $ */ + +/* wizard code - inspired by rogue code from Merlyn Leroy (digi-g!brian) */ + +#include "hack.h" +extern struct permonst pm_wizard; + +#define WIZSHOT 6 /* one chance in WIZSHOT that wizard will try magic */ +#define BOLT_LIM 8 /* from this distance D and 1 will try to hit you */ + +char wizapp[] = "@DNPTUVXcemntx"; + +static void aggravate(void); +static void clonewiz(struct monst *); + +/* If he has found the Amulet, make the wizard appear after some time */ +void +amulet(void) +{ + struct obj *otmp; + struct monst *mtmp; + + if (!flags.made_amulet || !flags.no_of_wizards) + return; + /* find wizard, and wake him if necessary */ + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) + if (mtmp->data->mlet == '1' && mtmp->msleep && !rn2(40)) + for (otmp = invent; otmp; otmp = otmp->nobj) + if (otmp->olet == AMULET_SYM && !otmp->spe) { + mtmp->msleep = 0; + if (dist(mtmp->mx, mtmp->my) > 2) + pline("You get the creepy feeling that somebody noticed your taking the Amulet."); + return; + } +} + +bool +wiz_hit(struct monst *mtmp) +{ + /* if we have stolen or found the amulet, we disappear */ + if (mtmp->minvent && mtmp->minvent->olet == AMULET_SYM && + mtmp->minvent->spe == 0) { + /* vanish -- very primitive */ + fall_down(mtmp); + return (1); + } + + /* if it is lying around someplace, we teleport to it */ + if (!carrying(AMULET_OF_YENDOR)) { + struct obj *otmp; + + for (otmp = fobj; otmp; otmp = otmp->nobj) + if (otmp->olet == AMULET_SYM && !otmp->spe) { + if ((u.ux != otmp->ox || u.uy != otmp->oy) && + !m_at(otmp->ox, otmp->oy)) { + /* teleport to it and pick it up */ + mtmp->mx = otmp->ox; + mtmp->my = otmp->oy; + freeobj(otmp); + mpickobj(mtmp, otmp); + pmon(mtmp); + return (0); + } + goto hithim; + } + return (0); /* we don't know where it is */ + } +hithim: + if (rn2(2)) { /* hit - perhaps steal */ + /* + * if hit 1/20 chance of stealing amulet & vanish + * - amulet is on level 26 again. + */ + if (hitu(mtmp, d(mtmp->data->damn, mtmp->data->damd)) + && !rn2(20) && stealamulet(mtmp)) + return (0); + } else + inrange(mtmp); /* try magic */ + return (0); +} + +void +inrange(struct monst *mtmp) +{ + schar tx, ty; + + /* do nothing if cancelled (but make '1' say something) */ + if (mtmp->data->mlet != '1' && mtmp->mcan) + return; + + /* spit fire only when both in a room or both in a corridor */ + if (inroom(u.ux, u.uy) != inroom(mtmp->mx, mtmp->my)) + return; + tx = u.ux - mtmp->mx; + ty = u.uy - mtmp->my; + if ((!tx && abs(ty) < BOLT_LIM) || (!ty && abs(tx) < BOLT_LIM) + || (abs(tx) == abs(ty) && abs(tx) < BOLT_LIM)) { + switch (mtmp->data->mlet) { + case 'D': + /* spit fire in the direction of @ (not nec. hitting) */ + buzz(-1, mtmp->mx, mtmp->my, sgn(tx), sgn(ty)); + break; + case '1': + if (rn2(WIZSHOT)) + break; + /* + * if you zapped wizard with wand of cancellation, he + * has to shake off the effects before he can throw + * spells successfully. 1/2 the time they fail anyway + */ + if (mtmp->mcan || rn2(2)) { + if (canseemon(mtmp)) + pline("%s makes a gesture, then curses.", + Monnam(mtmp)); + else + pline("You hear mumbled cursing."); + if (!rn2(3)) { + mtmp->mspeed = 0; + mtmp->minvis = 0; + } + if (!rn2(3)) + mtmp->mcan = 0; + } else { + if (canseemon(mtmp)) { + if (!rn2(6) && !Invis) { + pline("%s hypnotizes you.", Monnam(mtmp)); + nomul(rn2(3) + 3); + break; + } else + pline("%s chants an incantation.", + Monnam(mtmp)); + } else + pline("You hear a mumbled incantation."); + switch (rn2(Invis ? 5 : 6)) { + case 0: + /* create a nasty monster from a deep level */ + /* (for the moment, 'nasty' is not implemented) */ + makemon(NULL, u.ux, u.uy); + break; + case 1: + pline("\"Destroy the thief, my pets!\""); + aggravate(); /* aggravate all the monsters */ + /* fall into next case */ + case 2: + if (flags.no_of_wizards == 1 && rnd(5) == 0) + /* if only 1 wizard, clone himself */ + clonewiz(mtmp); + break; + case 3: + if (mtmp->mspeed == MSLOW) + mtmp->mspeed = 0; + else + mtmp->mspeed = MFAST; + break; + case 4: + mtmp->minvis = 1; + break; + case 5: + /* Only if not Invisible */ + pline("You hear a clap of thunder!"); + /* shoot a bolt of fire or cold, or a sleep ray */ + buzz(-rnd(3), mtmp->mx, mtmp->my, sgn(tx), sgn(ty)); + break; + } + } + } + if (u.uhp < 1) + done_in_by(mtmp); + } +} + +static void +aggravate(void) +{ + struct monst *mtmp; + + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { + mtmp->msleep = 0; + if (mtmp->mfroz && !rn2(5)) + mtmp->mfroz = 0; + } +} + +static void +clonewiz(struct monst *mtmp) +{ + struct monst *mtmp2; + + if ((mtmp2 = makemon(PM_WIZARD, mtmp->mx, mtmp->my)) != NULL) { + flags.no_of_wizards = 2; + unpmon(mtmp2); + mtmp2->mappearance = wizapp[rn2(sizeof(wizapp) - 1)]; + pmon(mtmp); + } +} diff --git a/hack/hack.worm.c b/hack/hack.worm.c new file mode 100644 index 0000000..3a8d360 --- /dev/null +++ b/hack/hack.worm.c @@ -0,0 +1,223 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.worm.c - version 1.0.2 */ +/* $FreeBSD: src/games/hack/hack.worm.c,v 1.4 1999/11/16 10:26:38 marcel Exp $ */ + +#include "hack.h" +#ifndef NOWORM + +struct wseg *wsegs[32]; /* linked list, tail first */ +struct wseg *wheads[32]; +long wgrowtime[32]; + +static void remseg(struct wseg *); + +bool +getwn(struct monst *mtmp) +{ + int tmp; + + for (tmp = 1; tmp < 32; tmp++) + if (!wsegs[tmp]) { + mtmp->wormno = tmp; + return (1); + } + return (0); /* level infested with worms */ +} + +/* called to initialize a worm unless cut in half */ +void +initworm(struct monst *mtmp) +{ + struct wseg *wtmp; + int tmp = mtmp->wormno; + + if (!tmp) + return; + wheads[tmp] = wsegs[tmp] = wtmp = newseg(); + wgrowtime[tmp] = 0; + wtmp->wx = mtmp->mx; + wtmp->wy = mtmp->my; + wtmp->nseg = 0; +} + +void +worm_move(struct monst *mtmp) +{ + struct wseg *wtmp, *whd; + int tmp = mtmp->wormno; + + wtmp = newseg(); + wtmp->wx = mtmp->mx; + wtmp->wy = mtmp->my; + wtmp->nseg = 0; + (whd = wheads[tmp])->nseg = wtmp; + wheads[tmp] = wtmp; + if (cansee(whd->wx, whd->wy)) { + unpmon(mtmp); + atl(whd->wx, whd->wy, '~'); + whd->wdispl = 1; + } else + whd->wdispl = 0; + if (wgrowtime[tmp] <= moves) { + if (!wgrowtime[tmp]) + wgrowtime[tmp] = moves + rnd(5); + else + wgrowtime[tmp] += 2 + rnd(15); + mtmp->mhpmax += 3; + mtmp->mhp += 3; + return; + } + whd = wsegs[tmp]; + wsegs[tmp] = whd->nseg; + remseg(whd); +} + +void +worm_nomove(struct monst *mtmp) +{ + int tmp; + struct wseg *wtmp; + + tmp = mtmp->wormno; + wtmp = wsegs[tmp]; + if (wtmp == wheads[tmp]) + return; + if (wtmp == NULL || wtmp->nseg == 0) + panic("worm_nomove?"); + wsegs[tmp] = wtmp->nseg; + remseg(wtmp); + mtmp->mhp -= 3; /* mhpmax not changed ! */ +} + +void +wormdead(struct monst *mtmp) +{ + int tmp = mtmp->wormno; + struct wseg *wtmp, *wtmp2; + + if (!tmp) + return; + mtmp->wormno = 0; + for (wtmp = wsegs[tmp]; wtmp; wtmp = wtmp2) { + wtmp2 = wtmp->nseg; + remseg(wtmp); + } + wsegs[tmp] = NULL; +} + +void +wormhit(struct monst *mtmp) +{ + int tmp = mtmp->wormno; + struct wseg *wtmp; + + if (!tmp) /* worm without tail */ + return; + for (wtmp = wsegs[tmp]; wtmp; wtmp = wtmp->nseg) + hitu(mtmp, 1); +} + +void +wormsee(unsigned int tmp) +{ + struct wseg *wtmp = wsegs[tmp]; + + if (!wtmp) + panic("wormsee: wtmp==0"); + for (; wtmp->nseg; wtmp = wtmp->nseg) + if (!cansee(wtmp->wx, wtmp->wy) && wtmp->wdispl) { + newsym(wtmp->wx, wtmp->wy); + wtmp->wdispl = 0; + } +} + +void +pwseg(struct wseg *wtmp) +{ + if (!wtmp->wdispl) { + atl(wtmp->wx, wtmp->wy, '~'); + wtmp->wdispl = 1; + } +} + +/* weptyp: uwep->otyp or 0 */ +void +cutworm(struct monst *mtmp, xchar x, xchar y, uchar weptyp) +{ + struct wseg *wtmp, *wtmp2; + struct monst *mtmp2; + int tmp, tmp2; + + if (mtmp->mx == x && mtmp->my == y) /* hit headon */ + return; + + /* cutting goes best with axe or sword */ + tmp = rnd(20); + if (weptyp == LONG_SWORD || weptyp == TWO_HANDED_SWORD || + weptyp == AXE) + tmp += 5; + if (tmp < 12) + return; + + /* if tail then worm just loses a tail segment */ + tmp = mtmp->wormno; + wtmp = wsegs[tmp]; + if (wtmp->wx == x && wtmp->wy == y) { + wsegs[tmp] = wtmp->nseg; + remseg(wtmp); + return; + } + + /* cut the worm in two halves */ + mtmp2 = newmonst(0); + *mtmp2 = *mtmp; + mtmp2->mxlth = mtmp2->mnamelth = 0; + + /* sometimes the tail end dies */ + if (rn2(3) || !getwn(mtmp2)) { + monfree(mtmp2); + tmp2 = 0; + } else { + tmp2 = mtmp2->wormno; + wsegs[tmp2] = wsegs[tmp]; + wgrowtime[tmp2] = 0; + } + do { + if (wtmp->nseg->wx == x && wtmp->nseg->wy == y) { + if (tmp2) + wheads[tmp2] = wtmp; + wsegs[tmp] = wtmp->nseg->nseg; + remseg(wtmp->nseg); + wtmp->nseg = 0; + if (tmp2) { + pline("You cut the worm in half."); + mtmp2->mhpmax = mtmp2->mhp = + d(mtmp2->data->mlevel, 8); + mtmp2->mx = wtmp->wx; + mtmp2->my = wtmp->wy; + mtmp2->nmon = fmon; + fmon = mtmp2; + pmon(mtmp2); + } else { + pline("You cut off part of the worm's tail."); + remseg(wtmp); + } + mtmp->mhp /= 2; + return; + } + wtmp2 = wtmp->nseg; + if (!tmp2) + remseg(wtmp); + wtmp = wtmp2; + } while (wtmp->nseg); + panic("Cannot find worm segment"); +} + +static void +remseg(struct wseg *wtmp) +{ + if (wtmp->wdispl) + newsym(wtmp->wx, wtmp->wy); + free(wtmp); +} +#endif /* NOWORM */ diff --git a/hack/hack.worn.c b/hack/hack.worn.c new file mode 100644 index 0000000..a754aee --- /dev/null +++ b/hack/hack.worn.c @@ -0,0 +1,70 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.worn.c - version 1.0.2 */ +/* $FreeBSD: src/games/hack/hack.worn.c,v 1.3 1999/11/16 02:57:14 billf Exp $ */ + +#include "hack.h" + +struct worn { + long w_mask; + struct obj **w_obj; +} worn[] = { + { W_ARM, &uarm }, + { W_ARM2, &uarm2 }, + { W_ARMH, &uarmh }, + { W_ARMS, &uarms }, + { W_ARMG, &uarmg }, + { W_RINGL, &uleft }, + { W_RINGR, &uright }, + { W_WEP, &uwep }, + { W_BALL, &uball }, + { W_CHAIN, &uchain }, + { 0, 0 } +}; + +void +setworn(struct obj *obj, long mask) +{ + struct worn *wp; + struct obj *oobj; + + for (wp = worn; wp->w_mask; wp++) + if (wp->w_mask & mask) { + oobj = *(wp->w_obj); + if (oobj && !(oobj->owornmask & wp->w_mask)) + impossible("Setworn: mask = %ld.", wp->w_mask); + if (oobj) + oobj->owornmask &= ~wp->w_mask; + if (obj && oobj && wp->w_mask == W_ARM) { + if (uarm2) + impossible("Setworn: uarm2 set?"); + else + setworn(uarm, W_ARM2); + } + *(wp->w_obj) = obj; + if (obj) + obj->owornmask |= wp->w_mask; + } + if (uarm2 && !uarm) { + uarm = uarm2; + uarm2 = 0; + uarm->owornmask ^= (W_ARM | W_ARM2); + } +} + +/* called e.g. when obj is destroyed */ +void +setnotworn(struct obj *obj) +{ + struct worn *wp; + + for (wp = worn; wp->w_mask; wp++) + if (obj == *(wp->w_obj)) { + *(wp->w_obj) = NULL; + obj->owornmask &= ~wp->w_mask; + } + if (uarm2 && !uarm) { + uarm = uarm2; + uarm2 = 0; + uarm->owornmask ^= (W_ARM | W_ARM2); + } +} diff --git a/hack/hack.zap.c b/hack/hack.zap.c new file mode 100644 index 0000000..61eb963 --- /dev/null +++ b/hack/hack.zap.c @@ -0,0 +1,688 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.zap.c - version 1.0.3 */ +/* $FreeBSD: src/games/hack/hack.zap.c,v 1.4 1999/11/16 10:26:38 marcel Exp $ */ +/* $DragonFly: src/games/hack/hack.zap.c,v 1.5 2006/08/21 19:45:32 pavalos Exp $ */ + +#include "hack.h" + +extern struct monst youmonst; + +static const char *fl[]= { + "magic missile", + "bolt of fire", + "sleep ray", + "bolt of cold", + "death ray" +}; + +static void bhitm(struct monst *, struct obj *); +static bool bhito(struct obj *, struct obj *); +static char dirlet(int, int); +static int zhit(struct monst *, int); +static bool revive(struct obj *); +static void rloco(struct obj *); +static void burn_scrolls(void); + +/* Routines for IMMEDIATE wands. */ +/* bhitm: monster mtmp was hit by the effect of wand otmp */ +static void +bhitm(struct monst *mtmp, struct obj *otmp) +{ + wakeup(mtmp); + switch (otmp->otyp) { + case WAN_STRIKING: + if (u.uswallow || rnd(20) < 10 + mtmp->data->ac) { + int tmp = d(2, 12); + hit("wand", mtmp, exclam(tmp)); + mtmp->mhp -= tmp; + if (mtmp->mhp < 1) + killed(mtmp); + } else + miss("wand", mtmp); + break; + case WAN_SLOW_MONSTER: + mtmp->mspeed = MSLOW; + break; + case WAN_SPEED_MONSTER: + mtmp->mspeed = MFAST; + break; + case WAN_UNDEAD_TURNING: + if (strchr(UNDEAD, mtmp->data->mlet)) { + mtmp->mhp -= rnd(8); + if (mtmp->mhp < 1) + killed(mtmp); + else + mtmp->mflee = 1; + } + break; + case WAN_POLYMORPH: + if (newcham(mtmp, &mons[rn2(CMNUM)])) + objects[otmp->otyp].oc_name_known = 1; + break; + case WAN_CANCELLATION: + mtmp->mcan = 1; + break; + case WAN_TELEPORTATION: + rloc(mtmp); + break; + case WAN_MAKE_INVISIBLE: + mtmp->minvis = 1; + break; +#ifdef WAN_PROBING + case WAN_PROBING: + mstatusline(mtmp); + break; +#endif /* WAN_PROBING */ + default: + impossible("What an interesting wand (%u)", otmp->otyp); + } +} + +/* + * object obj was hit by the effect of wand otmp + * returns TRUE if sth was done + */ +static bool +bhito(struct obj *obj, struct obj *otmp) +{ + int res = TRUE; + + if (obj == uball || obj == uchain) + res = FALSE; + else + switch (otmp->otyp) { + case WAN_POLYMORPH: + /* preserve symbol and quantity, but turn rocks into gems */ + mkobj_at((obj->otyp == ROCK || obj->otyp == ENORMOUS_ROCK) + ? GEM_SYM : obj->olet, + obj->ox, obj->oy)->quan = obj->quan; + delobj(obj); + break; + case WAN_STRIKING: + if (obj->otyp == ENORMOUS_ROCK) + fracture_rock(obj); + else + res = FALSE; + break; + case WAN_CANCELLATION: + if (obj->spe && obj->olet != AMULET_SYM) { + obj->known = 0; + obj->spe = 0; + } + break; + case WAN_TELEPORTATION: + rloco(obj); + break; + case WAN_MAKE_INVISIBLE: + obj->oinvis = 1; + break; + case WAN_UNDEAD_TURNING: + res = revive(obj); + break; + case WAN_SLOW_MONSTER: /* no effect on objects */ + case WAN_SPEED_MONSTER: +#ifdef WAN_PROBING + case WAN_PROBING: +#endif /* WAN_PROBING */ + res = FALSE; + break; + default: + impossible("What an interesting wand (%u)", otmp->otyp); + } + return (res); +} + +int +dozap(void) +{ + struct obj *obj; + xchar zx, zy; + + obj = getobj("/", "zap"); + if (!obj) + return (0); + if (obj->spe < 0 || (obj->spe == 0 && rn2(121))) { + pline("Nothing Happens."); + return (1); + } + if (obj->spe == 0) + pline("You wrest one more spell from the worn-out wand."); + if (!(objects[obj->otyp].bits & NODIR) && !getdir(1)) + return (1); /* make him pay for knowing !NODIR */ + obj->spe--; + if (objects[obj->otyp].bits & IMMEDIATE) { + if (u.uswallow) + bhitm(u.ustuck, obj); + else if (u.dz) { + if (u.dz > 0) { + struct obj *otmp = o_at(u.ux, u.uy); + if (otmp) + bhito(otmp, obj); + } + } else + bhit(u.dx, u.dy, rn1(8, 6), 0, bhitm, bhito, obj); + } else { + switch (obj->otyp) { + case WAN_LIGHT: + litroom(TRUE); + break; + case WAN_SECRET_DOOR_DETECTION: + if (!findit()) + return (1); + break; + case WAN_CREATE_MONSTER: + { + int cnt = 1; + if (!rn2(23)) + cnt += rn2(7) + 1; + while (cnt--) + makemon(NULL, u.ux, u.uy); + } + break; + case WAN_WISHING: + { + char buf[BUFSZ]; + struct obj *otmp; + if (u.uluck + rn2(5) < 0) { + pline("Unfortunately, nothing happens."); + break; + } + pline("You may wish for an object. What do you want? "); + getlin(buf); + if (buf[0] == '\033') + buf[0] = 0; + otmp = readobjnam(buf); + otmp = addinv(otmp); + prinv(otmp); + break; + } + case WAN_DIGGING: + /* + * Original effect (approximately): + * from CORR: dig until we pierce a wall + * from ROOM: piece wall and dig until we reach + * an ACCESSIBLE place. + * Currently: dig for digdepth positions; + * also down on request of Lennart Augustsson. + */ + { + struct rm *room; + int digdepth; + if (u.uswallow) { + struct monst *mtmp = u.ustuck; + + pline("You pierce %s's stomach wall!", + monnam(mtmp)); + mtmp->mhp = 1; /* almost dead */ + unstuck(mtmp); + mnexto(mtmp); + break; + } + if (u.dz) { + if (u.dz < 0) { + pline("You loosen a rock from the ceiling."); + pline("It falls on your head!"); + losehp(1, "falling rock"); + mksobj_at(ROCK, u.ux, u.uy); + fobj->quan = 1; + stackobj(fobj); + if (Invisible) + newsym(u.ux, u.uy); + } else + dighole(); + break; + } + zx = u.ux + u.dx; + zy = u.uy + u.dy; + digdepth = 8 + rn2(18); + Tmp_at(-1, '*'); /* open call */ + while (--digdepth >= 0) { + if (!isok(zx, zy)) + break; + room = &levl[zx][zy]; + Tmp_at(zx, zy); + if (!xdnstair) { + if (zx < 3 || zx > COLNO - 3 || + zy < 3 || zy > ROWNO - 3) + break; + if (room->typ == HWALL || + room->typ == VWALL) { + room->typ = ROOM; + break; + } + } else if (room->typ == HWALL || room->typ == VWALL || + room->typ == SDOOR || room->typ == LDOOR) { + room->typ = DOOR; + digdepth -= 2; + } else if (room->typ == SCORR || !room->typ) { + room->typ = CORR; + digdepth--; + } + mnewsym(zx, zy); + zx += u.dx; + zy += u.dy; + } + mnewsym(zx, zy); /* not always necessary */ + Tmp_at(-1, -1); /* closing call */ + break; + } + default: + buzz((int)obj->otyp - WAN_MAGIC_MISSILE, + u.ux, u.uy, u.dx, u.dy); + break; + } + if (!objects[obj->otyp].oc_name_known) { + objects[obj->otyp].oc_name_known = 1; + more_experienced(0, 10); + } + } + return (1); +} + +const char * +exclam(int force) +{ + /* force == 0 occurs e.g. with sleep ray */ + /* note that large force is usual with wands so that !! would + require information about hand/weapon/wand */ + return ((force < 0) ? "?" : (force <= 4) ? "." : "!"); +} + +void +hit(const char *str, struct monst *mtmp, const char *force) +{ + /* force: usually either "." or "!" */ + if (!cansee(mtmp->mx, mtmp->my)) + pline("The %s hits it.", str); + else + pline("The %s hits %s%s", str, monnam(mtmp), force); +} + +void +miss(const char *str, struct monst *mtmp) +{ + if (!cansee(mtmp->mx, mtmp->my)) + pline("The %s misses it.", str); + else + pline("The %s misses %s.", str, monnam(mtmp)); +} + +/* + * bhit: called when a weapon is thrown (sym = obj->olet) or when an + * IMMEDIATE wand is zapped (sym = 0); the weapon falls down at end of range + * or when a monster is hit; the monster is returned, and bhitpos is set to + * the final position of the weapon thrown; the ray of a wand may affect + * several objects and monsters on its path - for each of these an argument + * function is called. + */ +/* check !u.uswallow before calling bhit() */ + +/* ddx, ddy, range: direction and range + * sym: symbol displayed on path + * fhitm, fhito: fns called when mon/obj hit + * obj: 2nd arg to fhitm/fhito + */ +struct monst * +bhit(int ddx, int ddy, int range, char sym, + void (*fhitm)(struct monst *, struct obj *), + bool (*fhito)(struct obj *, struct obj *), struct obj *obj) +{ + struct monst *mtmp; + struct obj *otmp; + int typ; + + bhitpos.x = u.ux; + bhitpos.y = u.uy; + + if (sym) /* open call */ + tmp_at(-1, sym); + while (range-- > 0) { + bhitpos.x += ddx; + bhitpos.y += ddy; + typ = levl[bhitpos.x][bhitpos.y].typ; + if ((mtmp = m_at(bhitpos.x, bhitpos.y))) { + if (sym) { + tmp_at(-1, -1); /* close call */ + return (mtmp); + } + (*fhitm)(mtmp, obj); + range -= 3; + } + if (fhito && (otmp = o_at(bhitpos.x, bhitpos.y))) { + if ((*fhito)(otmp, obj)) + range--; + } + if (!ZAP_POS(typ)) { + bhitpos.x -= ddx; + bhitpos.y -= ddy; + break; + } + if (sym) + tmp_at(bhitpos.x, bhitpos.y); + } + + /* leave last symbol unless in a pool */ + if (sym) + tmp_at(-1, (levl[bhitpos.x][bhitpos.y].typ == POOL) ? -1 : 0); + return (0); +} + +struct monst * +boomhit(int dx, int dy) +{ + int i, ct; + struct monst *mtmp; + char sym = ')'; + + bhitpos.x = u.ux; + bhitpos.y = u.uy; + + for (i = 0; i < 8; i++) + if (xdir[i] == dx && ydir[i] == dy) + break; + tmp_at(-1, sym); /* open call */ + for (ct = 0; ct < 10; ct++) { + if (i == 8) + i = 0; + sym = ')' + '(' - sym; + tmp_at(-2, sym); /* change let call */ + dx = xdir[i]; + dy = ydir[i]; + bhitpos.x += dx; + bhitpos.y += dy; + if ((mtmp = m_at(bhitpos.x, bhitpos.y)) != NULL) { + tmp_at(-1, -1); + return (mtmp); + } + if (!ZAP_POS(levl[bhitpos.x][bhitpos.y].typ)) { + bhitpos.x -= dx; + bhitpos.y -= dy; + break; + } + if (bhitpos.x == u.ux && bhitpos.y == u.uy) { /* ct == 9 */ + if (rn2(20) >= 10 + u.ulevel) { /* we hit ourselves */ + thitu(10, rnd(10), "boomerang"); + break; + } else { /* we catch it */ + tmp_at(-1, -1); + pline("Skillfully, you catch the boomerang."); + return (&youmonst); + } + } + tmp_at(bhitpos.x, bhitpos.y); + if (ct % 5 != 0) + i++; + } + tmp_at(-1, -1); /* do not leave last symbol */ + return (0); +} + +static char +dirlet(int dx, int dy) +{ + return + (dx == dy) ? '\\' : (dx && dy) ? '/' : dx ? '-' : '|'; +} + +/* type == -1: monster spitting fire at you */ +/* type == -1,-2,-3: bolts sent out by wizard */ +/* called with dx = dy = 0 with vertical bolts */ +void +buzz(int type, xchar sx, xchar sy, int dx, int dy) +{ + int abstype = abs(type); + const char *fltxt = (type == -1) ? "blaze of fire" : fl[abstype]; + struct rm *lev; + xchar range; + struct monst *mon; + + if (u.uswallow) { + int tmp; + + if (type < 0) + return; + tmp = zhit(u.ustuck, type); + pline("The %s rips into %s%s", + fltxt, monnam(u.ustuck), exclam(tmp)); + return; + } + if (type < 0) + pru(); + range = rn1(7, 7); + Tmp_at(-1, dirlet(dx, dy)); /* open call */ + while (range-- > 0) { + sx += dx; + sy += dy; + if ((lev = &levl[sx][sy])->typ) + Tmp_at(sx, sy); + else { + int bounce = 0; + if (cansee(sx - dx, sy - dy)) + pline("The %s bounces!", fltxt); + if (ZAP_POS(levl[sx][sy - dy].typ)) + bounce = 1; + if (ZAP_POS(levl[sx - dx][sy].typ)) { + if (!bounce || rn2(2)) + bounce = 2; + } + switch (bounce) { + case 0: + dx = -dx; + dy = -dy; + continue; + case 1: + dy = -dy; + sx -= dx; + break; + case 2: + dx = -dx; + sy -= dy; + break; + } + Tmp_at(-2, dirlet(dx, dy)); + continue; + } + if (lev->typ == POOL && abstype == 1 /* fire */) { + range -= 3; + lev->typ = ROOM; + if (cansee(sx, sy)) { + mnewsym(sx, sy); + pline("The water evaporates."); + } else + pline("You hear a hissing sound."); + } + if ((mon = m_at(sx, sy)) && + (type != -1 || mon->data->mlet != 'D')) { + wakeup(mon); + if (rnd(20) < 18 + mon->data->ac) { + int tmp = zhit(mon, abstype); + if (mon->mhp < 1) { + if (type < 0) { + if (cansee(mon->mx, mon->my)) + pline("%s is killed by the %s!", + Monnam(mon), fltxt); + mondied(mon); + } else + killed(mon); + } else + hit(fltxt, mon, exclam(tmp)); + range -= 2; + } else + miss(fltxt, mon); + } else if (sx == u.ux && sy == u.uy) { + nomul(0); + if (rnd(20) < 18 + u.uac) { + int dam = 0; + range -= 2; + pline("The %s hits you!", fltxt); + switch (abstype) { + case 0: + dam = d(2, 6); + break; + case 1: + if (Fire_resistance) + pline("You don't feel hot!"); + else + dam = d(6, 6); + if (!rn2(3)) + burn_scrolls(); + break; + case 2: + nomul(-rnd(25)); /* sleep ray */ + break; + case 3: + if (Cold_resistance) + pline("You don't feel cold!"); + else + dam = d(6, 6); + break; + case 4: + u.uhp = -1; + } + losehp(dam, fltxt); + } else + pline("The %s whizzes by you!", fltxt); + stop_occupation(); + } + if (!ZAP_POS(lev->typ)) { + int bounce = 0, rmn; + if (cansee(sx, sy)) + pline("The %s bounces!", fltxt); + range--; + if (!dx || !dy || !rn2(20)) { + dx = -dx; + dy = -dy; + } else { + if (ZAP_POS(rmn = levl[sx][sy - dy].typ) && + (IS_ROOM(rmn) || ZAP_POS(levl[sx + dx][sy - dy].typ))) + bounce = 1; + if (ZAP_POS(rmn = levl[sx - dx][sy].typ) && + (IS_ROOM(rmn) || ZAP_POS(levl[sx - dx][sy + dy].typ))) + if (!bounce || rn2(2)) + bounce = 2; + + switch (bounce) { + case 0: + dy = -dy; + dx = -dx; + break; + case 1: + dy = -dy; + break; + case 2: + dx = -dx; + break; + } + Tmp_at(-2, dirlet(dx, dy)); + } + } + } + Tmp_at(-1, -1); +} + +static int +zhit(struct monst *mon, int type) /* returns damage to mon */ +{ + int tmp = 0; + + switch (type) { + case 0: /* magic missile */ + tmp = d(2, 6); + break; + case -1: /* Dragon blazing fire */ + case 1: /* fire */ + if (strchr("Dg", mon->data->mlet)) + break; + tmp = d(6, 6); + if (strchr("YF", mon->data->mlet)) + tmp += 7; + break; + case 2: /* sleep*/ + mon->mfroz = 1; + break; + case 3: /* cold */ + if (strchr("YFgf", mon->data->mlet)) + break; + tmp = d(6, 6); + if (mon->data->mlet == 'D') + tmp += 7; + break; + case 4: /* death*/ + if (strchr(UNDEAD, mon->data->mlet)) + break; + tmp = mon->mhp + 1; + break; + } + mon->mhp -= tmp; + return (tmp); +} + +#define CORPSE_I_TO_C(otyp) (char) ((otyp >= DEAD_ACID_BLOB)\ + ? 'a' + (otyp - DEAD_ACID_BLOB)\ + : '@' + (otyp - DEAD_HUMAN)) +static bool +revive(struct obj *obj) +{ + struct monst *mtmp = NULL; + + if (obj->olet == FOOD_SYM && obj->otyp > CORPSE) { + /* do not (yet) revive shopkeepers */ + /* Note: this might conceivably produce two monsters + * at the same position - strange, but harmless */ + mtmp = mkmon_at(CORPSE_I_TO_C(obj->otyp), obj->ox, obj->oy); + delobj(obj); + } + return (!!mtmp); /* TRUE if some monster created */ +} + +static void +rloco(struct obj *obj) +{ + int tx, ty, otx, oty; + + otx = obj->ox; + oty = obj->oy; + do { + tx = rn1(COLNO - 3, 2); + ty = rn2(ROWNO); + } while (!goodpos(tx, ty)); + obj->ox = tx; + obj->oy = ty; + if (cansee(otx, oty)) + newsym(otx, oty); +} + +/* fractured by pick-axe or wand of striking */ +/* no texts here! */ +void +fracture_rock(struct obj *obj) +{ + obj->otyp = ROCK; + obj->quan = 7 + rn2(60); + obj->owt = weight(obj); + obj->olet = WEAPON_SYM; + if (cansee(obj->ox, obj->oy)) + prl(obj->ox, obj->oy); +} + +static void +burn_scrolls(void) +{ + struct obj *obj, *obj2; + int cnt = 0; + + for (obj = invent; obj; obj = obj2) { + obj2 = obj->nobj; + if (obj->olet == SCROLL_SYM) { + cnt++; + useup(obj); + } + } + if (cnt > 1) { + pline("Your scrolls catch fire!"); + losehp(cnt, "burning scrolls"); + } else if (cnt) { + pline("Your scroll catches fire!"); + losehp(1, "burning scroll"); + } +} diff --git a/hack/help b/hack/help new file mode 100644 index 0000000..24b22a5 --- /dev/null +++ b/hack/help @@ -0,0 +1,132 @@ + Welcome to HACK! ( description of version 1.0.3 ) + + Hack is a Dungeons and Dragons like game where you (the adventurer) +descend into the depths of the dungeon in search of the Amulet of Yendor +(reputed to be hidden on the twentieth level). You are accompanied by a +little dog that can help you in many ways and can be trained to do all +sorts of things. On the way you will find useful (or useless) items, (quite +possibly with magic properties) and assorted monsters. You attack a monster +by trying to move into the space a monster is in (but often it is much +wiser to leave it alone). + + Unlike most adventure games, which give you a verbal description of +your location, hack gives you a visual image of the dungeon level you are on. + + Hack uses the following symbols: + A to Z and a to z: monsters. You can find out what a letter +represents by saying "/ (letter)", as in "/A", which will tell you that 'A' +is a giant ant. + - and | These form the walls of a room (or maze). + . this is the floor of a room. + # this is a corridor. + > this is the staircase to the next level. + < the staircase to the previous level. + ` A large boulder. + @ You (usually). + ^ A trap. + ) A weapon of some sort. + ( Some other useful object (key, rope, dynamite, camera, ...) + [ A suit of armor. + % A piece of food (not necessarily healthy ...). + / A wand. + = A ring. + ? A scroll. + ! A magic potion. + $ A pile or pot of gold. + +Commands: + Hack knows the following commands: + ? help: print this list. + Q Quit the game. + S Save the game. + ! Escape to a shell. + ^Z Suspend the game. + < up: go up the staircase (if you are standing on it). + > down: go down (just like up). + kjhlyubn - go one step in the direction indicated. + k: north (i.e., to the top of the screen), + j: south, h: west, l: east, y: ne, u: nw, b: se, n: sw. + KJHLYUBN - Go in that direction until you hit a wall or run + into something. + m (followed by one of kjhlyubn): move without picking up + any objects. + M (followed by one of KJHLYUBN): Move far, no pickup. + f (followed by one of kjhlyubn): move until something + interesting is found. + F (followed by one of KJHLYUBN): as previous, but forking + of corridors is not considered interesting. + i print your inventory. + I print selected parts of your inventory, like in + I* - print all gems in inventory; + IU - print all unpaid items; + IX - print all used up items that are on your shopping bill; + I$ - count your money. + s search for secret doors and traps around you. + ^ ask for the type of a trap you found earlier. + ) ask for current wielded weapon. + [ ask for current armor. + = ask for current rings. + $ count how many gold pieces you are carrying. + . rest, do nothing. + , pick up some things. + : look at what is here. + ^T teleport. + ^R redraw the screen. + ^P repeat last message + (subsequent ^P's repeat earlier messages). + / (followed by any symbol): tell what this symbol represents. + \ tell what has been discovered. + e eat food. + w wield weapon. w- means: wield nothing, use bare hands. + q drink (quaff) a potion. + r read a scroll. + T Takeoff armor. + R Remove Ring. + W Wear armor. + P Put on a ring. + z zap a wand. + t throw an object or shoot an arrow. + p pay your shopping bill. + d drop something. d7a: drop seven items of object a. + D Drop several things. + In answer to the question "What kinds of things do you + want to drop? [!%= au]" you should give zero or more + object symbols possibly followed by 'a' and/or 'u'. + 'a' means: drop all such objects, without asking for + confirmation. + 'u' means: drop only unpaid objects (when in a shop). + a use, apply - Generic command for using a key to lock + or unlock a door, using a camera, using a rope, etc. + c call: name a certain object or class of objects. + C Call: Name an individual monster. + E Engrave: Write a message in the dust on the floor. + E- means: use fingers for writing. + O Set options. You will be asked to enter an option line. + If this is empty, the current options are reported. + Otherwise it should be a list of options separated by commas. + Possible boolean options are: oneline, time, news, tombstone, + rest_on_space, fixinvlet, beginner, male, female. + They can be negated by prefixing them with '!' or "no". + A string option is name; it supplies the answer to the question + "Who are you?"; it may have a suffix. + A compound option is endgame; it is followed by a description + of what parts of the list of topscorers should be printed + when the game is finished. + Usually one will not want to use the 'O' command, but instead + put a HACKOPTIONS="...." line in one's environment. + v print version number. + + You can put a number before a command to repeat it that many times, + as in "20s" or "40.". + + At present, some information is displayed on the bottom line. + (It is expected that this information will go away in future versions.) + You see on what dungeon level you are, how many hit points you have + now (and will have when fully recovered), what your armor class is + (the lower the better), your strength, experience level and the + state of your stomach. + + Have Fun, and Good Hacking! + + + @@ -0,0 +1,55 @@ +y k u Move commands: + \|/ hykulnjb: single move in specified direction +h-+-l HYKULNJB: repeated move in specified direction + /|\ (until stopped by e.g. a wall) +b j n f<dir>: fast movement in direction <dir> + (until something interesting is seen) + m<dir>: move without picking up objects + +Meta commands: +Q quit leave the game +S save save the game (to be continued later) +! sh escape to some SHELL +^Z suspend suspend the game (independent of your current suspend char) +O set set options +? help print information +/ whatis give name (and sometimes more info) of specified monster +\ known print list of what's been discovered +v version print version number +^R redraw redraw the screen (^R denotes the symbol CTRL/R) +^P print repeat last message (subsequent ^P's repeat earlier messages) +# introduces a long command; not really implemented + +Game commands: +^T teleport teleport +a apply, use use something (a key, camera, etc.) +c call give a name to a class of objects +d drop drop an object. d7a: drop seven items of object a. +e eat eat something +i invent list the inventory (all objects you are carrying) +I invent list selected parts of the inventory + IU: list unpaid objects + IX: list unpaid but used up items + I$: count your money +p pay pay your bill +q drink quaff a potion +r read read a scroll +s search search for secret doors, hidden traps and monsters +t throw throw or shoot a weapon +w wield wield a weapon (w- wield nothing) +z zap zap a wand +C name name an individual monster (e.g., baptize your dog) +D Drop drop several things +E Engrave write a message in the dust on the floor (E- use fingers) +P wear put on a ring +R remove remove a ring +T remove take off some armor +W wear put on some armor +< up go up the stairs +> down go down the stairs +^ trap_id identify a previously found trap +),[,= ask for current weapon, armor, rings, respectively +$ gold count your gold +. rest wait a moment +, pickup pick up all you can carry +: look look at what is here diff --git a/hack/makedefs.c b/hack/makedefs.c new file mode 100644 index 0000000..7b66485 --- /dev/null +++ b/hack/makedefs.c @@ -0,0 +1,266 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* makedefs.c - version 1.0.2 */ +/* $FreeBSD: src/games/hack/makedefs.c,v 1.4 1999/11/16 02:57:15 billf Exp $ */ +/* $DragonFly: src/games/hack/makedefs.c,v 1.3 2006/08/21 19:45:32 pavalos Exp $ */ + +#include <fcntl.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +/* construct definitions of object constants */ +#define LINSZ 1000 +#define STRSZ 40 + +int fd; +char string[STRSZ]; + +static void readline(void); +static char nextchar(void); +static bool skipuntil(const char *); +static bool getentry(void); +static void capitalize(char *); +static bool letter(char); +static bool digit(char); + +int +main(int argc, char **argv) +{ + int idx = 0; + int propct = 0; + char *sp; + + if (argc != 2) { + fprintf(stderr, "usage: makedefs file\n"); + exit(1); + } + if ((fd = open(argv[1], O_RDONLY)) < 0) { + perror(argv[1]); + exit(1); + } + skipuntil("objects[] = {"); + while (getentry()) { + if (!*string) { + idx++; + continue; + } + for (sp = string; *sp; sp++) + if (*sp == ' ' || *sp == '\t' || *sp == '-') + *sp = '_'; + if (!strncmp(string, "RIN_", 4)) { + capitalize(string + 4); + printf("#define %s u.uprops[%d].p_flgs\n", + string + 4, propct++); + } + for (sp = string; *sp; sp++) + capitalize(sp); + /* avoid trouble with stupid C preprocessors */ + if (!strncmp(string, "WORTHLESS_PIECE_OF_", 19)) + printf("/* #define %s %d */\n", string, idx); + else + printf("#define %s %d\n", string, idx); + idx++; + } + printf("\n#define CORPSE DEAD_HUMAN\n"); + printf("#define LAST_GEM (JADE+1)\n"); + printf("#define LAST_RING %d\n", propct); + printf("#define NROFOBJECTS %d\n", idx - 1); + exit(0); +} + +char line[LINSZ], *lp = line, *lp0 = line, *lpe = line; +int eof; + +static void +readline(void) +{ + int n = read(fd, lp0, (line + LINSZ) - lp0); + + if (n < 0) { + printf("Input error.\n"); + exit(1); + } + if (n == 0) + eof++; + lpe = lp0 + n; +} + +static char +nextchar(void) +{ + if (lp == lpe) { + readline(); + lp = lp0; + } + return ((lp == lpe) ? 0 : *lp++); +} + +static bool +skipuntil(const char *s) +{ + const char *sp0; + char *sp1; +loop: + while (*s != nextchar()) + if (eof) { + printf("Cannot skipuntil %s\n", s); + exit(1); + } + if ((int)strlen(s) > lpe - lp + 1) { + char *lp1, *lp2; + lp2 = lp; + lp1 = lp = lp0; + while (lp2 != lpe) + *lp1++ = *lp2++; + lp2 = lp0; /* save value */ + lp0 = lp1; + readline(); + lp0 = lp2; + if ((int)strlen(s) > lpe - lp + 1) { + printf("error in skipuntil"); + exit(1); + } + } + sp0 = s + 1; + sp1 = lp; + while (*sp0 && *sp0 == *sp1) + sp0++, sp1++; + if (!*sp0) { + lp = sp1; + return (1); + } + goto loop; +} + +static bool +getentry(void) +{ + int inbraces = 0, inparens = 0, stringseen = 0, commaseen = 0; + int prefix = 0; + char ch; +#define NSZ 10 + char identif[NSZ], *ip; + + string[0] = string[4] = 0; + /* read until {...} or XXX(...) followed by , + * skip comment and #define lines + * deliver 0 on failure + */ + for (;;) { + ch = nextchar(); +swi: + if (letter(ch)) { + ip = identif; + do { + if (ip < identif + NSZ - 1) + *ip++ = ch; + ch = nextchar(); + } while (letter(ch) || digit(ch)); + *ip = 0; + while (ch == ' ' || ch == '\t') + ch = nextchar(); + if (ch == '(' && !inparens && !stringseen) + if (!strcmp(identif, "WAND") || + !strcmp(identif, "RING") || + !strcmp(identif, "POTION") || + !strcmp(identif, "SCROLL")) + strncpy(string, identif, 3), + string[3] = '_', + prefix = 4; + } + switch (ch) { + case '/': + /* watch for comment */ + if ((ch = nextchar()) == '*') + skipuntil("*/"); + goto swi; + case '{': + inbraces++; + continue; + case '(': + inparens++; + continue; + case '}': + inbraces--; + if (inbraces < 0) + return (0); + continue; + case ')': + inparens--; + if (inparens < 0) { + printf("too many ) ?"); + exit(1); + } + continue; + case '\n': + /* watch for #define at begin of line */ + if ((ch = nextchar()) == '#') { + char pch; + /* skip until '\n' not preceded by '\\' */ + do { + pch = ch; + ch = nextchar(); + } while (ch != '\n' || pch == '\\'); + continue; + } + goto swi; + case ',': + if (!inparens && !inbraces) { + if (prefix && !string[prefix]) + string[0] = 0; + if (stringseen) + return (1); + printf("unexpected ,\n"); + exit(1); + } + commaseen++; + continue; + case '\'': + if ((ch = nextchar()) == '\\') + ch = nextchar(); + if (nextchar() != '\'') { + printf("strange character denotation?\n"); + exit(1); + } + continue; + case '"': + { + char *sp = string + prefix; + char pch; + int store = (inbraces || inparens) + && !stringseen++ && !commaseen; + do { + pch = ch; + ch = nextchar(); + if (store && sp < string + STRSZ) + *sp++ = ch; + } while (ch != '"' || pch == '\\'); + if (store) + *--sp = 0; + continue; + } + } + } +} + +static void +capitalize(char *sp) +{ + if ('a' <= *sp && *sp <= 'z') + *sp += 'A' - 'a'; +} + +static bool +letter(char ch) +{ + return (('a' <= ch && ch <= 'z') || + ('A' <= ch && ch <= 'Z')); +} + +static bool +digit(char ch) +{ + return ('0' <= ch && ch <= '9'); +} diff --git a/hack/pathnames.h b/hack/pathnames.h new file mode 100644 index 0000000..fbb4a97 --- /dev/null +++ b/hack/pathnames.h @@ -0,0 +1,35 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pathnames.h 8.1 (Berkeley) 5/31/93 + */ + +#define _PATH_MAIL "/usr/bin/mail" +#define _PATH_QUEST "/var/games/questdir" +#define _PATH_HACK "/var/games/hackdir" + diff --git a/hack/rnd.c b/hack/rnd.c new file mode 100644 index 0000000..b761dd9 --- /dev/null +++ b/hack/rnd.c @@ -0,0 +1,35 @@ +/* rnd.c - version 1.0.2 */ +/* $FreeBSD: src/games/hack/rnd.c,v 1.5 1999/11/16 10:26:38 marcel Exp $ */ +/* $DragonFly: src/games/hack/rnd.c,v 1.3 2006/08/21 19:45:32 pavalos Exp $ */ + +#include "hack.h" + +#define RND(x) (random() % x) + +int +rn1(int x, int y) +{ + return (RND(x) + y); +} + +int +rn2(int x) +{ + return (RND(x)); +} + +int +rnd(int x) +{ + return (RND(x) + 1); +} + +int +d(int n, int x) +{ + int tmp = n; + + while (n--) + tmp += RND(x); + return (tmp); +} diff --git a/hack/rumors b/hack/rumors new file mode 100644 index 0000000..9435a5f --- /dev/null +++ b/hack/rumors @@ -0,0 +1,505 @@ +"Quit" is a four letter word. +"So when I die, the first thing I will see in Heaven is a score list?" +-- more -- +...and rings may protect your fingers. +...and sometimes a piercer drops by. +A Quasit is even faster than a jaguar! +A chameleon imitating a postman often delivers scrolls of fire. +A chameleon imitating a postman sometimes delivers scrolls of punishment. +A clove of garlic a day keeps your best friends away. +A cockatrice's corpse is guaranteed to be untainted! +A confused acid blob may attack. +A dead lizard is a good thing to turn undead. +A dragon is just a Snake that ate a scroll of fire. +A fading corridor enlightens your insight. +A glowing potion is too hot to drink. +A good amulet may protect you against guards. +A homunculus wouldnt want to hurt a wizard. +A jaguar shouldn't frighten you. +A long worm can be defined recursively. So how should you attack it? +A long worm hits with all of its length. +A magic vomit pump is a necessity for gourmands. +A monstrous mind is a toy for ever. +A nurse a day keeps the doctor away. +A potion of blindness makes you see invisible things. +A ring is just a wound wand. +A ring of adornment protects against Nymphs. +A ring of conflict is a bad thing if there is a nurse in the room. +A ring of extra ringfinger is useless if not enchanted. +A ring of stealth can be recognised by that it does not teleport you. +A rope may form a trail in a maze. +A rumour has it that rumours are just rumours. +A scroll of enchant amulet is only useful on your way back. +A smoky potion surely affects your vision. +A spear might hit a nurse. +A spear will hit an ettin. +A staff may recharge if you drop it for awhile. +A tin of smoked eel is a wonderful find. +A truly wise man never plays leapfrog with a unicorn. +A two-handed sword usually misses. +A unicorn can be tamed only by a fair maiden. +A visit to the Zoo is very educational; you meet interesting animals. +A wand of deaf is a more dangerous weapon than a wand of sheep. +A wand of vibration might bring the whole cave crashing about your ears. +A winner never quits. A quitter never wins. +A xan is a small animal. It doesn't reach higher than your leg. +Acid blobs should be attacked bare-handed. +Affairs with Nymphs are often very expensive. +Afraid of Mimics? Try to wear a ring of true seeing. +Afraid of falling piercers? Wear a helmet! +After being attacked by a Harpy you have a lot of arrows. +All monsters are created evil, but some are more evil than others. +Always attack a floating Eye from behind! +Always be aware of the phase of the moon! +Always read the info about a monster before dealing with it. +Always sweep the floor before engraving important messages. +Amulets are hard to make. Even for a wand of wishing. +An Umber hulk can be a confusing sight. +An elven cloak is always the height of fashion. +An elven cloak protects against magic. +An ettin is hard to kill; an imp is hard to hit. See the difference? +Any small object that is accidentally dropped will hide under a larger object. +Are you blind? Catch a floating Eye! +Asking about monsters may be very useful. +Attack long worms from the rear - that is so much safer! +Attacking an eel when there is none usually is a fatal mistake! +Balrogs only appear on the deeper levels. +Be careful when eating bananas. Monsters might slip on the peels. +Be careful when eating salmon - your fingers might become greasy. +Be careful when the moon is in its last quarter. +Be careful when throwing a boomerang - you might hit the back of your head. +Be nice to a nurse: put away your weapon and take off your clothes. +Being digested is a painfully slow process. +Better go home and hit your kids. They are just little monsters! +Better go home and play with your kids. They are just little monsters! +Better leave the dungeon, otherwise you might get hurt badly. +Beware of dark rooms - they may be the Morgue. +Beware of death rays! +Beware of falling rocks, wear a helmet! +Beware of hungry dogs! +Beware of the minotaur. He's very horny! +Beware of the potion of Nitroglycerine - it's not for the weak of heart. +Beware of wands of instant disaster. +Beware: there's always a chance that your wand explodes as you try to zap it! +Beyond the 23-rd level lies a happy retirement in a room of your own. +Blank scrolls make more interesting reading. +Blind? Eat a carrot! +Booksellers never read scrolls; it might carry them too far away. +Booksellers never read scrolls; it might leave their shop unguarded. +Changing your suit without dropping your sword? You must be kidding! +Cockatrices might turn themselves to stone faced with a mirror. +Consumption of home-made food is strictly forbidden in this dungeon. +Dark gems are just coloured glass. +Dark room? Just flash often with your camera. +Dark room? Your chance to develop your photographs! +Dark rooms are not *completely* dark: just wait and let your eyes adjust... +Dead lizards protect against a cockatrice. +Death is just around the next door. +Death is life's way of telling you you've been fired. +Descend in order to meet more decent monsters. +Did you know worms had teeth? +Didn't you forget to pay? +Didn't you forget to pay? +Direct a direct hit on your direct opponent, directing in the right direction. +Do something big today: lift a boulder. +Do you want to visit hell? Dig a *very* deep hole. +Dogs are attracted by the smell of tripe. +Dogs do not eat when the moon is full. +Dogs never step on cursed items. +Dogs of ghosts aren't angry, just hungry. +Don't bother about money: only Leprechauns and shopkeepers are interested. +Don't create fireballs: they might turn against you. +Don't eat too much: you might start hiccoughing! +Don't forget! Large dogs are MUCH harder to kill than little dogs. +Don't play hack at your work, your boss might hit you! +Don't swim with weapons or armour: they might rust! +Don't tell a soul you found a secret door, otherwise it isn't secret anymore. +Don't throw gems. They are so precious! Besides, you might hit a roommate. +Drinking might affect your health. +Drop your vanity and get rid of your jewels! Pickpockets about! +Dungeon expects every monster to do his duty. +Dust is an armor of poor quality. +Eat 10 cloves of garlic and keep all humans at a two-square distance. +Eat a homunculus if you want to avoid sickness. +Eating a Wraith is a rewarding experience! +Eating a freezing sphere is like eating a yeti. +Eating a killer bee is like eating a scorpion. +Eating a tengu is like eating a Nymph. +Eating unpaid Leprechauns may be advantageous. +Eels hide under mud. Use a unicorn to clear the water and make them visible. +Elven cloaks cannot rust. +Engrave your wishes with a wand of wishing. +Eventually all wands of striking do strike. +Eventually you will come to admire the swift elegance of a retreating nymph. +Ever fought with an enchanted tooth? +Ever heard hissing outside? I *knew* you hadn't! +Ever seen a leocrotta dancing the tengu? +Ever slept in the arms of a homunculus? +Ever tamed a shopkeeper? +Ever tried digging through a Vault Guard? +Ever tried enchanting a rope? +Ever tried to catch a flying boomerang? +Ever tried to put a Troll into a large box? +Ever wondered why one would want to dip something in a potion? +Every dog should be a domesticated one. +Every hand has only one finger to put a ring on. You've got only two hands. So? +Every level contains a shop; only the entrance is often hidden. +Everybody should have tasted a scorpion at least once in his life. +Expensive cameras have penetrating flashlights. +Feeding the animals is strictly prohibited. The Management. +Feeling lousy? Why don't you drink a potion of tea? +Fiery letters might deter monsters. +First Law of Hacking: leaving is much more difficult than entering. +For any remedy there is a misery. +Fourth Law of Hacking: you will find the exit at the entrance. +Gems are the droppings of other inmates. +Gems do get a burden. +Genocide on shopkeepers is punishable. +Getting Hungry? Stop wearing rings! +Getting Hungry? Wear an amulet! +Ghosts always empty the fridge. +Ghosts are visible because they don't leave a trace. +Giant beetles make giant holes in giant trees! +Giving head to a long worm is like a long lasting reception. +Gold is a heavy metal. +Good day for overcoming obstacles. Try a steeplechase. +Gossip is the opiate of the depressed. +Hackers do it with bugs. +Half Moon tonight. (At least it's better than no Moon at all.) +Handle your flasks carefully - there might be a ghost inside! +Have a good meal today: eat a minotaur. +Hey guys, you *WIELD* a dead lizard against a cocatrice! [David London] +Hissing is a sound I hate. +Hitting is the lingua franca in these regions. +Humans use walking canes when they grow old. +Hunger is a confusing experience for a dog! +Hungry dogs are unreliable. +Hungry? There is an abundance of food on the next level. +Hungry? Wear an amulet! +I doubt whether nurses are virgins. +I guess you have never hit a postman with an Amulet of Yendor yet... +I once knew a hacker who ate too fast and choked to death..... +I smell a maze of twisty little passages. +I wished, I never wished a wand of wishing. (Wishful thinking) +If "nothing happens", something *has* happened anyway!! +If a chameleon mimics a mace, it really mimics a Mimic mimicking a mace. +If a shopkeeper kicks you out of his shop, he'll kick you out of the dungeon. +If you are being punished, it's done with a deadly weapon. +If you are the shopkeeper you can take things for free. +If you are too cute some monsters might be tempted to embrace you. +If you can't learn to do it well, learn to enjoy doing it badly. +If you need a wand of digging, kindly ask the minotaur. +If you see nurses you better start looking somewhere for a doctor. +If you turn blind: don't expect your dog to be turned into a seeing-eye dog. +If you want to feal great, you must eat something real big. +If you want to float you'd better eat a floating eye. +If you want to genocide nurses, genocide @'s. +If you want to hit, use a dagger. +If you want to rob a shop, train your dog. +If you're afraid of trapdoors, just cover the floor with all you've got. +If you're lost, try buying a map next time you're in a shop. +If your ghost kills a player, it increases your score. +Important mail? Be careful that it isn't stolen! +Improve your environment, using a wand of rearrangement. +In a hurry? Try a ride on a fast moving quasit! +In a way, a scorpion is like a snake. +In need of a rest? Quaff a potion of sickness! +In total, there are eight sorts of shops. +Increase mindpower: Tame your own ghost! +Inside a shop you better take a look at the price tags before buying anything. +It furthers one to see the great man. +It is bad manners to use a wand in a shop. +It is not always a good idea to whistle for your dog. +It is said that Giant Rabbits can be tamed with carrots only. +It is said that purple worms and trappers fill the same niche. +It might be a good idea to offer the unicorn a ruby. +It seems you keep overlooking a sign reading "No trespassing"! +It would be peculiarly sad were your dog turned to stone. +It's all a matter of life and death, so beware of the undead. +It's bad luck to drown a postman. +It's bad luck, being punished. +It's easy to overlook a monster in a wood. +It's not safe to Save. +Jackals are intrinsically rotten. +Just below any trapdoor there may be another one. Just keep falling! +Keep a clear mind: quaff clear potions. +Keep your armours away from rust. +Keep your weaponry away from acids. +Kicking the terminal doesn't hurt the monsters. +Kill a unicorn and you kill your luck. +Killer bees keep appearing till you kill their queen. +Large dogs make larger turds than little ones. +Latest news? Put 'net.games.hack' in your .newsrc ! +Latest news? Put newsgroup 'netUNX.indoor.hackers-scroll' in your .newsrc! +Learn how to spell. Play Hack! +Leather armour cannot rust. +Leprechauns are the most skilled cutpurses in this dungeon. +Leprechauns hide their gold in a secret room. +Let your fingers do the walking on the yulkjhnb keys. +Let's face it: this time you're not going to win. +Let's have a party, drink a lot of booze. +Liquor sellers do not drink; they hate to see you twice. +Looking for a monster -- use a staff of monster summoning. +Looking pale? Quaff a red potion! +M.M.Vault cashiers teleport any amount of gold to the next local branch. +Many monsters make a murdering mob. +Meet yourself! Commit suicide and type "hack" +Meeting your own ghost decreases your luck considerably! +Memory flaw - core dumped. +Money is the root of all evil. +Money to invest? Take it to the local branch of the Magic Memory Vault! +Monsters come from nowhere to hit you everywhere. +Monsters sleep because you are boring, not because they ever get tired. +Most monsters can't swim. +Most monsters prefer minced meat. That's why they are hitting you! +Most rumors are just as misleading as this one. +Much ado Nothing Happens. +Murder complaint? Mail to 'netnix!devil!gamble!freak!trap!lastwill!rip'. +Need money? Sell your corpses to a tin factory. +Never ask a shopkeeper for a price list. +Never attack a guard. +Never drop a crysknife! No, never even unwield it, until... +Never eat with glowing hands! +Never fight a monster: you might get killed. +Never go into the dungeon at midnight. +Never kick a sleeping dog. +Never kiss an animal. It may cause kissing disease. +Never map the labyrinth. +Never mind the monsters hitting you: they just replace the charwomen. +Never ride a long worm. +Never step on a cursed engraving. +Never swim with a camera: there's nothing to take pictures of. +Never trust a random generator in magic fields. +Never use a wand of death. +Never use your best weapon to engrave a curse. +Never vomit on a door mat. +No easy fighting with a heavy load! +No level contains two shops. The maze is no level. So... +No part of this fortune may be reproduced, stored in a retrieval system, ... +No weapon is better than a crysknife. +Not all rumors are as misleading as this one. +Not even a spear will hit a Xorn. +Now what is it that cures digestion? +Nurses are accustomed to touch naked persons: they don't harm them. +Nurses prefer undressed hackers. +Nymphs and nurses like beautiful rings. +Nymphs are blondes. Are you a gentleman? +Nymphs are very pleased when you call them by their real name: Lorelei. +Offering a unicorn a worthless piece of glass might prove to be fatal! +Old hackers never die: young ones do. +Old trees sometimes fall without a warning! +Once your little dog will be a big dog, and you will be proud of it. +One can even choke in a fortune cookie! +One has to leave shops before closing time. +One homunculus a day keeps the doctor away. +One level further down somebody is getting killed, right now. +One wand of concentration equals eight scrolls of create monster. +Only Today! A dramatic price-cut on slightly used wands. +Only a Nymph knows how to unlock chains. +Only a dragon will never get a cold from a wand of cold. +Only a real dummy would ever call his sword 'Elbereth'. +Only a wizard can use a magic whistle. +Only adventurers of evil alignment think of killing their dog. +Only cave-women can catch a unicorn. And then only with a golden rope. +Only chaotic evils kill sleeping monsters. +Only david can find the zoo! +Only real trappers escape traps. +Only real wizards can write scrolls. +Only wizards are able to zap a wand. +Opening a tin is difficult, especially when you are not so strong! +Opening a tin is difficult, especially when you attempt this bare handed! +Operation coded OVERKILL has started now. +Orcs and killer bees share their lifestyle. +Orcs do not procreate in dark rooms. +PLEASE ignore previous rumour. +Plain nymphs are harmless. +Playing billiards pays when you are in a shop. +Polymorphing your dog probably makes you safer. +Praying will frighten Demons. +Punishment is a thing you call over yourself. So why complain? +Pursue the monsters and you will be had indeed. +Put on a ring of teleportation: it will take you away from onslaught. +Rays aren't boomerangs, of course, but still... +Read the manual before entering the cave - You might get killed otherwise. +Reading Herbert will disgust you, but in one case it might be enlightening. +Reading Tolkien might help you. +Reading might change your vision. +Reading might improve your scope. +Relying on a dog might turn you in a dog addict. +Reward your doggie with a giant Bat. +Ropes are made from the long, blond hairs of dead Nymphs. +Row (3x) that boat gently down the stream, Charon (4x), death is but a dream. +Running is good for your legs. +Rust monsters love water. There are potions they hate, however. +Savings do include amnesia. +Scorpions often hide under tripe rations. +Screw up your courage! You've screwed up everything else. +Scrolls of fire are useful against fog clouds. +Second Law of Hacking: first in, first out. +Selling and rebuying a wand will recharge it. +Shopkeepers accept creditcards, as long as you pay cash. +Shopkeepers are vegetarians: they only eat Swedes. +Shopkeepers can't read, so what use is engraving in a shop? +Shopkeepers can't swim. +Shopkeepers have incredible patience. +Shopkeepers often have strange names. +Shopkeepers sometimes die from old age. +Sleeping may increase your strength. +Snakes are often found under worthless objects. +Some Balrogs don't attack if you offer them a ring. +Some mazes (especially small ones) have no solutions, says man 6 maze. +Some monsters can be tamed. I once saw a hacker with a tame Dragon! +Some potions are quite mind-expanding. +Some questions Sphynxes ask just *don't* have any answers. +Sometimes "mu" is the answer. +Sometimes monsters are more likely to fight each other than attack you. +Sorry, no fortune this time. Better luck next cookie! +Spare your scrolls of make-edible until it's really necessary! +Speed Kills (The Doors) +Spinach, carrot, and a melon - a meal fit for a nurse! +Stay clear of the level of no return. +Suddenly the dungeon will collapse ... +Surprise your dog with an acid blob! +Tainted meat is even more sickening than poison! +Take a long worm from the rear, according to its mate it's a lot more fun. +Tame a troll and it will learn you fighting. +Taming a postman may cause a system security violation. +Taming is a gradual process of excercising and rewarding. +Telepathy is just a trick: once you know how to do it, it's easy. +Teleportation lessens your orientation. +The "pray" command is not yet implemented. +The Jackal only eats bad food. +The Leprechaun Gold Tru$t is no division of the Magic Memory Vault. +The Leprechauns hide their treasure in a small hidden room. +The air is positively magic in here. Better wear a negative armor. +The best equipment for your work is, of course, the most expensive. +The emptiness of a ghost is too heavy to bear. +The key to this game is that there are no keys. +The longer the wand the better. +The moon is not the only heavenly body to influence this game. +The postman always rings twice. +The proof of the quivering blob is in the eating thereof. +The secret of wands of Nothing Happens: try again! +The use of dynamite is dangerous. +There are better information sources than fortune cookies. +There are monsters of softening penetration. +There are monsters of striking charity. +There have been people like you in here; their ghosts seek revenge on you. +There is a VIP-lounge on this level. Only first-class travellers admitted. +There is a big treasure hidden in the zoo! +There is a message concealed in each fortune cookie. +There is a trap on this level! +There is more magic in this cave than meets the eye. +There is no business like throw business. +There is no harm in praising a large dog. +There is nothing like eating a Mimic. +There seem to be monsters of touching benevolence. +They say a gelatinous cube can paralyse you... +They say that Elven cloaks absorb enchantments. +They say that a dagger hits. +They say that a dog avoids traps. +They say that a dog can be trained to fetch objects. +They say that a dog never steps on a cursed object. +They say that a spear will hit a Dragon. +They say that a spear will hit a Xorn. +They say that a spear will hit a neo-otyugh. (Do YOU know what that is?) +They say that a spear will hit an ettin. +They say that a two-handed sword misses. +They say that a unicorn might bring you luck. +They say that an elven cloak may be worn over your armor. +They say that an elven cloak protects against magic. +They say that cavemen seldom find tins in the dungeon. +They say that dead lizards protect against a cockatrice. +They say that killing a shopkeeper brings bad luck. +They say that monsters never step on a scare monster scroll. +They say that only david can find the zoo! +They say that shopkeepers often have a large amount of money in their purse. +They say that the owner of the dungeon might change it slightly. +They say that the use of dynamite is dangerous. +They say that the walls in shops are made of extra hard material. +They say that there is a big treasure hidden in the zoo! +They say that there is a message concealed in each fortune cookie. +They say that there is a trap on this level! +They say that throwing food at a wild dog might tame him. +They say that you can meet old friends in the caves. +They say that you can't take your pick-axe into a shop. +They say that you cannot trust scrolls of rumour. +They say that you need a key in order to open locked doors. +Third Law of Hacking: the last blow counts most. +This dungeon is restroom equipped (for your convenience). +This fortune cookie is property of Fortune Cookies, Inc. +This is not a fortune. +This is the Leprechaun Law: every purse has a price. +Throwing food at a wild dog might tame him. +Tin openers are rare indeed. +Tired of irritating bats? Try a scroll of silence. +To hit or not to hit, that is the question. +To reach heaven, escape the dungeon while wearing a ring of levitation. +Tranquillizers might get you killed. +Travel fast, use some magic speed! +Tripe on its own is revolting, but with onions it's delicious! +Try hacking in the wee hours: you will have more room. +Try the fall back end run play against ghosts. +Ulch, that meat was painted. +Unwanted mail? Sell it to the bookshop! +Vampires hate garlic. +Vault guards always make sure you aren't a shopkeeper. +Vault guards never disturb their Lords. +Visitors are requested not to apply genocide to shopkeepers. +WARNING from H.M. Govt: Quaffing may be dangerous to your health. +Wanna fly? Eat a bat. +Want a hint? Zap a wand of make invisible on your weapon! +Want fun? Throw a potion in a pool and go swimming! +Want to conserve your dead corpses? Go to the tin factory! +Wanted: shopkeepers. Send a scroll of mail to: Mage of Yendor/Level 35/Dungeon. +Warning: end of file 'fortunes' reached. +Warning: people who eat dragons can go to hell!! +Watch your steps on staircases. +Wear armor, going naked seems to offend public decency in here. +What a pity, you cannot read it! +What do you think is the use of dead lizards? +What do you think would be the use of a two handed sword called "Orcrist" ? +When a piercer drops in on you, you will be tempted to hit the ceiling! +When in a maze follow the right wall and you will never get lost. +When in a shop, do as shopkeepers do. +When punished, watch your steps on the stairs! +When you have a key, you don't have to wait for the guard. +When you have seen one killer bee, you have seen them all. +When your dog follows you through a trap door, don't hit it! +Where do you think all those demons come from? From Hell, of course. +Where do you think the hell is located? It must be deep, deep down. +Who should ever have thought one could live from eating fog clouds? +Why a "2" for the postman? Well, how many times does he ring? +Why should one ever throw an egg to a cockatrice? +Why would anybody in his sane mind engrave "Elbereth" ? +Wish for a master key and open the Magic Memory Vault! +Wish for a pass-key and pass all obstacles! +Wish for a skeleton-key and open all doors! +Wishing too much may bring you too little. +Wizards do not sleep. +You are heading for head-stone for sure. +You are just the kind of bad food some monsters like to digest. +You can always wear an elven cloak. +You can eat what your dog can eat. +You can get a genuine Amulet of Yendor by doing the following: -- more -- +You can't get rid of a cursed plate mail with a can-opener. +You can't leave a shop through the back door: there ain't one! +You cannot ride a long worm. +You cannot trust scrolls of rumour. +You die... +You feel greedy and want more gold? Why don't you try digging? +You feel like someone is pulling your leg. +You have to outwit a Sphynx or pay her. +You may get rich selling letters, but beware of being blackmailed! +You may have a kick from kicking a little dog. +You might choke on your food by eating fortune cookies. +You might cut yourself on a long sword. +You might trick a shopkeeper if you're invisible. +You need a key in order to open locked doors. +You offend Shai-Hulud by sheathing your crysknife without having drawn blood. +You want to regain strength? Two levels ahead is a guesthouse! +You'll need a spear if you want to attack a Dragon. +You've got to know how to put out a yellow light. +Your dog can buy cheaper than you do. +Zapping a wand of Nothing Happens doesn't harm you a bit. +Zapping a wand of undead turning might bring your dog back to life. |