diff options
248 files changed, 66290 insertions, 0 deletions
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..75fd54f --- /dev/null +++ b/Makefile @@ -0,0 +1,70 @@ +# Makefile for bsd-games-extra, by B. Watson. This file is +# released under the WTFPL, so do WTF you want with it. + +# You don't want to parallelize this. Use "make -j1" if you +# normally set -jN in your MAKEFLAGS. + +# This is just the date, yyyymmdd +VERSION=20150209 + +# Slackware-specific stuff. Try MANDIR=/usr/share/man and PMAKE=bmake +# for other Linux distros. +MANDIR=/usr/man +PMAKE=pmake + +# This bit of ugliness requires GNU make. I don't know BSD make +# well enough to write the equivalent for it. +INC:=$(shell pwd)/include +CFLAGS="$(OPTFLAGS) -I$(INC) -include bsdcompat.h -std=gnu99" + +# Which games are we building? +DIRS=boggle bs cgram ching colorbars \ + dab dm grdc hack hals_end larn \ + paranoia rogue tetris + +# BSD Makefiles don't create subdirs for man pages, so we have +# to do it here. +MANSECTS=5 6 8 + +# Some of this stuff installs with wrong permissions, +# fix_perms rule fixes it according to these 3 variables: +SCOREFILES=tetris.scores rogue.scores +SGIDBINS=tetris rogue hack +SAVEDIRS=hackdir larn + +all: + for dir in $(DIRS); do ( cd $$dir && $(PMAKE) CFLAGS=$(CFLAGS) ); done + [ -x boggle/mkdict/mkdict ] && cd boggle && $(PMAKE) realall + +clean: + for dir in $(DIRS); do ( cd $$dir && $(PMAKE) clean ); done + +install: install_files fix_perms + +install_files: + for sect in $(MANSECTS); do \ + mkdir -p $(DESTDIR)/$(MANDIR)/man$$sect ; \ + done + for dir in $(DIRS); do \ + ( cd $$dir && \ + $(PMAKE) install \ + INSTALL="install -D" \ + MANDIR=$(MANDIR) \ + DOCDIR=/usr/doc/bsd-games-extra-$(VERSION) \ + ) ; \ + done + rm -f $(DESTDIR)/mkdict $(DESTDIR)/mkindex + +fix_perms: + for file in $(SCOREFILES); do \ + install -D -o root -g games -m664 /dev/null $(DESTDIR)/var/games/$$file ; \ + done + for bin in $(SGIDBINS); do \ + chown root:games $(DESTDIR)/usr/games/$$bin ; \ + chmod 2755 $(DESTDIR)/usr/games/$$bin ; \ + done + for dir in $(SAVEDIRS); do \ + mkdir -p $(DESTDIR)/var/games/$$dir ; \ + chown -R root:games $(DESTDIR)/var/games/$$dir ; \ + chmod -R ug+rw $(DESTDIR)/var/games/$$dir ; \ + done diff --git a/Makefile.inc b/Makefile.inc new file mode 100644 index 0000000..48090e0 --- /dev/null +++ b/Makefile.inc @@ -0,0 +1,26 @@ +# $NetBSD: Makefile.inc,v 1.16 2014/03/23 00:17:40 dholland Exp $ +# @(#)Makefile.inc 8.1 (Berkeley) 5/31/93 + +MKHIDEGAME?= no + +.if defined(HIDEGAME) && (${MKHIDEGAME} != no) && defined(PROG) +BINDIR= /usr/games/hide +BINGRP= games +.if defined(SETGIDGAME) +USE_FORT?= yes +BINMODE= 2550 +.else +BINMODE= 550 +.endif +SYMLINKS+= dm /usr/games/${PROG} +.else +BINDIR= /usr/games +.if defined(SETGIDGAME) +BINGRP= games +BINMODE= 2555 +.endif +.endif +# Note: do not bother with WARNS=6 until -Wconversion is either +# removed from WARNS=6 or rendered useful by improving gcc; as it is +# (with gcc48) it produces buckets of drivel. +WARNS?= 5 @@ -0,0 +1,101 @@ +bsd-games-extra - more games ported from *BSD. + +This package is meant to have all the games from BSD that are missing +from Slackware's bsd-games. These are: + +boggle +bs +cgram +ching +colorbars +dab +dm +grdc +hack +hals_end +larn +paranoia +rogue +tetris + +boggle, dm, and tetris are included in the Linux bsd-games-2.13 source +but are not built by Slackware's bsd-games.SlackBuild. + +hack, larn, paranoia, and rogue were removed from the Linux port of +bsd-games for license reasons. + +The others were never included in the Linux bsd-games source. + +Sources: + +bs, grdc, hack from DragonFlyBSD git: +http://gitweb.dragonflybsd.org/dragonfly.git/tree/v4.0.3:/games + +boggle cgram ching colorbars dab dm hals_end larn rogue tetris are +from NetBSD: +http://ftp.netbsd.org/pub/NetBSD/NetBSD-current/tar_files/src/games.tar.gz + +paranoia isn't actually a BSD game (it came from a magazine), but +it used to be distributed with bsd-games-1.3. I grabbed a copy here: +http://web.mit.edu/games/src/bsd-games/paranoia/ + +Building: + +You need GNU make and the Slackware pmake package (which is an old and +unmaintained port of Berkeley make). Run "make" in the top-level dir, +which will run pmake for each subdir. "make install" will install +everything, "make install DESTDIR=/somewhere" is useful for packagers. + +If you're not on Slackware, you probably will need MANDIR=/usr/share/man +on the make command line. Also, if your Berkeley make is called bmake, +say PMAKE=bmake (good luck). + +License: + +This README and the top-level Makefile are released under the WTFPL, +so do WTF you want with them. include/bsdcompat.h is mostly copied from +either libbsd's headers, or NetBSD source. The games are mostly licensed +with BSD licenses (big surprise there), but see the games themselves +for license details (comments in the source, mostly). + +Notes: + +I made no gameplay changes, and only two cosmetic changes: + +- larn was missing the player's @ symbol. In the ancient versions + of larn (posted to usenet, ported to DOS, Atari ST, etc), the @ + was there, like most roguelike games use. The code that printed the @ + started causing a segfault on UNIX at some point, so someone removed it, + and just let the terminal's cursor indicate the player's position. I + put the @ back and fixed the segfaulting bug because this is how + the game's original author intended it to be. + +- tetris was using black as a color, for the borders and for one of + the pieces. This is no good in the Linux console, or default + settings for Konsole or xfce4-terminal, since those all have black + backgrounds. xterm and rxvt still default to white, but I'm assuming + anyone old-school enough to use xterm or rxvt for playing Tetris is + going to already know how to change the bg color. + +I took a different approach than the bsd-games package uses. Instead of +rewriting the Makefiles from scratch for GNU make, or using autotools, +I used the BSD Makefiles, with as few modifications as I could get +away with. You can grep for my initials (bkw) to find my changes to the +Makefiles and sources. + +Functions like strlcpy() are from libbsd, but trying to #include anything +from /usr/include/bsd just led to breakage, so the neccessary BSD-flavored +macros and prototypes are in include/bsdcompat.h. + +For the most part, I stuck with NetBSD sources, where available. The games +ported from DragonFlyBSD mostly don't exist in NetBSD (the exception being +hack; the NetBSD version was giving me fits so I used the DragonFlyBSD +one instead). + +A few of these are installed setgid games. YMMV on whether that's an +acceptable security risk, but they were designed to work that way. + +If you're not familiar with BSD-style nroff documentation, it might +help to know how to read the rogue manual: + +nroff -me /usr/doc/bsd-games-extra-20150209/rogue.me | less -R diff --git a/boggle/Makefile b/boggle/Makefile new file mode 100644 index 0000000..4671c08 --- /dev/null +++ b/boggle/Makefile @@ -0,0 +1,40 @@ +# $NetBSD: Makefile,v 1.22 2003/10/21 10:01:19 lukem Exp $ +# @(#)Makefile 8.1 (Berkeley) 6/11/93 + +.include <bsd.own.mk> + +SUBDIR= boggle mkdict mkindex + +MKDICTDIR!= cd $(.CURDIR)/mkdict; ${PRINTOBJDIR} +MKDICT=${MKDICTDIR}/mkdict +MKINDEXDIR!= cd $(.CURDIR)/mkindex; ${PRINTOBJDIR} +MKINDEX=${MKINDEXDIR}/mkindex +# 20150209 bkw: look for dictionary words here: +WORDS=/usr/share/dict/words +DICTFILES=dictionary dictindex +.if ${MKSHARE} != "no" +FILES=${DICTFILES} +FILESDIR=/usr/share/games/boggle +.endif +CLEANFILES+=${DICTFILES} + +realall: ${FILES} + +${MKDICT}: + @cd ${.CURDIR}/mkdict && ${MAKE} + +${MKINDEX}: + @cd ${.CURDIR}/mkindex && ${MAKE} + +dictionary: ${WORDS} ${MKDICT} + ${_MKTARGET_CREATE} + rm -f ${.TARGET} + ./mkdict/${MKDICT} < ${WORDS} > ${.TARGET} + +dictindex: dictionary ${MKINDEX} + ${_MKTARGET_CREATE} + rm -f ${.TARGET} + ./mkindex/${MKINDEX} < dictionary > ${.TARGET} + +.include <bsd.prog.mk> +.include <bsd.subdir.mk> diff --git a/boggle/README b/boggle/README new file mode 100644 index 0000000..fc38842 --- /dev/null +++ b/boggle/README @@ -0,0 +1,66 @@ +$NetBSD: README,v 1.2 1995/03/21 12:14:21 cgd Exp $ + +Bog is a fairly portable simulation of Parker Brother's game of Boggle and +is similar to the 4.[23] BSD "boggle" and Sun's "boggletool". +Bog has not been derived from any proprietary code. +It has been tested on the Sun 3 under SunOS 3.2 and on the Atari 1040ST (MWC). + +What You Need + +You will need curses/termcap and a large word list. +The minix word list or /usr/dict/words will do nicely. +The word list must already be sorted (you can use "sort -c" to check). + +Contents + + README - this file + Makefile + bog.man - half-hearted man page (use the game's help command) + bog.h - configuration and header info + bog.c - machine independent game code + word.c - machine independent word list routines + help.c - (curses) help routine + mach.c - (curses) display code + prtable.c - ditto + timer.c - machine dependent (os) input polling + mkdict.c - convert a word list to a bog dictionary + mkindex.c - create an index file for the bog dictionary + showdict.c - print a bog dictionary to stdout + +Portability + +- I've tried to make bog.c (the program logic) independent of the I/O. + My plan was to make it straightforward to adapt the game to run under a + windowing system (eg., Suntools, GEM). I have no plan to actually do this. + I've stuck to a small subset of the curses routines. +- The program runs with the input in raw mode. +- If you want the running timer you must #define TIMER in bog.h + and insert the input polling code in timer.c for your system. There is + already code there for BSD, SYSV, and ATARI. + +Setup + +1. Check bog.h and Makefile and edit to fit your environment +2. "make all" + This will make all the binaries and create the dictionary and index files +3. Move "dict", "dict.ind", and "helpfile" to where you specified in bog.h +4. Play away + +Distribution + +You may use this software for your enjoyment and you may share it with others. +You may not sell this software or use it for any commercial purposes +whatsoever. All modified versions of the software that you redistribute must +clearly indicate your changes. + +If you come across any bugs or make any changes you'd like to share please +send mail to me rather than posting to the net. + +Enjoy. [But beware: boggle can be addictive!] + +----- +Barry Brachman | UUCP: {alberta,uw-beaver,uunet}! +Dept. of Computer Science| ubc-vision!ubc-csgrads!brachman +Univ. of British Columbia| Internet: brachman@cs.ubc.ca +Vancouver, B.C. V6T 1W5 | brachman%ubc.csnet@csnet-relay.arpa +(604) 228-5010 | brachman@ubc.csnet diff --git a/boggle/boggle/Makefile b/boggle/boggle/Makefile new file mode 100644 index 0000000..400b438 --- /dev/null +++ b/boggle/boggle/Makefile @@ -0,0 +1,19 @@ +# $NetBSD: Makefile,v 1.10 2010/02/06 23:45:25 he Exp $ +# @(#)Makefile 8.1 (Berkeley) 6/11/93 + +.include <bsd.own.mk> + +PROG= boggle +SRCS= bog.c help.c mach.c prtable.c timer.c word.c +DPADD= ${LIBCURSES} ${LIBTERMINFO} +# 20150209 bkw: removed -lterminfo from LDADD, added -lbsd +LDADD= -lcurses -lbsd +HIDEGAME=hidegame +MAN= boggle.6 +.if ${MKSHARE} != "no" +FILES= helpfile +FILESDIR=/usr/share/games/boggle +.endif + +.include "../../Makefile.inc" +.include <bsd.prog.mk> diff --git a/boggle/boggle/bog.c b/boggle/boggle/bog.c new file mode 100644 index 0000000..e24d9e2 --- /dev/null +++ b/boggle/boggle/bog.c @@ -0,0 +1,714 @@ +/* $NetBSD: bog.c,v 1.29 2014/03/22 23:39:04 dholland Exp $ */ + +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Barry Brachman. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1993\ + The Regents of the University of California. All rights reserved."); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)bog.c 8.2 (Berkeley) 5/4/95"; +#else +__RCSID("$NetBSD: bog.c,v 1.29 2014/03/22 23:39:04 dholland Exp $"); +#endif +#endif /* not lint */ + +#include <ctype.h> +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <sys/tty.h> + +#include "bog.h" +#include "extern.h" + +static char *batchword(FILE *); +static void playgame(void); +static int validword(const char *); +static void checkdict(void); +static void newgame(const char *); +static int compar(const void *, const void *); +static void clearwordpath(int full); +static void usage(void) __dead; + +struct dictindex dictindex[26]; + +/* + * Cube position numbering: + * + * 0 1 2 3 + * 4 5 6 7 + * 8 9 A B + * C D E F + */ +static int adjacency[16][16] = { +/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ + { 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* 0 */ + { 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* 1 */ + { 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, /* 2 */ + { 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, /* 3 */ + { 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0 }, /* 4 */ + { 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0 }, /* 5 */ + { 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0 }, /* 6 */ + { 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0 }, /* 7 */ + { 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0 }, /* 8 */ + { 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0 }, /* 9 */ + { 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1 }, /* A */ + { 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1 }, /* B */ + { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0 }, /* C */ + { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0 }, /* D */ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1 }, /* E */ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0 } /* F */ +}; + +static int letter_map[26][16]; + +char board[17]; +int wordpath[MAXWORDLEN + 1]; +int wordlen; /* Length of last word returned by nextword() */ +int usedbits; + +const char *pword[MAXPWORDS]; +static char pwords[MAXPSPACE], *pwordsp; +int npwords; + +const char *mword[MAXMWORDS]; +static char mwords[MAXMSPACE], *mwordsp; +int nmwords; + +int ngames = 0; +int tnmwords = 0, tnpwords = 0; + +#include <setjmp.h> +jmp_buf env; + +time_t start_t; +int debug; +int tlimit; + +static FILE *dictfp; +static int batch; +static int minlength; +static int reuse; + +int +main(int argc, char *argv[]) +{ + long seed; + int ch, done, i, selfuse, sflag; + char *bspec, *p; + + /* revoke setgid privileges */ + setgid(getgid()); + + seed = 0; + batch = debug = reuse = selfuse = sflag = 0; + bspec = NULL; + minlength = 3; + tlimit = 180; /* 3 minutes is standard */ + + while ((ch = getopt(argc, argv, "bds:t:w:")) != -1) + switch(ch) { + case 'b': + batch = 1; + break; + case 'd': + debug = 1; + break; + case 's': + sflag = 1; + seed = atol(optarg); + break; + case 't': + if ((tlimit = atoi(optarg)) < 1) + errx(1, "bad time limit"); + break; + case 'w': + if ((minlength = atoi(optarg)) < 3) + errx(1, "min word length must be > 2"); + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + /* process final arguments */ + if (argc > 0) { + if (strcmp(argv[0], "+") == 0) + reuse = 1; + else if (strcmp(argv[0], "++") == 0) + selfuse = 1; + } + + if (reuse || selfuse) { + argc -= 1; + argv += 1; + } + + if (argc > 0) { + if (islower((unsigned char)argv[0][0])) { + if (strlen(argv[0]) != 16) { + usage(); + } else { + /* This board is assumed to be valid... */ + bspec = argv[0]; + } + } else { + usage(); + } + } + + if (batch && bspec == NULL) + errx(1, "must give both -b and a board setup"); + + if (selfuse) + for (i = 0; i < 16; i++) + adjacency[i][i] = 1; + + if (batch) { + newgame(bspec); + while ((p = batchword(stdin)) != NULL) + (void) printf("%s\n", p); + exit (0); + } + setup(sflag, seed); + prompt("Loading the dictionary..."); + if ((dictfp = opendict(DICT)) == NULL) { + warn("%s", DICT); + cleanup(); + exit(1); + } +#ifdef LOADDICT + if (loaddict(dictfp) < 0) { + warnx("can't load %s", DICT); + cleanup(); + exit(1); + } + (void)fclose(dictfp); + dictfp = NULL; +#endif + if (loadindex(DICTINDEX) < 0) { + warnx("can't load %s", DICTINDEX); + cleanup(); + exit(1); + } + + prompt("Type <space> to begin..."); + while (inputch() != ' '); + + for (done = 0; !done;) { + newgame(bspec); + bspec = NULL; /* reset for subsequent games */ + playgame(); +#ifdef NEW_STYLE + prompt("Type <q>uit, <esc> locate, any other key to continue..."); +#else + prompt("Type <space> to continue, any cap to quit..."); +#endif + delay(50); /* wait for user to quit typing */ + flushin(stdin); + for (;;) { + ch = inputch(); +#ifdef NEW_STYLE + if (ch == '\e') + findword(); + else if (ch == CTRL('l') || ch == CTRL('r')) + redraw(); + else { + if (ch == 'q') { + done = 1; + break; + } else { + break; + } + } +#else + if (ch == '\e') + findword(); + else if (ch == CTRL('l') || ch == CTRL('r')) + redraw(); + else { + if (isupper(ch)) { + done = 1; + break; + } + if (ch == ' ') + break; + } +#endif + } + } + cleanup(); + exit (0); +} + +/* + * Read a line from the given stream and check if it is legal + * Return a pointer to a legal word or a null pointer when EOF is reached + */ +static char * +batchword(FILE *fp) +{ + char *w; + + clearwordpath(1); + while ((w = nextword(fp)) != NULL) { + if (wordlen < minlength) + continue; + clearwordpath(0); + usedbits = 0; + if (checkword(w, -1, wordpath) != -1) + return (w); + } + return (NULL); +} + +/* + * Play a single game + * Reset the word lists from last game + * Keep track of the running stats + */ +static void +playgame(void) +{ + int i; + time_t t; + char buf[MAXWORDLEN + 1]; + + ngames++; + npwords = 0; + pwordsp = pwords; + nmwords = 0; + mwordsp = mwords; + + time(&start_t); + + clearwordpath(1); + showboard(board); + startwords(); + if (setjmp(env)) { + badword(); + goto timesup; + } + + while (1) { + if (get_line(buf) == NULL) { + if (feof(stdin)) + clearerr(stdin); + break; + } + time(&t); + if (t - start_t >= tlimit) { + badword(); + break; + } + if (buf[0] == '\0') { + int remaining; + + remaining = tlimit - (int) (t - start_t); + (void)snprintf(buf, sizeof(buf), + "%d:%02d", remaining / 60, remaining % 60); + showstr(buf, 1); + continue; + } + if (strlen(buf) < (size_t)minlength) { + badword(); + continue; + } + + clearwordpath(0); + usedbits = 0; + + if (checkword(buf, -1, wordpath) < 0) + badword(); + else { + if (debug) { + (void) printf("["); + for (i = 0; wordpath[i] != -1; i++) + (void) printf(" %d", wordpath[i]); + (void) printf(" ]\n"); + } + for (i = 0; i < npwords; i++) { + if (strcmp(pword[i], buf) == 0) + break; + } + if (i != npwords) { /* already used the word */ + badword(); + showword(i); + } + else if (!validword(buf)) + badword(); + else { + size_t len; + + len = strlen(buf) + 1; + if (npwords == MAXPWORDS - 1 || + pwordsp + len >= &pwords[MAXPSPACE]) { + warnx("Too many words!"); + cleanup(); + exit(1); + } + pword[npwords++] = pwordsp; + (void) strcpy(pwordsp, buf); + pwordsp += len; + addword(buf); + } + } + } + +timesup: ; + + /* + * Sort the player's words and terminate the list with a null + * entry to help out checkdict() + */ + qsort(pword, npwords, sizeof(pword[0]), compar); + pword[npwords] = NULL; + + /* + * These words don't need to be sorted since the dictionary is sorted + */ + checkdict(); + + tnmwords += nmwords; + tnpwords += npwords; + + results(); +} + +/* + * Check if the given word is present on the board, with the constraint + * that the first letter of the word is adjacent to square 'prev' + * Keep track of the current path of squares for the word + * A 'q' must be followed by a 'u' + * Words must end with a null + * Return 1 on success, -1 on failure + */ +int +checkword(const char *word, int prev, int *path) +{ + const char *p; + char *q; + int i, *lm; + + if (debug) { + (void) printf("checkword(%s, %d, [", word, prev); + for (i = 0; wordpath[i] != -1; i++) + (void) printf(" %d", wordpath[i]); + (void) printf(" ]\n"); + } + + if (*word == '\0') + return (1); + + lm = letter_map[*word - 'a']; + + if (prev == -1) { + char subword[MAXWORDLEN + 1]; + + /* + * Check for letters not appearing in the cube to eliminate some + * recursive calls + * Fold 'qu' into 'q' + */ + p = word; + q = subword; + while (*p != '\0') { + if (*letter_map[*p - 'a'] == -1) + return (-1); + *q++ = *p; + if (*p++ == 'q') { + if (*p++ != 'u') + return (-1); + } + } + *q = '\0'; + while (*lm != -1) { + *path = *lm; + usedbits |= (1 << *lm); + if (checkword(subword + 1, *lm, path + 1) > 0) + return (1); + usedbits &= ~(1 << *lm); + lm++; + } + return (-1); + } + + /* + * A cube is only adjacent to itself in the adjacency matrix if selfuse + * was set, so a cube can't be used twice in succession if only the + * reuse flag is set + */ + for (i = 0; lm[i] != -1; i++) { + if (adjacency[prev][lm[i]]) { + int used; + + used = 1 << lm[i]; + /* + * If necessary, check if the square has already + * been used. + */ + if (!reuse && (usedbits & used)) + continue; + *path = lm[i]; + usedbits |= used; + if (checkword(word + 1, lm[i], path + 1) > 0) + return (1); + usedbits &= ~used; + } + } + *path = -1; /* in case of a backtrack */ + return (-1); +} + +/* + * A word is invalid if it is not in the dictionary + * At this point it is already known that the word can be formed from + * the current board + */ +static int +validword(const char *word) +{ + int j; + const char *q, *w; + + j = word[0] - 'a'; + if (dictseek(dictfp, dictindex[j].start, SEEK_SET) < 0) { + (void) fprintf(stderr, "Seek error\n"); + cleanup(); + exit(1); + } + + while ((w = nextword(dictfp)) != NULL) { + int ch; + + if (*w != word[0]) /* end of words starting with word[0] */ + break; + q = word; + while ((ch = *w++) == *q++ && ch != '\0') + ; + if (*(w - 1) == '\0' && *(q - 1) == '\0') + return (1); + } + if (dictfp != NULL && feof(dictfp)) /* Special case for z's */ + clearerr(dictfp); + return (0); +} + +/* + * Check each word in the dictionary against the board + * Delete words from the machine list that the player has found + * Assume both the dictionary and the player's words are already sorted + */ +static void +checkdict(void) +{ + char *p, *w; + const char **pw; + int i; + int prevch, previndex, st; + + mwordsp = mwords; + nmwords = 0; + pw = pword; + prevch ='a'; + + (void) dictseek(dictfp, 0L, SEEK_SET); + while ((w = nextword(dictfp)) != NULL) { + if (wordlen < minlength) + continue; + if (*w != prevch) { + /* + * If we've moved on to a word with a different first + * letter then we can speed things up by skipping all + * words starting with a letter that doesn't appear in + * the cube. + */ + i = (int) (*w - 'a'); + while (i < 26 && letter_map[i][0] == -1) + i++; + if (i == 26) + break; + previndex = prevch - 'a'; + prevch = i + 'a'; + /* + * Fall through if the word's first letter appears in + * the cube (i.e., if we can't skip ahead), otherwise + * seek to the beginning of words in the dictionary + * starting with the next letter (alphabetically) + * appearing in the cube and then read the first word. + */ + if (i != previndex + 1) { + if (dictseek(dictfp, + dictindex[i].start, SEEK_SET) < 0) { + warnx("seek error in checkdict()"); + cleanup(); + exit(1); + } + continue; + } + } + + clearwordpath(0); + usedbits = 0; + if (checkword(w, -1, wordpath) == -1) + continue; + + st = 1; + while (*pw != NULL && (st = strcmp(*pw, w)) < 0) + pw++; + if (st == 0) /* found it */ + continue; + if (nmwords == MAXMWORDS || + mwordsp + wordlen + 1 >= &mwords[MAXMSPACE]) { + warnx("too many words!"); + cleanup(); + exit(1); + } + mword[nmwords++] = mwordsp; + p = w; + while ((*mwordsp++ = *p++) != '\0') + ; + } +} + +/* + * Crank up a new game + * If the argument is non-null then it is assumed to be a legal board spec + * in ascending cube order, oth. make a random board + */ +static void +newgame(const char *b) +{ + int i, p, q; + const char *tmp; + int *lm[26]; + static const char *cubes[16] = { + "ednosw", "aaciot", "acelrs", "ehinps", + "eefhiy", "elpstu", "acdemp", "gilruw", + "egkluy", "ahmors", "abilty", "adenvz", + "bfiorx", "dknotu", "abjmoq", "egintv" + }; + + if (b == NULL) { + /* + * Shake the cubes and make the board + */ + i = 0; + while (i < 100) { + p = (int) (random() % 16); + q = (int) (random() % 16); + if (p != q) { + tmp = cubes[p]; + cubes[p] = cubes[q]; + cubes[q] = tmp; + i++; + } + /* else try again */ + } + + for (i = 0; i < 16; i++) + board[i] = cubes[i][random() % 6]; + } + else { + for (i = 0; i < 16; i++) + board[i] = b[i]; + } + board[16] = '\0'; + + /* + * Set up the map from letter to location(s) + * Each list is terminated by a -1 entry + */ + for (i = 0; i < 26; i++) { + lm[i] = letter_map[i]; + *lm[i] = -1; + } + + for (i = 0; i < 16; i++) { + int j; + + j = (int) (board[i] - 'a'); + *lm[j] = i; + *(++lm[j]) = -1; + } + + if (debug) { + for (i = 0; i < 26; i++) { + int ch, j; + + (void) printf("%c:", 'a' + i); + for (j = 0; (ch = letter_map[i][j]) != -1; j++) + (void) printf(" %d", ch); + (void) printf("\n"); + } + } + +} + +/* + * Clear wordpath[]. + */ +static void +clearwordpath(int full) +{ + size_t pos; + const size_t max = MAXWORDLEN + 1; + + for (pos = 0; pos < max && (full || wordpath[pos] != -1); pos++) { + wordpath[pos] = -1; + } +} + +static int +compar(const void *p, const void *q) +{ + return (strcmp(*(const char *const *)p, *(const char *const *)q)); +} + +static void +usage(void) +{ + (void) fprintf(stderr, + "usage: %s [-bd] [-s#] [-t#] [-w#] [+[+]] [boardspec]\n", + getprogname()); + exit(1); +} diff --git a/boggle/boggle/bog.h b/boggle/boggle/bog.h new file mode 100644 index 0000000..06d2f5b --- /dev/null +++ b/boggle/boggle/bog.h @@ -0,0 +1,83 @@ +/* $NetBSD: bog.h,v 1.3 2003/08/07 09:37:05 agc Exp $ */ + +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Barry Brachman. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)bog.h 8.1 (Berkeley) 6/11/93 + */ + +#define LOADDICT 1 /* Load the dictionary for speed */ + +/* + * Locations for the dictionary (generated by mkdict), + * index (generated by mkindex), and helpfile + */ +#define DICT "/usr/share/games/boggle/dictionary" +#define DICTINDEX "/usr/share/games/boggle/dictindex" +#define HELPFILE "/usr/share/games/boggle/helpfile" + +/* + * The theoretical maximum for MAXWORDLEN is ('a' - 1) == 96 + */ +#define MAXWORDLEN 40 /* Maximum word length */ +#define MAXPWORDS 200 /* Maximum number of player's words */ +#define MAXMWORDS 200 /* Maximum number of machine's words */ +#define MAXPSPACE 2000 /* Space for player's words */ +#define MAXMSPACE 4000 /* Space for machines's words */ + +#define MAXCOLS 20 + +/* + * The following determine the screen layout + */ +#define PROMPT_COL 20 +#define PROMPT_LINE 2 + +#define BOARD_COL 0 +#define BOARD_LINE 0 + +#define SCORE_COL 20 +#define SCORE_LINE 0 + +#define LIST_COL 0 +#define LIST_LINE 10 + +#define TIMER_COL 20 +#define TIMER_LINE 2 + +/* + * Internal dictionary index + * Initialized from the file created by mkindex + */ +struct dictindex { + long start; + long length; +}; diff --git a/boggle/boggle/boggle.6 b/boggle/boggle/boggle.6 new file mode 100644 index 0000000..f5812ff --- /dev/null +++ b/boggle/boggle/boggle.6 @@ -0,0 +1,127 @@ +.\" $NetBSD: boggle.6,v 1.9 2006/09/24 01:38:57 jmcneill Exp $ +.\" +.\" Copyright (c) 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Barry Brachman. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)boggle.6 8.1 (Berkeley) 6/11/93 +.\" +.Dd September 23, 2006 +.Dt BOGGLE 6 +.Os +.Sh NAME +.Nm boggle +.Nd word search game +.Sh SYNOPSIS +.Nm +.Op Fl bd +.Op Fl s Ar seed +.Op Fl t Ar time +.Op Fl w Ar length +.Op + Op + +.Op boardspec +.Sh DESCRIPTION +The object of +.Nm +is to find as many words as possible on the Boggle board within the three +minute time limit. +A Boggle board is a four by four arrangement of Boggle cubes, each side of +each cube displaying a letter of the alphabet or `qu'. +Words are formed by finding a sequence of cubes (letters) that are in the +game's dictionary. +The (N+1)th cube in the word must be horizontally, +vertically, or diagonally adjacent to the Nth cube. +Cubes cannot be reused. +Words consist solely of lower case letters and must be at least 3 letters long. +.Pp +Command line flags can be given to change the rules of the game. +.Bl -tag -width boardspec +.It Fl b +Run +.Nm +in batch mode. +A +.Ar boardspec +must also be given. +The dictionary is read from stdin and a list of words appearing in +.Ar boardspec +is printed to stdout. +.It Fl d +Enable debugging output. +.It Fl s Ar seed +Specify a seed +.Ar seed +other than the time of day. +.It Fl t Ar time +Set the time limit for each game from the default 3 minutes to +.Ar time +seconds. +.It Fl w Ar length +Change the minimum word length from 3 letters to +.Ar length . +.It Ar + +This flag allows a cube to be used multiple times, but not in succession. +.It Ar ++ +This flag allows the same cubes to be considered adjacent to itself. +.It Ar boardspec +A starting board position can be specified on the command line by +listing the board left to right and top to bottom. +.El +.Pp +Help is available during play by typing +.Sq Ic \&? . +More detailed information on the game is given there. +.Sh AUTHORS +Boggle is a trademark of Parker Brothers. +.Pp +Barry Brachman +.Pp +Dept. of Computer Science +.Pp +University of British Columbia +.Sh BUGS +If there are a great many words in the cube the final display of the words +may scroll off of the screen. +(On a 25 line screen about 130 words can be displayed.) +.Pp +No word can contain a +.Sq q +that is not immediately followed by a +.Sq u . +.Pp +When using the +.Ar + +or +.Ar ++ +options the display of words found in the board doesn't indicate reused cubes. +.Pp +The dictionary that +.Nx +installs omits many words that belong in the English language, most +notably inflected forms. diff --git a/boggle/boggle/extern.h b/boggle/boggle/extern.h new file mode 100644 index 0000000..ee9a340 --- /dev/null +++ b/boggle/boggle/extern.h @@ -0,0 +1,62 @@ +/* $NetBSD: extern.h,v 1.11 2009/08/12 05:29:40 dholland Exp $ */ + +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)extern.h 8.1 (Berkeley) 6/11/93 + */ + +#include <time.h> + +void addword(const char *); +void badword(void); +int checkword(const char *, int, int *); +void cleanup(void); +void delay(int); +long dictseek(FILE *, long, int); +void findword(void); +void flushin(FILE *); +char *get_line(char *); +int help(void); +int inputch(void); +int loaddict(FILE *); +int loadindex(const char *); +char *nextword(FILE *); +FILE *opendict(const char *); +void prompt(const char *); +void prtable(const char *const [], + int, int, int, void (*)(const char *const [], int), + int (*)(const char *const [], int)); +void redraw(void); +void results(void); +int setup(int, time_t); +void showboard(const char *); +void showstr(const char *, int); +void showword(int); +void startwords(void); +int timerch(void); diff --git a/boggle/boggle/help.c b/boggle/boggle/help.c new file mode 100644 index 0000000..0310683 --- /dev/null +++ b/boggle/boggle/help.c @@ -0,0 +1,105 @@ +/* $NetBSD: help.c,v 1.7 2011/08/31 16:24:55 plunky Exp $ */ + +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Barry Brachman. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)help.c 8.1 (Berkeley) 6/11/93"; +#else +__RCSID("$NetBSD: help.c,v 1.7 2011/08/31 16:24:55 plunky Exp $"); +#endif +#endif /* not lint */ + +#include <curses.h> +#include <stdio.h> + +#include "bog.h" +#include "extern.h" + +extern int nlines; + +int +help(void) +{ + int eof, i; + FILE *fp; + WINDOW *win; + char buf[BUFSIZ]; + + if ((fp = fopen(HELPFILE, "r")) == NULL) + return(-1); + win = newwin(0, 0, 0, 0); + clearok(win, 1); + + eof = 0; + if (ungetc(getc(fp), fp) == EOF) { + wprintw(win, "There doesn't seem to be any help."); + eof = 1; /* Nothing there... */ + } + + while (!eof) { + for (i = 0; i < nlines - 3; i++) { + if (fgets(buf, sizeof(buf), fp) == NULL) { + eof = 1; + break; + } + if (buf[0] == '.' && buf[1] == '\n') + break; + wprintw(win, "%s", buf); + } + if (eof || ungetc(getc(fp), fp) == EOF) { + eof = 1; + break; + } + wmove(win, nlines - 1, 0); + wprintw(win, + "Type <space> to continue, anything else to quit..."); + wrefresh(win); + if ((inputch() & 0177) != ' ') + break; + wclear(win); + } + + fclose(fp); + if (eof) { + wmove(win, nlines - 1, 0); + wprintw(win, "Hit any key to continue..."); + wrefresh(win); + inputch(); + } + delwin(win); + clearok(stdscr, 1); + refresh(); + return(0); +} diff --git a/boggle/boggle/helpfile b/boggle/boggle/helpfile new file mode 100644 index 0000000..c511efe --- /dev/null +++ b/boggle/boggle/helpfile @@ -0,0 +1,92 @@ + +Commands: + +Enter word: <return> or <linefeed> or <space> +Delete previous character: <delete> or <backspace> +Delete line: <^u> or <^w> +Redraw screen: <^l> or <^r> +Pause game: <^s> +Resume game: <^q> or <^s> +Suspend game (BSD only): <^z> +Give up on current cube: <^d> +Show remaining time: <space> first thing on a line +Show help: ? (Suspends timer until done) +Exit game: <^c> + +(^u means "control u", etc.) + +[Note for users of versions of this program that do not display a timer: +The first word entered after the timer has run out causes a list of all the +words you found, the words you missed, and your running statistics to be +displayed.] + +Any time you are prompted while the board is displayed you can type: + <esc>word +to see where "word" is on the board. + +Usage: + bog [-b] [-d] [-s#] [-t#] [-w#] [+[+]] [boardspec] + + -b: batch mode (boardspec must be present); dictionary read from stdin + -d: debug mode + -s#: use # as the random number seed + -t#: time limit is # seconds instead of default 180 + -w#: minimum word length is # letters instead of default 3 + +: can reuse a cube, but not twice in succession + ++: can reuse cubes arbitrarily + boardspec: the first board to use (use 'q' for 'qu'); e.g.: + bog nolezeebnqieegei +. + Default Rules + +A Boggle board is a four by four arrangement of Boggle cubes. +You have 3 minutes to find as many words as possible in the Boggle board. +Words are formed by finding a sequence of cubes (letters) that are in the +game's dictionary. The (N+1)th cube in the word must be horizontally, +vertically, or diagonally adjacent to the Nth cube. Cubes cannot be reused. +Words consist solely of lower case letters and must be at least 3 letters long. +. + Options + +Command line flags can be given to change the rules of the game. +The '+' flag allows a cube to be used multiple times, but not in succession. +The '++' flag makes each cube adjacent to itself. +The time limit can be changed from the default 3 minutes by using the flag +'-t#', where # is the duration (in seconds) of each game. +The minimum word length can be changed from 3 letters by specifying 'w#', +where # is the minimum number of letters to use. +. + Bugs and Limitations + +The following bugs and problems are known to exist: + +- If there are a great many words in the cube the final display of the words + may scroll off of the screen. (On a 25 line screen about 130 words can be + displayed.) + +- Computing the complete word list can be too slow on small machines. + +- No word can contain a 'q' that is not immediately followed by a 'u'. + +- When using the '+' or '++' options the display of words found in the board + doesn't indicate reused cubes. +. + About This Program + +Permission is given to freely copy and distribute this software providing: + +1) You do not sell it, +2) You do not use it for commercial advantage, +3) If you pass the program on you must make the source code available, and +4) This notice must accompany the distribution + +Please notify the author of any bugs or if you have any suggestions. + +Copyright (c) 1988 +Barry Brachman | UUCP: {alberta,uw-beaver,uunet}! +Dept. of Computer Science| ubc-vision!ubc-csgrads!brachman +Univ. of British Columbia| Internet: brachman@cs.ubc.ca +Vancouver, B.C. V6T 1W5 | brachman%ubc.csnet@csnet-relay.arpa +(604) 228-5010 | brachman@ubc.csnet + +Boggle is a trademark of Parker Brothers. diff --git a/boggle/boggle/mach.c b/boggle/boggle/mach.c new file mode 100644 index 0000000..7c86176 --- /dev/null +++ b/boggle/boggle/mach.c @@ -0,0 +1,665 @@ +/* $NetBSD: mach.c,v 1.21 2011/08/31 16:24:55 plunky Exp $ */ + +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Barry Brachman. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)mach.c 8.1 (Berkeley) 6/11/93"; +#else +__RCSID("$NetBSD: mach.c,v 1.21 2011/08/31 16:24:55 plunky Exp $"); +#endif +#endif /* not lint */ + +/* + * Terminal interface + * + * Input is raw and unechoed + */ +#include <sys/ioctl.h> + +#include <ctype.h> +#include <curses.h> +#include <fcntl.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <termios.h> +#include <time.h> + +#include "bog.h" +#include "extern.h" + +static int ccol, crow, maxw; +static int colstarts[MAXCOLS], ncolstarts; +static int lastline; +static int ncols; +int nlines; + +extern const char *pword[], *mword[]; +extern int ngames, nmwords, npwords, tnmwords, tnpwords; +extern char board[]; +extern int usedbits, wordpath[]; +extern time_t start_t; +extern int debug; + +static void cont_catcher(int); +static int prwidth(const char *const [], int); +static void prword(const char *const [], int); +static void stop_catcher(int); +static void tty_cleanup(void); +static int tty_setup(void); +static void tty_showboard(const char *); +static void winch_catcher(int); +static void getword(char *); +static void starttime(void); +static void stoptime(void); + + +/* + * Do system dependent initialization + * This is called once, when the program starts + */ +int +setup(int sflag, time_t seed) +{ + if (tty_setup() < 0) + return(-1); + + if (!sflag) + time(&seed); + srandom(seed); + if (debug) + (void) printf("seed = %ld\n", (long) seed); + return(0); +} + +/* + * Do system dependent clean up + * This is called once, just before the program terminates + */ +void +cleanup(void) +{ + tty_cleanup(); +} + +/* + * Display the player's word list, the list of words not found, and the running + * stats + */ +void +results(void) +{ + int col, row; + int denom1, denom2; + + move(LIST_LINE, LIST_COL); + clrtobot(); + printw("Words you found (%d):", npwords); + refresh(); + move(LIST_LINE + 1, LIST_COL); + prtable(pword, npwords, 0, ncols, prword, prwidth); + + getyx(stdscr, row, col); + move(row + 1, col); + printw("Words you missed (%d):", nmwords); + refresh(); + move(row + 2, col); + prtable(mword, nmwords, 0, ncols, prword, prwidth); + + denom1 = npwords + nmwords; + denom2 = tnpwords + tnmwords; + + move(SCORE_LINE, SCORE_COL); + printw("Percentage: %0.2f%% (%0.2f%% over %d game%s)\n", + denom1 ? (100.0 * npwords) / (double) (npwords + nmwords) : 0.0, + denom2 ? (100.0 * tnpwords) / (double) (tnpwords + tnmwords) : 0.0, + ngames, ngames > 1 ? "s" : ""); +} + +static void +prword(const char *const base[], int indx) +{ + printw("%s", base[indx]); +} + +static int +prwidth(const char *const base[], int indx) +{ + return (strlen(base[indx])); +} + +/* + * Main input routine + * + * - doesn't accept words longer than MAXWORDLEN or containing caps + */ +char * +get_line(char *q) +{ + int ch, done; + char *p; + int row, col; + + p = q; + done = 0; + while (!done) { + ch = timerch(); + switch (ch) { + case '\n': + case '\r': + case ' ': + done = 1; + break; + case '\e': + findword(); + break; + case '\177': /* <del> */ + case CTRL('h'): /* <bs> */ + if (p == q) + break; + p--; + getyx(stdscr, row, col); + move(row, col - 1); + clrtoeol(); + refresh(); + break; + case CTRL('u'): /* <^u> */ + case CTRL('w'): /* <^w> */ + if (p == q) + break; + getyx(stdscr, row, col); + move(row, col - (int) (p - q)); + p = q; + clrtoeol(); + refresh(); + break; +#ifdef SIGTSTP + case CTRL('z'): /* <^z> */ + stop_catcher(0); + break; +#endif + case CTRL('s'): /* <^s> */ + stoptime(); + printw("<PAUSE>"); + refresh(); + while ((ch = inputch()) != '\021' && ch != '\023') + ; + move(crow, ccol); + clrtoeol(); + refresh(); + starttime(); + break; + case CTRL('c'): /* <^c> */ + cleanup(); + exit(0); + /*NOTREACHED*/ + case CTRL('d'): /* <^d> */ + done = 1; + ch = EOF; + break; + case CTRL('r'): /* <^l> */ + case CTRL('l'): /* <^r> */ + redraw(); + break; + case '?': + stoptime(); + if (help() < 0) + showstr("Can't open help file", 1); + touchwin(stdscr); + starttime(); + break; + default: + if (!islower(ch)) + break; + if ((int) (p - q) == MAXWORDLEN) { + p = q; + badword(); + break; + } + *p++ = ch; + addch(ch); + refresh(); + break; + } + } + *p = '\0'; + if (ch == EOF) + return (NULL); + return(q); +} + +int +inputch(void) +{ + return (getch() & 0177); +} + +void +redraw(void) +{ + clearok(stdscr, 1); + refresh(); +} + +void +flushin(FILE *fp) +{ + + (void) tcflush(fileno(fp), TCIFLUSH); +} + +static int gone; + +/* + * Stop the game timer + */ +static void +stoptime(void) +{ + time_t t; + + (void)time(&t); + gone = (int) (t - start_t); +} + +/* + * Restart the game timer + */ +static void +starttime(void) +{ + time_t t; + + (void)time(&t); + start_t = t - (long) gone; +} + +/* + * Initialize for the display of the player's words as they are typed + * This display starts at (LIST_LINE, LIST_COL) and goes "down" until the last + * line. After the last line a new column is started at LIST_LINE + * Keep track of each column position for showword() + * There is no check for exceeding COLS + */ +void +startwords(void) +{ + crow = LIST_LINE; + ccol = LIST_COL; + maxw = 0; + ncolstarts = 1; + colstarts[0] = LIST_COL; + move(LIST_LINE, LIST_COL); + refresh(); +} + +/* + * Add a word to the list and start a new column if necessary + * The maximum width of the current column is maintained so we know where + * to start the next column + */ +void +addword(const char *w) +{ + int n; + + if (crow == lastline) { + crow = LIST_LINE; + ccol += (maxw + 5); + colstarts[ncolstarts++] = ccol; + maxw = 0; + move(crow, ccol); + } + else { + move(++crow, ccol); + if ((n = strlen(w)) > maxw) + maxw = n; + } + refresh(); +} + +/* + * The current word is unacceptable so erase it + */ +void +badword(void) +{ + + move(crow, ccol); + clrtoeol(); + refresh(); +} + +/* + * Highlight the nth word in the list (starting with word 0) + * No check for wild arg + */ +void +showword(int n) +{ + int col, row; + + row = LIST_LINE + n % (lastline - LIST_LINE + 1); + col = colstarts[n / (lastline - LIST_LINE + 1)]; + move(row, col); + standout(); + printw("%s", pword[n]); + standend(); + move(crow, ccol); + refresh(); + delay(15); + move(row, col); + printw("%s", pword[n]); + move(crow, ccol); + refresh(); +} + +/* + * Get a word from the user and check if it is in either of the two + * word lists + * If it's found, show the word on the board for a short time and then + * erase the word + * + * Note: this function knows about the format of the board + */ +void +findword(void) +{ + int c, col, found, i, r, row; + char buf[MAXWORDLEN + 1]; + + getyx(stdscr, r, c); + getword(buf); + found = 0; + for (i = 0; i < npwords; i++) { + if (strcmp(buf, pword[i]) == 0) { + found = 1; + break; + } + } + if (!found) { + for (i = 0; i < nmwords; i++) { + if (strcmp(buf, mword[i]) == 0) { + found = 1; + break; + } + } + } + for (i = 0; i < MAXWORDLEN; i++) + wordpath[i] = -1; + usedbits = 0; + if (!found || checkword(buf, -1, wordpath) == -1) { + move(r, c); + clrtoeol(); + addstr("[???]"); + refresh(); + delay(10); + move(r, c); + clrtoeol(); + refresh(); + return; + } + + standout(); + for (i = 0; wordpath[i] != -1; i++) { + row = BOARD_LINE + (wordpath[i] / 4) * 2 + 1; + col = BOARD_COL + (wordpath[i] % 4) * 4 + 2; + move(row, col); + if (board[wordpath[i]] == 'q') + printw("Qu"); + else + printw("%c", + toupper((unsigned char)board[wordpath[i]])); + move(r, c); + refresh(); + delay(5); + } + + standend(); + + for (i = 0; wordpath[i] != -1; i++) { + row = BOARD_LINE + (wordpath[i] / 4) * 2 + 1; + col = BOARD_COL + (wordpath[i] % 4) * 4 + 2; + move(row, col); + if (board[wordpath[i]] == 'q') + printw("Qu"); + else + printw("%c", + toupper((unsigned char)board[wordpath[i]])); + } + move(r, c); + clrtoeol(); + refresh(); +} + +/* + * Display a string at the current cursor position for the given number of secs + */ +void +showstr(const char *str, int delaysecs) +{ + addstr(str); + refresh(); + delay(delaysecs * 10); + move(crow, ccol); + clrtoeol(); + refresh(); +} + +/* + * Get a valid word and put it in the buffer + */ +static void +getword(char *q) +{ + int ch, col, done, i, row; + char *p; + + done = 0; + i = 0; + p = q; + addch('['); + refresh(); + while (!done && i < MAXWORDLEN - 1) { + ch = getch() & 0177; + switch (ch) { + case '\177': /* <del> */ + case '\010': /* <bs> */ + if (p == q) + break; + p--; + getyx(stdscr, row, col); + move(row, col - 1); + clrtoeol(); + break; + case '\025': /* <^u> */ + case '\027': /* <^w> */ + if (p == q) + break; + getyx(stdscr, row, col); + move(row, col - (int) (p - q)); + p = q; + clrtoeol(); + break; + case ' ': + case '\n': + case '\r': + done = 1; + break; + case '\014': /* <^l> */ + case '\022': /* <^r> */ + clearok(stdscr, 1); + refresh(); + break; + default: + if (islower(ch)) { + *p++ = ch; + addch(ch); + i++; + } + break; + } + refresh(); + } + *p = '\0'; + addch(']'); + refresh(); +} + +void +showboard(const char *b) +{ + tty_showboard(b); +} + +void +prompt(const char *mesg) +{ + move(PROMPT_LINE, PROMPT_COL); + printw("%s", mesg); + move(PROMPT_LINE + 1, PROMPT_COL); + refresh(); +} + +static int +tty_setup(void) +{ + if (!initscr()) { + fprintf(stderr, "couldn't initialize screen\n"); + exit (0); + } + raw(); + noecho(); + + /* + * Does curses look at the winsize structure? + * Should handle SIGWINCH ... + */ + nlines = LINES; + lastline = nlines - 1; + ncols = COLS; + + signal(SIGTSTP, stop_catcher); + signal(SIGCONT, cont_catcher); + signal(SIGWINCH, winch_catcher); + return(0); +} + +static void +stop_catcher(int signo __attribute__((unused))) +{ + sigset_t isigset, osigset; + + stoptime(); + noraw(); + echo(); + move(nlines - 1, 0); + refresh(); + + signal(SIGTSTP, SIG_DFL); + sigemptyset(&isigset); + sigaddset(&isigset, SIGTSTP); + sigprocmask(SIG_UNBLOCK, &isigset, &osigset); + kill(0, SIGTSTP); + sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0); + signal(SIGTSTP, stop_catcher); +} + +static void +cont_catcher(int signo __attribute__((unused))) +{ + noecho(); + raw(); + clearok(stdscr, 1); + move(crow, ccol); + refresh(); + starttime(); +} + +/* + * The signal is caught but nothing is done about it... + * It would mean reformatting the entire display + */ +static void +winch_catcher(int signo __attribute__((unused))) +{ + struct winsize win; + + (void) signal(SIGWINCH, winch_catcher); + (void) ioctl(fileno(stdout), TIOCGWINSZ, &win); + /* + LINES = win.ws_row; + COLS = win.ws_col; + */ +} + +static void +tty_cleanup(void) +{ + move(nlines - 1, 0); + refresh(); + noraw(); + echo(); + endwin(); +} + +static void +tty_showboard(const char *b) +{ + int i; + int line; + + clear(); + move(BOARD_LINE, BOARD_COL); + line = BOARD_LINE; + printw("+---+---+---+---+"); + move(++line, BOARD_COL); + for (i = 0; i < 16; i++) { + if (b[i] == 'q') + printw("| Qu"); + else + printw("| %c ", toupper((unsigned char)b[i])); + if ((i + 1) % 4 == 0) { + printw("|"); + move(++line, BOARD_COL); + printw("+---+---+---+---+"); + move(++line, BOARD_COL); + } + } + move(SCORE_LINE, SCORE_COL); + printw("Type '?' for help"); + refresh(); +} diff --git a/boggle/boggle/prtable.c b/boggle/boggle/prtable.c new file mode 100644 index 0000000..63d57e1 --- /dev/null +++ b/boggle/boggle/prtable.c @@ -0,0 +1,127 @@ +/* $NetBSD: prtable.c,v 1.10 2013/10/19 17:23:08 christos Exp $ */ + +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Barry Brachman. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)prtable.c 8.1 (Berkeley) 6/11/93 + */ + +#include <sys/cdefs.h> +#ifndef lint +__RCSID("$NetBSD: prtable.c,v 1.10 2013/10/19 17:23:08 christos Exp $"); +#endif /* not lint */ + +#include <curses.h> + +#include "extern.h" + +#define NCOLS 5 + +static int get_maxlen(const char *const [], int, int (*)(const char *const *, int)); + +/* + * Routine to print a table + * Modified from 'ls.c' mods (BJB/83) + * Arguments: + * base - address of first entry + * num - number of entries + * d_cols - number of columns to use if > 0, "best" size if == 0 + * width - max line width if not zero + * prentry - address of the routine to call to print the string + * length - address of the routine to call to determine the length + * of string to be printed + * + * prtable and length are called with the address of the base and + * an index + */ +void +prtable(const char *const base[], int num, int d_cols, int width, + void (*prentry)(const char *const [], int), + int (*length)(const char *const [], int)) +{ + int c, j; + int a, b, cols, loc, maxlen, nrows, z; + int col, row; + + if (num == 0) + return; + maxlen = get_maxlen(base, num, length) + 1; + if (d_cols > 0) + cols = d_cols; + else + cols = width / maxlen; + if (cols == 0) + cols = NCOLS; + nrows = (num - 1) / cols + 1; + for (a = 1; a <= nrows; a++) { + b = c = z = loc = 0; + for (j = 0; j < num; j++) { + c++; + if (c >= a + b) + break; + } + while (j < num) { + (*prentry)(base, j); + loc += (*length)(base, j); + z++; + b += nrows; + for (j++; j < num; j++) { + c++; + if (c >= a + b) + break; + } + if (j < num) { + while (loc < z * maxlen) { + addch(' '); + loc++; + } + } + } + getyx(stdscr, row, col); + __USE(col); + move(row + 1, 0); + } + refresh(); +} + +static int +get_maxlen(const char *const base[], int num, + int (*length)(const char *const *, int)) +{ + int i, len, max; + + max = (*length)(base, 0); + for (i = 0; i < num; i++) { + if ((len = (*length)(base, i)) > max) + max = len; + } + return(max); +} diff --git a/boggle/boggle/timer.c b/boggle/boggle/timer.c new file mode 100644 index 0000000..dc4dc69 --- /dev/null +++ b/boggle/boggle/timer.c @@ -0,0 +1,119 @@ +/* $NetBSD: timer.c,v 1.10 2005/07/01 16:38:24 jmc Exp $ */ + +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Barry Brachman. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)timer.c 8.2 (Berkeley) 2/22/94"; +#else +__RCSID("$NetBSD: timer.c,v 1.10 2005/07/01 16:38:24 jmc Exp $"); +#endif +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> +#include <sys/poll.h> + +#include <curses.h> +#include <setjmp.h> +#include <string.h> +#include <stdio.h> +#include <unistd.h> + +#include "bog.h" +#include "extern.h" + +static int waitch(int); + +extern int tlimit; +extern time_t start_t; +extern jmp_buf env; + +/* + * Update the display of the remaining time while waiting for a character + * If time runs out do a longjmp() to the game controlling routine, returning + * non-zero; oth. return the character + * Leave the cursor where it was initially + */ +int +timerch(void) +{ + time_t prevt, t; + int col, remaining, row; + + getyx(stdscr, row, col); + prevt = 0L; + for (;;) { + if (waitch(1) == 1) + break; + time(&t); + if (t == prevt) + continue; + prevt = t; + remaining = tlimit - (int) (t - start_t); + if (remaining < 0) { + longjmp(env, 1); + /*NOTREACHED*/ + } + move(TIMER_LINE, TIMER_COL); + printw("%d:%02d", remaining / 60, remaining % 60); + move(row, col); + refresh(); + } + return (getch() & 0177); +} + +/* + * Wait up to 'delay' microseconds for input to appear + * Returns 1 if input is ready, 0 oth. + */ +static int +waitch(int tdelay) +{ + struct pollfd set[1]; + + set[0].fd = STDIN_FILENO; + set[0].events = POLLIN; + return (poll(set, 1, tdelay)); +} + +void +delay(int tenths) +{ + struct timespec duration; + + duration.tv_nsec = (tenths % 10 ) * 100000000L; + duration.tv_sec = (long) (tenths / 10); + nanosleep(&duration, NULL); +} diff --git a/boggle/boggle/word.c b/boggle/boggle/word.c new file mode 100644 index 0000000..abcc479 --- /dev/null +++ b/boggle/boggle/word.c @@ -0,0 +1,215 @@ +/* $NetBSD: word.c,v 1.9 2006/03/18 09:40:46 rtr Exp $ */ + +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Barry Brachman. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)word.c 8.1 (Berkeley) 6/11/93"; +#else +__RCSID("$NetBSD: word.c,v 1.9 2006/03/18 09:40:46 rtr Exp $"); +#endif +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/stat.h> + +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "bog.h" +#include "extern.h" + +static char *dictspace, *dictend; +static char *sp; + +static int first = 1, lastch = 0; + +extern struct dictindex dictindex[]; +extern int wordlen; + +/* + * Return the next word in the compressed dictionary in 'buffer' or + * NULL on end-of-file + */ +char * +nextword(FILE *fp) +{ + int ch, pcount; + char *p; + static char buf[MAXWORDLEN + 1]; + + if (fp == NULL) { + if (sp == dictend) + return (NULL); + + p = buf + (int) *sp++; + + /* + * The dictionary ends with a null byte + */ + while (*sp >= 'a') + if ((*p++ = *sp++) == 'q') + *p++ = 'u'; + } else { + if (first) { + if ((pcount = getc(fp)) == EOF) + return (NULL); + first = 0; + } else if ((pcount = lastch) == EOF) + return (NULL); + + p = buf + pcount; + + while ((ch = getc(fp)) != EOF && ch >= 'a') + if ((*p++ = ch) == 'q') + *p++ = 'u'; + lastch = ch; + } + wordlen = (int) (p - buf); + *p = '\0'; + return (buf); +} + +/* + * Reset the state of nextword() and do the fseek() + */ +long +dictseek(FILE *fp, long offset, int ptrname) +{ + if (fp == NULL) { + if ((sp = dictspace + offset) >= dictend) + return (-1); + return (0); + } + + first = 1; + return (fseek(fp, offset, ptrname)); +} + +FILE * +opendict(const char *dict) +{ + FILE *fp; + + if ((fp = fopen(dict, "r")) == NULL) + return (NULL); + return (fp); +} + +/* + * Load the given dictionary and initialize the pointers + */ +int +loaddict(FILE *fp) +{ + struct stat statb; + long n; + int st; + char *p; + + if (fstat(fileno(fp), &statb) < 0) { + (void)fclose(fp); + return (-1); + } + + /* + * An extra character (a sentinel) is allocated and set to null + * to improve the expansion loop in nextword(). + */ + if ((dictspace = malloc(statb.st_size + 1)) == NULL) { + (void)fclose(fp); + return (-1); + } + n = (long)statb.st_size; + sp = dictspace; + dictend = dictspace + n; + + p = dictspace; + st = -1; + while (n > 0 && (st = fread(p, 1, BUFSIZ, fp)) > 0) { + p += st; + n -= st; + } + if (st < 0) { + (void)fclose(fp); + warnx("Error reading dictionary"); + return (-1); + } + *p = '\0'; + return (0); +} + +/* + * Dependent on the exact format of the index file: + * Starting offset field begins in column 1 and length field in column 9 + * Taking the easy way out, the input buffer is made "large" and a check + * is made for lines that are too long + */ +int +loadindex(const char *indexfile) +{ + int i, j; + char buf[BUFSIZ]; + FILE *fp; + + if ((fp = fopen(indexfile, "r")) == NULL) { + warn("Can't open '%s'", indexfile); + return (-1); + } + i = 0; + while (fgets(buf, sizeof(buf), fp) != NULL) { + if (strchr(buf, '\n') == NULL) { + warnx("A line in the index file is too long"); + (void) fclose(fp); + return(-1); + } + j = *buf - 'a'; + if (i != j) { + warnx("Bad index order"); + (void) fclose(fp); + return(-1); + } + dictindex[j].start = atol(buf + 1); + dictindex[j].length = atol(buf + 9) - dictindex[j].start; + i++; + } + (void) fclose(fp); + if (i != 26) { + warnx("Bad index length"); + return(-1); + } + return(0); +} diff --git a/boggle/mkdict/Makefile b/boggle/mkdict/Makefile new file mode 100644 index 0000000..079fbd0 --- /dev/null +++ b/boggle/mkdict/Makefile @@ -0,0 +1,10 @@ +# $NetBSD: Makefile,v 1.12 2002/09/18 06:16:40 lukem Exp $ +# @(#)Makefile 8.1 (Berkeley) 6/11/93 + +NOMAN= # defined + +# 20150209 bkw: PROG instead of HOSTPROG +PROG= mkdict +CPPFLAGS+= -I${.CURDIR}/../boggle + +.include <bsd.prog.mk> diff --git a/boggle/mkdict/mkdict.c b/boggle/mkdict/mkdict.c new file mode 100644 index 0000000..6dc083b --- /dev/null +++ b/boggle/mkdict/mkdict.c @@ -0,0 +1,124 @@ +/* $NetBSD: mkdict.c,v 1.11 2005/07/01 16:38:24 jmc Exp $ */ + +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Barry Brachman. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static const char copyright[] = + "@(#) Copyright (c) 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#if 0 +static char sccsid[] = "@(#)mkdict.c 8.1 (Berkeley) 6/11/93"; +#else +static const char rcsid[] = + "$NetBSD: mkdict.c,v 1.11 2005/07/01 16:38:24 jmc Exp $"; +#endif +#endif /* not lint */ + +/* + * Filter out words that: + * 1) Are not completely made up of lower case letters + * 2) Contain a 'q' not immediately followed by a 'u' + * 3) Are less that 3 characters long + * 4) Are greater than MAXWORDLEN characters long + */ + +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "bog.h" + +int +main(int argc, char *argv[]) +{ + char *p, *q; + int ch, common, nwords; + int current, len, prev, qcount; + char buf[2][MAXWORDLEN + 1]; + + prev = 0; + current = 1; + buf[prev][0] = '\0'; + + for (nwords = 1; + fgets(buf[current], MAXWORDLEN + 1, stdin) != NULL; ++nwords) { + if ((p = strchr(buf[current], '\n')) == NULL) { + fprintf(stderr, "word too long: %s\n", buf[current]); + while ((ch = getc(stdin)) != EOF && ch != '\n') + ; + if (ch == EOF) + break; + continue; + } + len = 0; + for (p = buf[current]; *p != '\n'; p++) { + if (!islower((unsigned char)*p)) + break; + if (*p == 'q') { + q = p + 1; + if (*q != 'u') + break; + else { + while ((*q = *(q + 1))) + q++; + } + len++; + } + len++; + } + if (*p != '\n' || len < 3 || len > MAXWORDLEN) + continue; + if (argc == 2 && nwords % atoi(argv[1])) + continue; + + *p = '\0'; + p = buf[current]; + q = buf[prev]; + qcount = 0; + while ((ch = *p++) == *q++ && ch != '\0') + if (ch == 'q') + qcount++; + common = p - buf[current] - 1; + printf("%c%s", common + qcount, p - 1); + prev = !prev; + current = !current; + } + fprintf(stderr, "%d words\n", nwords); + fflush(stdout); + if (ferror(stdout)) { + perror("error writing standard output"); + exit(1); + } + exit(0); +} diff --git a/boggle/mkindex/Makefile b/boggle/mkindex/Makefile new file mode 100644 index 0000000..8f55079 --- /dev/null +++ b/boggle/mkindex/Makefile @@ -0,0 +1,10 @@ +# $NetBSD: Makefile,v 1.10 2002/09/18 06:16:40 lukem Exp $ +# @(#)Makefile 8.1 (Berkeley) 6/11/93 + +NOMAN= # defined + +# 20150209 bkw: PROG instead of HOSTPROG +PROG= mkindex +CPPFLAGS+= -I${.CURDIR}/../boggle + +.include <bsd.prog.mk> diff --git a/boggle/mkindex/mkindex.c b/boggle/mkindex/mkindex.c new file mode 100644 index 0000000..ddda2bd --- /dev/null +++ b/boggle/mkindex/mkindex.c @@ -0,0 +1,131 @@ +/* $NetBSD: mkindex.c,v 1.11 2009/08/12 05:29:40 dholland Exp $ */ + +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Barry Brachman. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = "@(#) Copyright (c) 1993\n\ + The Regents of the University of California. All rights reserved.\n"; + +#if 0 +static char sccsid[] = "@(#)mkindex.c 8.1 (Berkeley) 6/11/93"; +#else +static char rcsid[] = + "$NetBSD: mkindex.c,v 1.11 2009/08/12 05:29:40 dholland Exp $"; +#endif +#endif /* not lint */ + +#include <stdio.h> +#include <stdlib.h> + +#include "bog.h" + +static char *nextword(FILE *, char *, int *, int *); + +int +main(void) +{ + int clen, rlen, prev, i; + long off, start; + char buf[MAXWORDLEN + 1]; + + prev = '\0'; + off = start = 0L; + while (nextword(stdin, buf, &clen, &rlen) != NULL) { + if (*buf != prev) { + /* + * Boggle expects a full index even if the dictionary + * had no words beginning with some letters. + * So we write out entries for every letter from prev + * to *buf. + */ + if (prev != '\0') + printf("%c %6ld %6ld\n", prev, start, off - 1); + for (i = (prev ? prev + 1 : 'a'); i < *buf; i++) + printf("%c %6ld %6ld\n", i, off, off - 1); + prev = *buf; + start = off; + } + off += clen + 1; + } + printf("%c %6ld %6ld\n", prev, start, off - 1); + for (i = prev + 1; i <= 'z'; i++) + printf("%c %6ld %6ld\n", i, off, off - 1); + fflush(stdout); + if (ferror(stdout)) { + perror("error writing standard output"); + exit(1); + } + exit(0); +} + +/* + * Return the next word in the compressed dictionary in 'buffer' or + * NULL on end-of-file + * Also set clen to the length of the compressed word (for mkindex) and + * rlen to the strlen() of the real word + */ +static char * +nextword(FILE *fp, char *buffer, int *clen, int *rlen) +{ + int ch, pcount; + char *p, *q; + static char buf[MAXWORDLEN + 1]; + static int first = 1; + static int lastch = 0; + + if (first) { + if ((pcount = getc(fp)) == EOF) + return (NULL); + first = 0; + } + else if ((pcount = lastch) == EOF) + return (NULL); + + p = buf + (*clen = pcount); + + while ((ch = getc(fp)) != EOF && ch >= 'a') + *p++ = ch; + lastch = ch; + *p = '\0'; + + *rlen = (int) (p - buf); + *clen = *rlen - *clen; + + p = buf; + q = buffer; + while ((*q++ = *p) != '\0') { + if (*p++ == 'q') + *q++ = 'u'; + } + return (buffer); +} diff --git a/bs/Makefile b/bs/Makefile new file mode 100644 index 0000000..b7d56ae --- /dev/null +++ b/bs/Makefile @@ -0,0 +1,10 @@ +# $FreeBSD: src/games/bs/Makefile,v 1.5.2.1 2001/04/25 09:28:49 ru Exp $ +# $DragonFly: src/games/bs/Makefile,v 1.4 2006/10/08 16:22:35 pavalos Exp $ + +PROG= bs +MAN= bs.6 +DPADD= ${LIBNCURSES} +LDADD= -lncurses +HIDEGAME=hidegame + +.include <bsd.prog.mk> @@ -0,0 +1,76 @@ +.\" $FreeBSD: src/games/bs/bs.6,v 1.1.1.1.12.1 2001/07/22 11:32:10 dd Exp $ +.Dd August 23, 1989 +.Dt BS 6 +.Os +.Sh NAME +.Nm bs +.Nd battleships game +.Sh SYNOPSIS +.Nm +.Op Fl bsc +.Sh DESCRIPTION +This program allows you to play the familiar Battleships game against the +computer on a 10x10 board. +The interface is visual and largely self-explanatory; +you place your ships and pick your shots by moving the +cursor around the +.Sq sea +with the +.Xr rogue 6 +/ +.Xr hack 6 +motion keys +.Dq hjklyubn . +.Pp +Note that when selecting a ship to place, you must type the capital letter +(these are, after all, capital ships). +During ship placement, the `r' command may be used to ignore the current +position and randomly place your currently selected ship. +The `R' command will place all remaining ships randomly. +The \&^L command (form feed, ASCII 12) will force a screen redraw). +.Pp +The command-line arguments control game modes. +.Bl -tag -width ".Fl b" +.It Fl b +selects a +.Dq blitz +variant +.It Fl s +selects a +.Dq salvo +variant +.It Fl c +permits ships to be placed adjacently +.El +.Pp +The +.Dq blitz +variant allows a side to shoot for as long as it continues to score hits. +.Pp +The +.Dq salvo +game allows a player one shot per turn for each of his/her ships still afloat. +This puts a premium scoring hits early and knocking out some +ships and also makes much harder the situation where you face a superior force +with only your PT-boat. +.Pp +Normally, ships must be separated by at least one square of open water. +The +.Fl c +option disables this check and allows them to close-pack. +.Pp +The algorithm the computer uses once it has found a ship to sink is provably +optimal. +The dispersion criterion for the random-fire algorithm may not be. +.Sh AUTHORS +.An -nosplit +Originally written by one +.An Bruce Holloway +in 1986. +Salvo mode added by +.An Chuck A. DeGaul Aq cbosgd!cad . +Visual user interface, +.Sq closepack +option, code rewrite and manual page by +.An Eric S. Raymond Aq Mt esr@snark.thyrsus.com , +August 1989. @@ -0,0 +1,1196 @@ +/*- + * bs.c - original author: Bruce Holloway + * salvo option by: Chuck A DeGaul + * with improved user interface, autoconfiguration and code cleanup + * by Eric S. Raymond <esr@snark.thyrsus.com> + * v1.2 with color support and minor portability fixes, November 1990 + * v2.0 featuring strict ANSI/POSIX conformance, November 1993. + * + * $FreeBSD: src/games/bs/bs.c,v 1.9 2000/02/21 03:07:31 billf Exp $ + * $DragonFly: src/games/bs/bs.c,v 1.7 2007/04/18 18:32:12 swildner Exp $ + */ + +#include <assert.h> +#include <ctype.h> +#include <ncurses.h> +#include <signal.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +/* + * Constants for tuning the random-fire algorithm. It prefers moves that + * diagonal-stripe the board with a stripe separation of srchstep. If + * no such preferred moves are found, srchstep is decremented. + */ +#define BEGINSTEP 3 /* initial value of srchstep */ + +/* miscellaneous constants */ +#define SHIPTYPES 5 +#define OTHER (1-turn) +#define PLAYER 0 +#define COMPUTER 1 +#define MARK_HIT 'H' +#define MARK_MISS 'o' +#define CTRLC '\003' /* used as terminate command */ +#define FF '\014' /* used as redraw command */ + +/* coordinate handling */ +#define BWIDTH 10 +#define BDEPTH 10 + +/* display symbols */ +#define SHOWHIT '*' +#define SHOWSPLASH ' ' +#define IS_SHIP(c) isupper(c) + +/* how to position us on player board */ +#define PYBASE 3 +#define PXBASE 3 +#define PY(y) (PYBASE + (y)) +#define PX(x) (PXBASE + (x)*3) +#define pgoto(y, x) move(PY(y), PX(x)) + +/* how to position us on cpu board */ +#define CYBASE 3 +#define CXBASE 48 +#define CY(y) (CYBASE + (y)) +#define CX(x) (CXBASE + (x)*3) +#define cgoto(y, x) move(CY(y), CX(x)) + +#define ONBOARD(x, y) (x >= 0 && x < BWIDTH && y >= 0 && y < BDEPTH) + +/* other board locations */ +#define COLWIDTH 80 +#define PROMPTLINE 21 /* prompt line */ +#define SYBASE CYBASE + BDEPTH + 3 /* move key diagram */ +#define SXBASE 63 +#define MYBASE SYBASE - 1 /* diagram caption */ +#define MXBASE 64 +#define HYBASE SYBASE - 1 /* help area */ +#define HXBASE 0 + +/* this will need to be changed if BWIDTH changes */ +static char numbers[] = " 0 1 2 3 4 5 6 7 8 9"; + +static char carrier[] = "Aircraft Carrier"; +static char battle[] = "Battleship"; +static char sub[] = "Submarine"; +static char destroy[] = "Destroyer"; +static char ptboat[] = "PT Boat"; + +static char name[40]; +static char dftname[] = "stranger"; + +/* direction constants */ +enum directions { E, SE, S, SW, W, NW, N, NE }; +static int xincr[8] = {1, 1, 0, -1, -1, -1, 0, 1}; +static int yincr[8] = {0, 1, 1, 1, 0, -1, -1, -1}; + +/* current ship position and direction */ +static int curx = (BWIDTH / 2); +static int cury = (BDEPTH / 2); + +typedef struct { + char *name; /* name of the ship type */ + char hits; /* how many times has this ship been hit? */ + char symbol; /* symbol for game purposes */ + char length; /* length of ship */ + char x, y; /* coordinates of ship start point */ + enum directions dir; /* direction of `bow' */ + bool placed; /* has it been placed on the board? */ +} +ship_t; + +ship_t plyship[SHIPTYPES] = +{ + { carrier, 0, 'A', 5, 0, 0, E, FALSE}, + { battle, 0, 'B', 4, 0, 0, E, FALSE}, + { destroy, 0, 'D', 3, 0, 0, E, FALSE}, + { sub, 0, 'S', 3, 0, 0, E, FALSE}, + { ptboat, 0, 'P', 2, 0, 0, E, FALSE}, +}; + +ship_t cpuship[SHIPTYPES] = +{ + { carrier, 0, 'A', 5, 0, 0, E, FALSE}, + { battle, 0, 'B', 4, 0, 0, E, FALSE}, + { destroy, 0, 'D', 3, 0, 0, E, FALSE}, + { sub, 0, 'S', 3, 0, 0, E, FALSE}, + { ptboat, 0, 'P', 2, 0, 0, E, FALSE}, +}; + +/* "Hits" board, and main board. */ +static char hits[2][BWIDTH][BDEPTH], board[2][BWIDTH][BDEPTH]; + +static int turn; /* 0=player, 1=computer */ +static int plywon=0, cpuwon=0; /* How many games has each won? */ + +static int salvo, blitz, closepack; + +#define PR addstr + +static void prompt(int, const char *, ...) __printflike(2, 3); +static bool checkplace (int, ship_t *, int); +static int getcoord (int); +int playagain (void); + +/* end the game, either normally or due to signal */ +static void +uninitgame(void) +{ + clear(); + refresh(); + resetterm(); + echo(); + endwin(); + exit(0); +} + +static void +sighandler(__attribute__((unused)) int sig) +{ + uninitgame(); +} + +/* announce which game options are enabled */ +static void +announceopts(void) +{ + printw("Playing %s game (", (salvo || blitz || closepack) ? + "optional" : "standard"); + + if (salvo) + printw("salvo, "); + else + printw("nosalvo, "); + + if (blitz) + printw("blitz, "); + else + printw("noblitz, "); + + if (closepack) + printw("closepack)"); + else + printw("noclosepack)"); +} + +static void +intro(void) +{ + char *tmpname; + + srandomdev(); + + tmpname = getlogin(); + signal(SIGINT,sighandler); + signal(SIGINT,sighandler); + signal(SIGIOT,sighandler); /* for assert(3) */ + if(signal(SIGQUIT,SIG_IGN) != SIG_IGN) + signal(SIGQUIT,sighandler); + + if(tmpname != '\0') { + strcpy(name,tmpname); + name[0] = toupper(name[0]); + } else { + strcpy(name,dftname); + } + + initscr(); +#ifdef KEY_MIN + keypad(stdscr, TRUE); +#endif /* KEY_MIN */ + saveterm(); + nonl(); + cbreak(); + noecho(); + +#ifdef PENGUIN + clear(); + mvaddstr(4,29,"Welcome to Battleship!"); + move(8,0); + PR(" \\\n"); + PR(" \\ \\ \\\n"); + PR(" \\ \\ \\ \\ \\_____________\n"); + PR(" \\ \\ \\_____________ \\ \\/ |\n"); + PR(" \\ \\/ \\ \\/ |\n"); + PR(" \\/ \\_____/ |__\n"); + PR(" ________________/ |\n"); + PR(" \\ S.S. Penguin |\n"); + PR(" \\ /\n"); + PR(" \\___________________________________________________/\n"); + + mvaddstr(22,27,"Hit any key to continue..."); refresh(); + getch(); +#endif /* PENGUIN */ + +#ifdef A_COLOR + start_color(); + + init_pair(COLOR_BLACK, COLOR_BLACK, COLOR_BLACK); + init_pair(COLOR_GREEN, COLOR_GREEN, COLOR_BLACK); + init_pair(COLOR_RED, COLOR_RED, COLOR_BLACK); + init_pair(COLOR_CYAN, COLOR_CYAN, COLOR_BLACK); + init_pair(COLOR_WHITE, COLOR_WHITE, COLOR_BLACK); + init_pair(COLOR_MAGENTA, COLOR_MAGENTA, COLOR_BLACK); + init_pair(COLOR_BLUE, COLOR_BLUE, COLOR_BLACK); + init_pair(COLOR_YELLOW, COLOR_YELLOW, COLOR_BLACK); +#endif /* A_COLOR */ + +} + +/* print a message at the prompt line */ +static void +prompt(int n, const char *f, ...) +{ + char buf[COLWIDTH + 1]; + va_list ap; + + va_start(ap, f); + move(PROMPTLINE + n, 0); + clrtoeol(); + vsnprintf(buf, COLWIDTH + 1, f, ap); + printw("%s", buf); + refresh(); + va_end(ap); +} + +static void +error(const char *s) +{ + move(PROMPTLINE + 2, 0); + clrtoeol(); + if (s) { + addstr(s); + beep(); + } +} + +static void +placeship(int b, ship_t *ss, int vis) +{ + int l; + + for(l = 0; l < ss->length; ++l) { + int newx = ss->x + l * xincr[ss->dir]; + int newy = ss->y + l * yincr[ss->dir]; + + board[b][newx][newy] = ss->symbol; + if (vis) { + pgoto(newy, newx); + addch((chtype)ss->symbol); + } + } + ss->hits = 0; +} + +static int +rnd(int n) +{ + return(random() % n); +} + +/* generate a valid random ship placement into px,py */ +static void +randomplace(int b, ship_t *ss) +{ + do { + ss->y = rnd(BDEPTH); + ss->x = rnd(BWIDTH); + ss->dir = rnd(2) ? E : S; + } while (!checkplace(b, ss, FALSE)); +} + +static void +initgame(void) +{ + int i, j, unplaced; + ship_t *ss; + + clear(); + mvaddstr(0,35,"BATTLESHIPS"); + move(PROMPTLINE + 2, 0); + announceopts(); + + bzero(board, sizeof(char) * BWIDTH * BDEPTH * 2); + bzero(hits, sizeof(char) * BWIDTH * BDEPTH * 2); + for (i = 0; i < SHIPTYPES; i++) { + ss = cpuship + i; + ss->x = ss->y = ss->dir = ss->hits = ss->placed = 0; + ss = plyship + i; + ss->x = ss->y = ss->dir = ss->hits = ss->placed = 0; + } + + /* draw empty boards */ + mvaddstr(PYBASE - 2, PXBASE + 5, "Main Board"); + mvaddstr(PYBASE - 1, PXBASE - 3,numbers); + for(i=0; i < BDEPTH; ++i) + { + mvaddch(PYBASE + i, PXBASE - 3, i + 'A'); +#ifdef A_COLOR + if (has_colors()) + attron(COLOR_PAIR(COLOR_BLUE)); +#endif /* A_COLOR */ + addch(' '); + for (j = 0; j < BWIDTH; j++) + addstr(" . "); +#ifdef A_COLOR + attrset(0); +#endif /* A_COLOR */ + addch(' '); + addch(i + 'A'); + } + mvaddstr(PYBASE + BDEPTH, PXBASE - 3,numbers); + mvaddstr(CYBASE - 2, CXBASE + 7,"Hit/Miss Board"); + mvaddstr(CYBASE - 1, CXBASE - 3, numbers); + for(i=0; i < BDEPTH; ++i) + { + mvaddch(CYBASE + i, CXBASE - 3, i + 'A'); +#ifdef A_COLOR + if (has_colors()) + attron(COLOR_PAIR(COLOR_BLUE)); +#endif /* A_COLOR */ + addch(' '); + for (j = 0; j < BWIDTH; j++) + addstr(" . "); +#ifdef A_COLOR + attrset(0); +#endif /* A_COLOR */ + addch(' '); + addch(i + 'A'); + } + + mvaddstr(CYBASE + BDEPTH,CXBASE - 3,numbers); + + mvprintw(HYBASE, HXBASE, + "To position your ships: move the cursor to a spot, then"); + mvprintw(HYBASE+1,HXBASE, + "type the first letter of a ship type to select it, then"); + mvprintw(HYBASE+2,HXBASE, + "type a direction ([hjkl] or [4862]), indicating how the"); + mvprintw(HYBASE+3,HXBASE, + "ship should be pointed. You may also type a ship letter"); + mvprintw(HYBASE+4,HXBASE, + "followed by `r' to position it randomly, or type `R' to"); + mvprintw(HYBASE+5,HXBASE, + "place all remaining ships randomly."); + + mvaddstr(MYBASE, MXBASE, "Aiming keys:"); + mvaddstr(SYBASE, SXBASE, "y k u 7 8 9"); + mvaddstr(SYBASE+1, SXBASE, " \\|/ \\|/ "); + mvaddstr(SYBASE+2, SXBASE, "h-+-l 4-+-6"); + mvaddstr(SYBASE+3, SXBASE, " /|\\ /|\\ "); + mvaddstr(SYBASE+4, SXBASE, "b j n 1 2 3"); + + /* have the computer place ships */ + for(ss = cpuship; ss < cpuship + SHIPTYPES; ss++) + { + randomplace(COMPUTER, ss); + placeship(COMPUTER, ss, FALSE); + } + + ss = NULL; + do { + char c, docked[SHIPTYPES + 2], *cp = docked; + + /* figure which ships still wait to be placed */ + *cp++ = 'R'; + for (i = 0; i < SHIPTYPES; i++) + if (!plyship[i].placed) + *cp++ = plyship[i].symbol; + *cp = '\0'; + + /* get a command letter */ + prompt(1, "Type one of [%s] to pick a ship.", docked+1); + do { + c = getcoord(PLAYER); + } while + (!strchr(docked, c)); + + if (c == 'R') + ungetch('R'); + else + { + /* map that into the corresponding symbol */ + for (ss = plyship; ss < plyship + SHIPTYPES; ss++) + if (ss->symbol == c) + break; + + prompt(1, "Type one of [hjklrR] to place your %s.", ss->name); + pgoto(cury, curx); + } + + do { + c = getch(); + } while + (!strchr("hjklrR", c) || c == FF); + + if (c == FF) + { + clearok(stdscr, TRUE); + refresh(); + } + else if (c == 'r') + { + prompt(1, "Random-placing your %s", ss->name); + randomplace(PLAYER, ss); + placeship(PLAYER, ss, TRUE); + error(NULL); + ss->placed = TRUE; + } + else if (c == 'R') + { + prompt(1, "Placing the rest of your fleet at random..."); + for (ss = plyship; ss < plyship + SHIPTYPES; ss++) + if (!ss->placed) + { + randomplace(PLAYER, ss); + placeship(PLAYER, ss, TRUE); + ss->placed = TRUE; + } + error(NULL); + } + else if (strchr("hjkl8462", c)) + { + ss->x = curx; + ss->y = cury; + + switch(c) + { + case 'k': case '8': ss->dir = N; break; + case 'j': case '2': ss->dir = S; break; + case 'h': case '4': ss->dir = W; break; + case 'l': case '6': ss->dir = E; break; + } + + if (checkplace(PLAYER, ss, TRUE)) + { + placeship(PLAYER, ss, TRUE); + error(NULL); + ss->placed = TRUE; + } + } + + for (unplaced = i = 0; i < SHIPTYPES; i++) + unplaced += !plyship[i].placed; + } while + (unplaced); + + turn = rnd(2); + + mvprintw(HYBASE, HXBASE, + "To fire, move the cursor to your chosen aiming point "); + mvprintw(HYBASE+1, HXBASE, + "and strike any key other than a motion key. "); + mvprintw(HYBASE+2, HXBASE, + " "); + mvprintw(HYBASE+3, HXBASE, + " "); + mvprintw(HYBASE+4, HXBASE, + " "); + mvprintw(HYBASE+5, HXBASE, + " "); + + prompt(0, "Press any key to start..."); + getch(); +} + +static int +getcoord(int atcpu) +{ + int ny, nx, c; + + if (atcpu) + cgoto(cury,curx); + else + pgoto(cury, curx); + refresh(); + for (;;) + { + if (atcpu) + { + mvprintw(CYBASE + BDEPTH+1, CXBASE+11, "(%d, %c)", curx, 'A'+cury); + cgoto(cury, curx); + } + else + { + mvprintw(PYBASE + BDEPTH+1, PXBASE+11, "(%d, %c)", curx, 'A'+cury); + pgoto(cury, curx); + } + + switch(c = getch()) + { + case 'k': case '8': +#ifdef KEY_MIN + case KEY_UP: +#endif /* KEY_MIN */ + ny = cury+BDEPTH-1; nx = curx; + break; + case 'j': case '2': +#ifdef KEY_MIN + case KEY_DOWN: +#endif /* KEY_MIN */ + ny = cury+1; nx = curx; + break; + case 'h': case '4': +#ifdef KEY_MIN + case KEY_LEFT: +#endif /* KEY_MIN */ + ny = cury; nx = curx+BWIDTH-1; + break; + case 'l': case '6': +#ifdef KEY_MIN + case KEY_RIGHT: +#endif /* KEY_MIN */ + ny = cury; nx = curx+1; + break; + case 'y': case '7': +#ifdef KEY_MIN + case KEY_A1: +#endif /* KEY_MIN */ + ny = cury+BDEPTH-1; nx = curx+BWIDTH-1; + break; + case 'b': case '1': +#ifdef KEY_MIN + case KEY_C1: +#endif /* KEY_MIN */ + ny = cury+1; nx = curx+BWIDTH-1; + break; + case 'u': case '9': +#ifdef KEY_MIN + case KEY_A3: +#endif /* KEY_MIN */ + ny = cury+BDEPTH-1; nx = curx+1; + break; + case 'n': case '3': +#ifdef KEY_MIN + case KEY_C3: +#endif /* KEY_MIN */ + ny = cury+1; nx = curx+1; + break; + case FF: + nx = curx; ny = cury; + clearok(stdscr, TRUE); + refresh(); + break; + default: + if (atcpu) + mvaddstr(CYBASE + BDEPTH + 1, CXBASE + 11, " "); + else + mvaddstr(PYBASE + BDEPTH + 1, PXBASE + 11, " "); + return(c); + } + + curx = nx % BWIDTH; + cury = ny % BDEPTH; + } +} + +/* is this location on the selected zboard adjacent to a ship? */ +static int +collidecheck(int b, int y, int x) +{ + int collide; + + /* anything on the square */ + if ((collide = IS_SHIP(board[b][x][y])) != 0) + return(collide); + + /* anything on the neighbors */ + if (!closepack) { + int i; + + for (i = 0; i < 8; i++) { + int xend, yend; + + yend = y + yincr[i]; + xend = x + xincr[i]; + if (ONBOARD(xend, yend)) + collide += IS_SHIP(board[b][xend][yend]); + } + } + + return(collide); +} + +static bool +checkplace(int b, ship_t *ss, int vis) +{ + int l, xend, yend; + + /* first, check for board edges */ + xend = ss->x + (ss->length - 1) * xincr[ss->dir]; + yend = ss->y + (ss->length - 1) * yincr[ss->dir]; + if (!ONBOARD(xend, yend)) { + if (vis) { + switch(rnd(3)) { + case 0: + error("Ship is hanging from the edge of the world"); + break; + case 1: + error("Try fitting it on the board"); + break; + case 2: + error("Figure I won't find it if you put it there?"); + break; + } + } + return(0); + } + + for(l = 0; l < ss->length; ++l) { + if(collidecheck(b, ss->y+l*yincr[ss->dir], ss->x+l*xincr[ss->dir])) { + if (vis) { + switch(rnd(3)) { + case 0: + error("There's already a ship there"); + break; + case 1: + error("Collision alert! Aaaaaagh!"); + break; + case 2: + error("Er, Admiral, what about the other ship?"); + break; + } + } + return(FALSE); + } + } + return(TRUE); +} + +static int +awinna(void) +{ + int i, j; + ship_t *ss; + + for(i=0; i<2; ++i) + { + ss = (i) ? cpuship : plyship; + for(j=0; j < SHIPTYPES; ++j, ++ss) + if(ss->length > ss->hits) + break; + if (j == SHIPTYPES) + return(OTHER); + } + return(-1); +} + +/* a hit on the targeted ship */ +static ship_t * +hitship(int x, int y) +{ + ship_t *sb, *ss; + char sym; + int oldx, oldy; + + getyx(stdscr, oldy, oldx); + sb = (turn) ? plyship : cpuship; + if(!(sym = board[OTHER][x][y])) + return(NULL); + for(ss = sb; ss < sb + SHIPTYPES; ++ss) + if(ss->symbol == sym) + { + if (++ss->hits < ss->length) { /* still afloat? */ + return(NULL); + } else { /* sunk */ + int i, j; + + if (!closepack) { + for (j = -1; j <= 1; j++) { + int bx = ss->x + j * xincr[(ss->dir + 2) % 8]; + int by = ss->y + j * yincr[(ss->dir + 2) % 8]; + + for (i = -1; i <= ss->length; ++i) { + int cx, cy; + + cx = bx + i * xincr[ss->dir]; + cy = by + i * yincr[ss->dir]; + if (ONBOARD(cx, cy)) { + hits[turn][cx][cy] = MARK_MISS; + if (turn % 2 == PLAYER) { + cgoto(cy, cx); +#ifdef A_COLOR + if (has_colors()) + attron(COLOR_PAIR(COLOR_GREEN)); +#endif /* A_COLOR */ + + addch(MARK_MISS); +#ifdef A_COLOR + attrset(0); +#endif /* A_COLOR */ + } + } + } + } + } + + for (i = 0; i < ss->length; ++i) + { + int dx = ss->x + i * xincr[ss->dir]; + int dy = ss->y + i * yincr[ss->dir]; + + hits[turn][dx][dy] = ss->symbol; + if (turn % 2 == PLAYER) + { + cgoto(dy, dx); + addch(ss->symbol); + } + } + + move(oldy, oldx); + return(ss); + } + } + move(oldy, oldx); + return(NULL); +} + +static int +plyturn(void) +{ + ship_t *ss; + bool hit; + char const *m; + + m = NULL; + prompt(1, "Where do you want to shoot? "); + for (;;) + { + getcoord(COMPUTER); + if (hits[PLAYER][curx][cury]) + { + prompt(1, "You shelled this spot already! Try again."); + beep(); + } + else + break; + } + hit = IS_SHIP(board[COMPUTER][curx][cury]); + hits[PLAYER][curx][cury] = hit ? MARK_HIT : MARK_MISS; + cgoto(cury, curx); +#ifdef A_COLOR + if (has_colors()) { + if (hit) + attron(COLOR_PAIR(COLOR_RED)); + else + attron(COLOR_PAIR(COLOR_GREEN)); + } +#endif /* A_COLOR */ + addch((chtype)hits[PLAYER][curx][cury]); +#ifdef A_COLOR + attrset(0); +#endif /* A_COLOR */ + + prompt(1, "You %s.", hit ? "scored a hit" : "missed"); + if(hit && (ss = hitship(curx, cury))) + { + switch(rnd(5)) + { + case 0: + m = " You sank my %s!"; + break; + case 1: + m = " I have this sinking feeling about my %s...."; + break; + case 2: + m = " My %s has gone to Davy Jones's locker!"; + break; + case 3: + m = " Glub, glub -- my %s is headed for the bottom!"; + break; + case 4: + m = " You'll pick up survivors from my %s, I hope...!"; + break; + } + printw(m, ss->name); + beep(); + return(awinna() == -1); + } + return(hit); +} + +static int +sgetc(const char *s) +{ + const char *s1; + int ch; + + refresh(); + for(;;) { + ch = getch(); + if (islower(ch)) + ch = toupper(ch); + + if (ch == CTRLC) + uninitgame(); + + for (s1=s; *s1 && ch != *s1; ++s1) + continue; + + if (*s1) { + addch((chtype)ch); + refresh(); + return(ch); + } + } +} + +/* random-fire routine -- implements simple diagonal-striping strategy */ +static void +randomfire(int *px, int *py) +{ + static int turncount = 0; + static int srchstep = BEGINSTEP; + static int huntoffs; /* Offset on search strategy */ + int ypossible[BWIDTH * BDEPTH], xpossible[BWIDTH * BDEPTH], nposs; + int ypreferred[BWIDTH * BDEPTH], xpreferred[BWIDTH * BDEPTH], npref; + int x, y, i; + + if (turncount++ == 0) + huntoffs = rnd(srchstep); + + /* first, list all possible moves */ + nposs = npref = 0; + for (x = 0; x < BWIDTH; x++) + for (y = 0; y < BDEPTH; y++) + if (!hits[COMPUTER][x][y]) + { + xpossible[nposs] = x; + ypossible[nposs] = y; + nposs++; + if (((x+huntoffs) % srchstep) != (y % srchstep)) + { + xpreferred[npref] = x; + ypreferred[npref] = y; + npref++; + } + } + + if (npref) + { + i = rnd(npref); + + *px = xpreferred[i]; + *py = ypreferred[i]; + } + else if (nposs) + { + i = rnd(nposs); + + *px = xpossible[i]; + *py = ypossible[i]; + + if (srchstep > 1) + --srchstep; + } + else + { + error("No moves possible?? Help!"); + exit(1); + /*NOTREACHED*/ + } +} + +#define S_MISS 0 +#define S_HIT 1 +#define S_SUNK -1 + +/* fire away at given location */ +static bool +cpufire(int x, int y) +{ + bool hit, sunk; + ship_t *ss; + + ss = NULL; + hits[COMPUTER][x][y] = (hit = (board[PLAYER][x][y])) ? MARK_HIT : MARK_MISS; + mvprintw(PROMPTLINE, 0, + "I shoot at %c%d. I %s!", y + 'A', x, hit ? "hit" : "miss"); + ss = hitship(x, y); + sunk = hit && ss; + if (sunk) + printw(" I've sunk your %s", ss->name); + clrtoeol(); + + pgoto(y, x); +#ifdef A_COLOR + if (has_colors()) { + if (hit) + attron(COLOR_PAIR(COLOR_RED)); + else + attron(COLOR_PAIR(COLOR_GREEN)); + } +#endif /* A_COLOR */ + addch((chtype)(hit ? SHOWHIT : SHOWSPLASH)); +#ifdef A_COLOR + attrset(0); +#endif /* A_COLOR */ + + return(hit ? (sunk ? S_SUNK : S_HIT) : S_MISS); +} + +/* + * This code implements a fairly irregular FSM, so please forgive the rampant + * unstructuredness below. The five labels are states which need to be held + * between computer turns. + */ +static bool +cputurn(void) +{ +#define POSSIBLE(x, y) (ONBOARD(x, y) && !hits[COMPUTER][x][y]) +#define RANDOM_FIRE 0 +#define RANDOM_HIT 1 +#define HUNT_DIRECT 2 +#define FIRST_PASS 3 +#define REVERSE_JUMP 4 +#define SECOND_PASS 5 + static int next = RANDOM_FIRE; + static bool used[4]; + static ship_t ts; + int navail, x, y, d, n, hit = S_MISS; + + switch(next) + { + case RANDOM_FIRE: /* last shot was random and missed */ + refire: + randomfire(&x, &y); + if (!(hit = cpufire(x, y))) + next = RANDOM_FIRE; + else + { + ts.x = x; ts.y = y; + ts.hits = 1; + next = (hit == S_SUNK) ? RANDOM_FIRE : RANDOM_HIT; + } + break; + + case RANDOM_HIT: /* last shot was random and hit */ + used[E/2] = used[S/2] = used[W/2] = used[N/2] = FALSE; + /* FALLTHROUGH */ + + case HUNT_DIRECT: /* last shot hit, we're looking for ship's long axis */ + for (d = navail = 0; d < 4; d++) + { + x = ts.x + xincr[d*2]; y = ts.y + yincr[d*2]; + if (!used[d] && POSSIBLE(x, y)) + navail++; + else + used[d] = TRUE; + } + if (navail == 0) /* no valid places for shots adjacent... */ + goto refire; /* ...so we must random-fire */ + else + { + for (d = 0, n = rnd(navail) + 1; n; n--) + while (used[d]) + d++; + + assert(d <= 4); + + used[d] = FALSE; + x = ts.x + xincr[d*2]; + y = ts.y + yincr[d*2]; + + assert(POSSIBLE(x, y)); + + if (!(hit = cpufire(x, y))) + next = HUNT_DIRECT; + else + { + ts.x = x; ts.y = y; ts.dir = d*2; ts.hits++; + next = (hit == S_SUNK) ? RANDOM_FIRE : FIRST_PASS; + } + } + break; + + case FIRST_PASS: /* we have a start and a direction now */ + x = ts.x + xincr[ts.dir]; + y = ts.y + yincr[ts.dir]; + if (POSSIBLE(x, y) && (hit = cpufire(x, y))) + { + ts.x = x; ts.y = y; ts.hits++; + next = (hit == S_SUNK) ? RANDOM_FIRE : FIRST_PASS; + } + else + next = REVERSE_JUMP; + break; + + case REVERSE_JUMP: /* nail down the ship's other end */ + d = ts.dir + 4; + x = ts.x + ts.hits * xincr[d]; + y = ts.y + ts.hits * yincr[d]; + if (POSSIBLE(x, y) && (hit = cpufire(x, y))) + { + ts.x = x; ts.y = y; ts.dir = d; ts.hits++; + next = (hit == S_SUNK) ? RANDOM_FIRE : SECOND_PASS; + } + else + next = RANDOM_FIRE; + break; + + case SECOND_PASS: /* kill squares not caught on first pass */ + x = ts.x + xincr[ts.dir]; + y = ts.y + yincr[ts.dir]; + if (POSSIBLE(x, y) && (hit = cpufire(x, y))) + { + ts.x = x; ts.y = y; ts.hits++; + next = (hit == S_SUNK) ? RANDOM_FIRE: SECOND_PASS; + break; + } + else + next = RANDOM_FIRE; + break; + } + + /* check for continuation and/or winner */ + if (salvo) + { + refresh(); + sleep(1); + } + if (awinna() != -1) + return(FALSE); + +#ifdef DEBUG + mvprintw(PROMPTLINE + 2, 0, + "New state %d, x=%d, y=%d, d=%d", + next, x, y, d); +#endif /* DEBUG */ + return(hit); +} + +int +playagain(void) +{ + int j; + ship_t *ss; + + for (ss = cpuship; ss < cpuship + SHIPTYPES; ss++) { + for(j = 0; j < ss->length; j++) { + cgoto(ss->y + j * yincr[ss->dir], ss->x + j * xincr[ss->dir]); + addch((chtype)ss->symbol); + } + } + + if(awinna()) { + ++cpuwon; + } else { + ++plywon; + } + + j = 18 + strlen(name); + if(plywon >= 10) { + ++j; + } else if(cpuwon >= 10) { + ++j; + } + + mvprintw(1,(COLWIDTH-j)/2, + "%s: %d Computer: %d",name,plywon,cpuwon); + + prompt(2, (awinna()) ? "Want to be humiliated again, %s [yn]? " + : "Going to give me a chance for revenge, %s [yn]? ",name); + return(sgetc("YN") == 'Y'); +} + +static void +usage(void) +{ + fprintf(stderr, "Usage: battle [-s | -b] [-c]\n"); + fprintf(stderr, "\tWhere the options are:\n"); + fprintf(stderr, "\t-s : salvo - One shot per ship in play\n"); + fprintf(stderr, "\t-b : blitz - Fire until you miss\n"); + fprintf(stderr, "\t-c : closepack - Ships may be adjacent\n"); + fprintf(stderr, "Blitz and Salvo are mutually exclusive\n"); + exit(1); +} + +static int +scount(int who) +{ + int i, shots; + ship_t *sp; + + if (who) + sp = cpuship; /* count cpu shots */ + else + sp = plyship; /* count player shots */ + + for (i=0, shots = 0; i < SHIPTYPES; i++, sp++) + { + if (sp->hits >= sp->length) + continue; /* dead ship */ + else + shots++; + } + return(shots); +} + +int +main(int argc, char **argv) +{ + int ch; + + /* revoke */ + setgid(getgid()); + + while ((ch = getopt(argc, argv, "bsc")) != -1) { + switch (ch) { + case 'b': + blitz = 1; + break; + case 's': + salvo = 1; + break; + case 'c': + closepack = 1; + break; + case '?': + default: + usage(); + } + } + argc -= optind; + argv += optind; + + if (blitz && salvo) + usage(); + + intro(); + + do { + initgame(); + while(awinna() == -1) { + if (blitz) { + while(turn ? cputurn() : plyturn()) + continue; + } else if (salvo) { + int i; + + i = scount(turn); + while (i--) { + if (turn) { + if (cputurn() && awinna() != -1) + i = 0; + } else { + if (plyturn() && awinna() != -1) + i = 0; + } + } + } else { /* Normal game */ + if(turn) + cputurn(); + else + plyturn(); + } + turn = OTHER; + } + } while (playagain()); + + uninitgame(); + exit(0); +} diff --git a/cgram/Makefile b/cgram/Makefile new file mode 100644 index 0000000..222a745 --- /dev/null +++ b/cgram/Makefile @@ -0,0 +1,11 @@ +# $NetBSD: Makefile,v 1.1 2013/08/04 05:42:47 dholland Exp $ + +PROG=cgram +DPADD=${LIBCURSES} ${LIBTERMINFO} +# 20150209 bkw: remove -lterminfo +LDADD=-lcurses +SRCS=cgram.c +MAN=cgram.6 +HIDEGAME=hidegame + +.include <bsd.prog.mk> diff --git a/cgram/cgram.6 b/cgram/cgram.6 new file mode 100644 index 0000000..9f31580 --- /dev/null +++ b/cgram/cgram.6 @@ -0,0 +1,65 @@ +.\" $NetBSD: cgram.6,v 1.2 2013/08/04 07:55:09 wiz Exp $ +.\" +.\" Copyright (c) 2004, 2013 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by David A. Holland. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd August 3, 2013 +.Dt CGRAM 6 +.Os +.Sh NAME +.Nm cgram +.Nd solve Sunday-paper cryptograms +.Sh SYNOPSIS +.Nm +.Sh DESCRIPTION +.Nm +is a curses-based widget for solving Sunday-paper-type cryptograms +based on substitution ciphers. +A random cleartext is chosen using +.Xr fortune 6 +and a random substitution key is generated. +.Pp +The ciphertext is displayed. +Typing a letter changes the key so that the letter under the cursor +maps to the newly typed letter, and updates the display accordingly. +Use Emacs-type cursor commands to move around. +Enter a tilde +.Pq ~ +to quit. +Press asterisk +.Pq * +to enter an easier mode where correct letters are displayed in +boldface. +.Sh SEE ALSO +.Xr caesar 6 +.Sh HISTORY +.Nm +was written circa 2004. +It was imported into +.Nx +in 2013 and first appeared in +.Nx 7.0 . diff --git a/cgram/cgram.c b/cgram/cgram.c new file mode 100644 index 0000000..76ea55f --- /dev/null +++ b/cgram/cgram.c @@ -0,0 +1,344 @@ +/*- + * Copyright (c) 2013 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by David A. Holland. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include <time.h> +#include <err.h> +#include <assert.h> +#include <curses.h> +#include "pathnames.h" + +//////////////////////////////////////////////////////////// + +static char *xstrdup(const char *s) { + char *ret; + + ret = malloc(strlen(s) + 1); + if (ret == NULL) { + errx(1, "Out of memory"); + } + strcpy(ret, s); + return ret; +} + +//////////////////////////////////////////////////////////// + +struct stringarray { + char **v; + int num; +}; + +static void stringarray_init(struct stringarray *a) { + a->v = NULL; + a->num = 0; +} + +static void stringarray_cleanup(struct stringarray *a) { + free(a->v); +} + +static void stringarray_add(struct stringarray *a, const char *s) { + a->v = realloc(a->v, (a->num + 1) * sizeof(a->v[0])); + if (a->v == NULL) { + errx(1, "Out of memory"); + } + a->v[a->num] = xstrdup(s); + a->num++; +} + +//////////////////////////////////////////////////////////// + +static struct stringarray lines; +static struct stringarray sollines; +static bool hinting; +static int scrolldown; +static unsigned curx; +static int cury; + +static void readquote(void) { + FILE *f = popen(_PATH_FORTUNE, "r"); + if (!f) { + err(1, "%s", _PATH_FORTUNE); + } + + char buf[128], buf2[8*sizeof(buf)]; + while (fgets(buf, sizeof(buf), f)) { + char *s = strrchr(buf, '\n'); + assert(s); + assert(strlen(s)==1); + *s = 0; + + int i,j; + for (i=j=0; buf[i]; i++) { + if (buf[i]=='\t') { + buf2[j++] = ' '; + while (j%8) buf2[j++] = ' '; + } + else if (buf[i]=='\b') { + if (j>0) j--; + } + else { + buf2[j++] = buf[i]; + } + } + buf2[j] = 0; + + stringarray_add(&lines, buf2); + stringarray_add(&sollines, buf2); + } + + pclose(f); +} + +static void encode(void) { + int used[26]; + for (int i=0; i<26; i++) used[i] = 0; + + int key[26]; + int keypos=0; + while (keypos < 26) { + int c = random()%26; + if (used[c]) continue; + key[keypos++] = c; + used[c] = 1; + } + + for (int y=0; y<lines.num; y++) { + for (unsigned x=0; lines.v[y][x]; x++) { + if (islower((unsigned char)lines.v[y][x])) { + int q = lines.v[y][x]-'a'; + lines.v[y][x] = 'a'+key[q]; + } + if (isupper((unsigned char)lines.v[y][x])) { + int q = lines.v[y][x]-'A'; + lines.v[y][x] = 'A'+key[q]; + } + } + } +} + +static int substitute(int ch) { + assert(cury>=0 && cury<lines.num); + if (curx >= strlen(lines.v[cury])) { + beep(); + return -1; + } + + int och = lines.v[cury][curx]; + if (!isalpha((unsigned char)och)) { + beep(); + return -1; + } + + int loch = tolower((unsigned char)och); + int uoch = toupper((unsigned char)och); + int lch = tolower((unsigned char)ch); + int uch = toupper((unsigned char)ch); + + for (int y=0; y<lines.num; y++) { + for (unsigned x=0; lines.v[y][x]; x++) { + if (lines.v[y][x]==loch) { + lines.v[y][x] = lch; + } + else if (lines.v[y][x]==uoch) { + lines.v[y][x] = uch; + } + else if (lines.v[y][x]==lch) { + lines.v[y][x] = loch; + } + else if (lines.v[y][x]==uch) { + lines.v[y][x] = uoch; + } + } + } + return 0; +} + +//////////////////////////////////////////////////////////// + +static void redraw(void) { + erase(); + bool won = true; + for (int i=0; i<LINES-1; i++) { + move(i, 0); + int ln = i+scrolldown; + if (ln < lines.num) { + for (unsigned j=0; lines.v[i][j]; j++) { + int ch = lines.v[i][j]; + if (ch != sollines.v[i][j] && isalpha((unsigned char)ch)) { + won = false; + } + bool bold=false; + if (hinting && ch==sollines.v[i][j] && + isalpha((unsigned char)ch)) { + bold = true; + attron(A_BOLD); + } + addch(lines.v[i][j]); + if (bold) { + attroff(A_BOLD); + } + } + } + clrtoeol(); + } + + move(LINES-1, 0); + if (won) { + addstr("*solved* "); + } + addstr("~ to quit, * to cheat, ^pnfb to move"); + + move(LINES-1, 0); + + move(cury-scrolldown, curx); + + refresh(); +} + +static void opencurses(void) { + initscr(); + cbreak(); + noecho(); +} + +static void closecurses(void) { + endwin(); +} + +//////////////////////////////////////////////////////////// + +static void loop(void) { + bool done=false; + while (!done) { + redraw(); + int ch = getch(); + switch (ch) { + case 1: /* ^A */ + curx=0; + break; + case 2: /* ^B */ + if (curx > 0) { + curx--; + } + else if (cury > 0) { + cury--; + curx = strlen(lines.v[cury]); + } + break; + case 5: /* ^E */ + curx = strlen(lines.v[cury]); + break; + case 6: /* ^F */ + if (curx < strlen(lines.v[cury])) { + curx++; + } + else if (cury < lines.num - 1) { + cury++; + curx = 0; + } + break; + case 12: /* ^L */ + clear(); + break; + case 14: /* ^N */ + if (cury < lines.num-1) { + cury++; + } + if (curx > strlen(lines.v[cury])) { + curx = strlen(lines.v[cury]); + } + if (scrolldown < cury - (LINES-2)) { + scrolldown = cury - (LINES-2); + } + break; + case 16: /* ^P */ + if (cury > 0) { + cury--; + } + if (curx > strlen(lines.v[cury])) { + curx = strlen(lines.v[cury]); + } + if (scrolldown > cury) { + scrolldown = cury; + } + break; + case '*': + hinting = !hinting; + break; + case '~': + done = true; + break; + default: + if (isalpha(ch)) { + if (!substitute(ch)) { + if (curx < strlen(lines.v[cury])) { + curx++; + } + if (curx==strlen(lines.v[cury]) && cury < lines.num-1) { + curx=0; + cury++; + } + } + } + else if (curx < strlen(lines.v[cury]) && ch==lines.v[cury][curx]) { + curx++; + if (curx==strlen(lines.v[cury]) && cury < lines.num-1) { + curx=0; + cury++; + } + } + else { + beep(); + } + break; + } + } +} + +//////////////////////////////////////////////////////////// + +int main(void) { + stringarray_init(&lines); + stringarray_init(&sollines); + srandom(time(NULL)); + readquote(); + encode(); + opencurses(); + + loop(); + + closecurses(); + stringarray_cleanup(&sollines); + stringarray_cleanup(&lines); + return 0; +} diff --git a/cgram/pathnames.h b/cgram/pathnames.h new file mode 100644 index 0000000..51da95c --- /dev/null +++ b/cgram/pathnames.h @@ -0,0 +1,30 @@ +/*- + * Copyright (c) 2013 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by David A. Holland. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#define _PATH_FORTUNE "/usr/games/fortune" diff --git a/ching/Makefile b/ching/Makefile new file mode 100644 index 0000000..ab7834f --- /dev/null +++ b/ching/Makefile @@ -0,0 +1,5 @@ +# $NetBSD: Makefile,v 1.1 2005/06/30 13:30:33 perry Exp $ + +SUBDIR= ching castching printching + +.include <bsd.subdir.mk> diff --git a/ching/Makefile.inc b/ching/Makefile.inc new file mode 100644 index 0000000..b4674a6 --- /dev/null +++ b/ching/Makefile.inc @@ -0,0 +1,5 @@ +# $NetBSD: Makefile.inc,v 1.3 2013/08/11 03:27:02 dholland Exp $ + +CPPFLAGS+=-I${.CURDIR}/../include +BINDIR?=/usr/games +WARNS?=5 diff --git a/ching/castching/Makefile b/ching/castching/Makefile new file mode 100644 index 0000000..dd84fc9 --- /dev/null +++ b/ching/castching/Makefile @@ -0,0 +1,7 @@ +# $NetBSD: Makefile,v 1.1 2005/06/30 13:30:33 perry Exp $ + +PROG= castching +NOMAN= # defined +BINDIR= /usr/libexec/ching + +.include <bsd.prog.mk> diff --git a/ching/castching/castching.c b/ching/castching/castching.c new file mode 100644 index 0000000..351037a --- /dev/null +++ b/ching/castching/castching.c @@ -0,0 +1,135 @@ +/* $NetBSD: castching.c,v 1.3 2009/08/12 05:40:03 dholland Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Guy Harris. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1988, 1993\ + The Regents of the University of California. All rights reserved."); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)ching.cno.c 8.1 (Berkeley) 5/31/93"; +#else +__RCSID("$NetBSD: castching.c,v 1.3 2009/08/12 05:40:03 dholland Exp $"); +#endif +#endif /* not lint */ + +/* + * castching - Read a question, cast a change, and output the line + * values to the standard output for processing by "printching". + */ +#include <stdio.h> +#include <stdlib.h> +#include <time.h> +#include <unistd.h> +#include "ching.h" + +static time_t now; /* current time */ + +static unsigned seed; /* seed for random number generator */ + +static int getquest(void); +static unsigned getrand(void); +static unsigned getrnum(void); +static char *change(void); + +static char string[6+1]; /* where the actual change string is put */ + +static int table[2][2][2] = { + { { OYIN, YYANG,}, { YYANG, YYIN,} }, + { { YYANG, YYIN,}, { YYIN, OYANG,} }, +}; + +/*ARGSUSED*/ +int +main(int argc, char **argv) +{ + time(&now); + /* randomize */ + seed = (int)now + getquest() + getgid() + getuid() + getpid(); + printf("%s\n", change()); + exit(0); +} + +/* + * Hash the question by adding all the characters together. + */ +static int +getquest(void) +{ + int result; + int c; + + result = 0; + while ((c = getchar()) != EOF) + result += c; + return(result); +} + +/* + * Get a set of six lines making up a change. + */ +static char * +change(void) +{ + int i; + + for (i = 0; i < 6; i++) + string[i] = table[getrnum()&01][getrnum()&01][getrnum()&01] + '0'; + string[i] = '\0'; + return(string); +} + +/* + * Get a number more random than what getrand() gives. + */ +static unsigned +getrnum(void) +{ + return((getrand())>>(getrand()%17)); +} + +/* + * Get a random number. + */ +static unsigned +getrand(void) +{ + return(seed = (seed*13077) + 6925); +} diff --git a/ching/ching/Makefile b/ching/ching/Makefile new file mode 100644 index 0000000..85559c1 --- /dev/null +++ b/ching/ching/Makefile @@ -0,0 +1,13 @@ +# $NetBSD: Makefile,v 1.2 2008/10/30 21:37:55 mrg Exp $ + +SCRIPTS=ching.sh +MAN= ching.6 + +.include <bsd.own.mk> + +.if ${MKSHARE} != "no" +FILES= hexagrams macros +FILESDIR=/usr/share/games/ching +.endif + +.include <bsd.prog.mk> diff --git a/ching/ching/ching.6 b/ching/ching/ching.6 new file mode 100644 index 0000000..a1cd662 --- /dev/null +++ b/ching/ching/ching.6 @@ -0,0 +1,154 @@ +.\" $NetBSD: ching.6,v 1.5 2005/07/05 08:48:47 wiz Exp $ +.\" +.\" Copyright (c) Caldera International Inc. 2001-2002. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" +.\" 1. Redistributions of source code and documentation must retain the +.\" above copyright notice, this list of conditions and the following +.\" disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed or owned by Caldera +.\" International, Inc. +.\" 4. Neither the name of Caldera International, Inc. nor the names of +.\" other contributors may be used to endorse or promote products +.\" derived from this software without specific prior written permission. +.\" +.\" USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA +.\" INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +.\" DISCLAIMED. IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE +.\" FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +.\" BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +.\" WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +.\" OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +.\" IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" +.\" Copyright (c) 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" +.\" @(#)ching.6 8.1 (Berkeley) 5/31/93 +.\" +.Dd May 31, 1993 +.Dt CHING 6 +.Os +.Sh NAME +.Nm ching +.Nd the book of changes and other cookies +.Sh SYNOPSIS +.Nm +.Op hexagram +.Sh DESCRIPTION +The +.Em I Ching +or +.Em Book of Changes +is an ancient Chinese oracle that has been in use for centuries +as a source of wisdom and advice. +.Pp +The text of the +.Em oracle +(as it is sometimes known) consists of sixty-four +.Em hexagrams , +each symbolized by a particular arrangement of six straight (\-\-\-) +and broken (\-\ \-) lines. These lines have values ranging +from six through nine, with the even values indicating the broken lines. +.Pp +Each hexagram consists of two major sections. The +.Sy Judgement +relates specifically to the matter at hand +.Po e.g. , +.Dq \&It furthers one to have somewhere to go. +.Pc +while the +.Sy Image +describes the general attributes of the hexagram and how they apply +to one's own life +.Pq Dq Thus the superior man makes himself strong and untiring. +.Pp +When any of the lines have the values six or nine, they are moving +lines; for each there is an appended judgement which becomes +significant. +Furthermore, the moving lines are inherently unstable +and change into their opposites; a second hexagram (and thus an +additional judgement) is formed. +.Pp +Normally, one consults the oracle by fixing the desired question +firmly in mind and then casting a set of changes (lines) +using yarrow\-stalks or tossed coins. The resulting hexagram +will be the answer to the question. +.Pp +Using an algorithm suggested by S. C. Johnson, the +.Ux +.Em oracle +simply reads a question from the standard input (up to an EOF) and +hashes the individual characters in combination with the time of +day, process id and any other magic numbers which happen to be +lying around the system. +The resulting value is used as the seed of a random +number generator which drives a simulated coin\-toss divination. +The answer is then piped through +.Xr nroff 1 +for formatting and will appear on the standard output. +.Pp +For those who wish to remain steadfast in the old traditions, the +oracle will also accept the results of a personal divination using, +for example, coins. +To do this, cast the change and then type the +resulting line values as an argument. +.Pp +The impatient modern may prefer to settle for Chinese cookies; try +.Xr fortune 6 . +.Sh DIAGNOSTICS +The great prince issues commands, +.Pp +Founds states, vests families with fiefs. +.Pp +Inferior people should not be employed. +.Sh SEE ALSO +It furthers one to see the great man. +.Sh BUGS +Waiting in the mud +.Pp +Brings about the arrival of the enemy. +.Pp +If one is not extremely careful, +.Pp +Somebody may come up from behind and strike him. +.Pp +Misfortune. diff --git a/ching/ching/ching.sh b/ching/ching/ching.sh new file mode 100644 index 0000000..45a0bf5 --- /dev/null +++ b/ching/ching/ching.sh @@ -0,0 +1,81 @@ +#!/bin/sh +# +# $NetBSD: ching.sh,v 1.1 2005/06/30 13:30:33 perry Exp $ +# +# Copyright (c) Caldera International Inc. 2001-2002. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code and documentation must retain the +# above copyright notice, this list of conditions and the following +# disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed or owned by Caldera +# International, Inc. +# 4. Neither the name of Caldera International, Inc. nor the names of +# other contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA +# INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE +# FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +# IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# +# Copyright (c) 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)ching.sh 8.1 (Berkeley) 5/31/93 +# + +SHARE=/usr/share/games/ching +PROGS=/usr/libexec/ching + +case $1 in + [6-9]*) HEXAGRAM=$1; shift;; +esac + +if [ -z "$HEXAGRAM" ]; then + HEXAGRAM=$($PROGS/castching) + echo +fi + +$PROGS/printching $HEXAGRAM | nroff $SHARE/macros - | ${PAGER-more} diff --git a/ching/ching/hexagrams b/ching/ching/hexagrams new file mode 100644 index 0000000..3d55e99 --- /dev/null +++ b/ching/ching/hexagrams @@ -0,0 +1,2337 @@ +.\" $NetBSD: hexagrams,v 1.1 2005/06/30 13:30:33 perry Exp $ +.\" +.\" Copyright (c) Caldera International Inc. 2001-2002. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" +.\" 1. Redistributions of source code and documentation must retain the +.\" above copyright notice, this list of conditions and the following +.\" disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed or owned by Caldera +.\" International, Inc. +.\" 4. Neither the name of Caldera International, Inc. nor the names of +.\" other contributors may be used to endorse or promote products +.\" derived from this software without specific prior written permission. +.\" +.\" USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA +.\" INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +.\" DISCLAIMED. IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE +.\" FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +.\" BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +.\" WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +.\" OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +.\" IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" +.\" Copyright (c) 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.H 1 "Ch\'ien" "The Creative" +.X 1 1 +.J +The Creative works sublime success, +Furthering through perseverance. +.I +The movement of heaven is full of power. +Thus the superior man makes himself strong and untiring. +.L 1 9 +Hidden dragon. Do not act. +.L 2 9 +Dragon appearing in the field. +It furthers one to see the great man. +.L 3 9 +All day long the superior man is creatively active. +At nightfall his mind is still beset with cares. +Danger. No blame. +.L 4 9 +Wavering flight over the depths. +No blame. +.L 5 9 G +Flying dragon in the heavens. +It furthers one to see the great man. +.L 6 9 +Arrogant dragon will have cause to repent. +.LA 9 +There appears a flight of dragons without heads. +Good fortune. +.H 2 "K\'un" "The Receptive" +.X 8 8 +.J +The Receptive brings about sublime success, +Furthering through the perseverance of a mare. +If the superior man undertakes something and tries to lead, +He goes astray; +But if he follows, he finds guidance. +It is favorable to find friends in the west and south, +To forego friends in the east and north. +Quiet perseverance brings good fortune. +.I +The earth's condition is receptive devotion. +Thus the superior man who has breadth of character +Carries the outer world. +.L 1 6 +When there is hoarfrost underfoot, +Solid ice is not far off. +.L 2 6 G +Straight, square, great. +Without purpose, +Yet nothing remains unfurthered. +.L 3 6 +Hidden lines. +One is able to remain persevering. +If by chance you are in the service of a king, +Seek not works, but bring to completion. +.L 4 6 +A tied-up sack. No blame, no praise. +.L 5 6 +A yellow lower garment brings supreme good fortune. +.L 6 6 +Dragons fight in the meadow. +Their blood is black and yellow. +.LA 6 +Lasting perseverance furthers. +.H 3 "Chun" "Difficulty at the Beginning" +.X 6 7 +.J +Difficulty at the Beginning works supreme success, +Furthering through perseverance. +Nothing should be undertaken. +It furthers one to appoint helpers. +.I +Clouds and thunder: +The image of Difficulty at the Beginning. +Thus the superior man +Brings order out of confusion. +.L 1 9 G +Hesitation and hindrance. +It furthers one to remain persevering. +It furthers one to appoint helpers. +.L 2 6 +Difficulties pile up. +Horse and wagon part. +He is not a robber; +He wants to woo when the time comes. +The maiden is chaste, +She does not pledge herself. +Ten years\(emthen she pledges herself. +.L 3 6 +Whoever hunts deer without the forester +Only loses his way in the forest. +The superior man understands the signs of the time +And prefers to desist. +To go on brings humiliation. +.L 4 6 +Horse and wagon part. +Strive for union. +To go brings good fortune. +Everything acts to further. +.L 5 9 G +Difficulties in blessing. +A little perseverance brings good fortune. +Great perseverance brings misfortune. +.L 6 6 +Horse and wagon part. +Bloody tears flow. +.H 4 "M\o'^e'ng" "Youthful Folly" +.X 4 6 +.J +Youthful Folly has success. +It is not I who seek the young fool; +The young fool seeks me. +At the first oracle I inform him. +If he asks two or three times, it is importunity. +If he importunes, I give him no information. +Perseverance furthers. +.I +A spring wells up at the foot of the mountain: +The image of Youth. +Thus the superior man fosters his character +By thoroughness in all that he does. +.L 1 6 +To make a fool develop +It furthers one to apply discipline. +The fetters should be removed. +To go on in this way brings humiliation. +.L 2 9 G +To bear with fools in kindliness brings good fortune. +To know how to take women +Brings good fortune. +The son is capable of taking charge of the household. +.L 3 6 +Take not a maiden who, when she sees a man of bronze, +Loses possession of herself. +Nothing furthers. +.L 4 6 +Entangled folly brings humiliation. +.L 5 6 G +Childlike folly brings good fortune. +.L 6 9 +In punishing folly +It does not further one +To commit transgressions. +The only thing that furthers +Is to prevent transgressions. +.H 5 "Hsu" "Waiting (Nourishment)" +.X 6 1 +.J +Waiting. If you are sincere, +You have light and success. +Perseverance brings good fortune. +It furthers one to cross the great water. +.I +Clouds rise up to heaven: +The image of Waiting. +Thus the superior man eats and drinks, +Is joyous and of good cheer. +.L 1 9 +Waiting in the meadow. +It furthers one to abide in what endures. +No blame. +.L 2 9 +Waiting on the sand. +There is some gossip. +The end brings good fortune. +.L 3 9 +Waiting in the mud +Brings about the arrival of the enemy. +.L 4 6 +Waiting in blood. +Get out of the pit. +.L 5 9 G +Waiting at meat and drink. +Perseverance brings good fortune. +.L 6 6 +One falls into the pit. +Three uninvited guests arrive. +Honor them, and in the end there will be good fortune. +.H 6 "Sung" "Conflict" +.X 1 6 +.J +Conflict. You are sincere +And are being obstructed. +A cautious halt halfway brings good fortune. +Going through to the end brings misfortune. +It furthers one to see the great man. +It does not further one to cross the great water. +.I +Heaven and water go their opposite ways: +The image of Conflict. +Thus in all his transactions the superior man +Carefully considers the beginning. +.L 1 6 +If one does not perpetuate the affair, +There is a little gossip. +In the end, good fortune comes. +.L 2 9 +One cannot engage in conflict; +One returns home, gives way. +The people of his town, +Three hundred households, +Remain free of guilt. +.L 3 6 +To nourish oneself on ancient virtue induces perseverance. +Danger. In the end, good fortune comes. +If by chance you are in the service of a king, +Seek not works. +.L 4 9 +One cannot engage in conflict. +One turns back and submits to fate, +Changes one's attitude, +And finds peace in perseverance. +Good fortune. +.L 5 9 G +To contend before him +Brings supreme good fortune. +.L 6 9 +Even if by chance a leather belt is bestowed on one, +By the end of a morning +It will have been snatched away three times. +.H 7 "Shih" "The Army" +.X 8 6 +.J +The Army. The army needs perseverance +And a strong man. +Good fortune without blame. +.I +In the middle of the earth is water: +The image of the Army. +Thus the superior man increases his masses +By generosity toward the people. +.L 1 6 +An army must set forth in proper order. +If the order is not good, misfortune threatens. +.L 2 9 G +In the midst of the army. +Good fortune. No blame. +The king bestows a triple decoration. +.L 3 6 +Perchance the army carries corpses in the wagon. +Misfortune. +.L 4 6 +The army retreats. No blame. +.L 5 6 G +There is game in the field. +It furthers one to catch it. +Without blame. +Let the eldest lead the army. +The younger transports corpses; +Then perseverance brings misfortune. +.L 6 6 +The great prince issues commands, +Founds states, vests families with fiefs. +Inferior people should not be employed. +.H 8 "Pi" "Holding Together [Union]" +.X 6 8 +.J +Holding Together brings good fortune. +Inquire of the oracle once again +Whether you possess sublimity, constancy, and perseverance; +Then there is no blame. +Those who are uncertain gradually join. +Whoever comes too late +Meets with misfortune. +.I +On the earth is water: +The image of Holding Together. +Thus the kings of antiquity +Bestowed the different states as fiefs +And cultivated friendly relations +With the feudal lords. +.L 1 6 +Hold to him in truth and loyalty; +This is without blame. +Truth, like a full earthen bowl: +Thus in the end +Good fortune comes from without. +.L 2 6 +Hold to him inwardly. +Perseverance brings good fortune. +.L 3 6 +You hold together with the wrong people. +.L 4 6 +Hold to him outwardly also. +Perseverance brings good fortune. +.L 5 9 G +Manifestation of holding together. +In the hunt the king uses beaters on three sides only +And foregoes game that runs off in front. +The citizens need no warning. +Good fortune. +.L 6 6 +He finds no head for holding together. +Misfortune. +.H 9 "Hsiao Ch\'u" "The Taming Power of the Small" +.X 2 1 +.J +The Taming Power of the Small +Has success. +Dense clouds, no rain from our western region. +.I +The wind drives across heaven: +The image of the Taming Power of the Small. +Thus the superior man +Refines the outward aspect of his nature. +.L 1 9 +Return to the way. +How could there be blame in this? +Good fortune. +.L 2 9 +He allows himself to be drawn into returning. +Good fortune. +.L 3 9 +The spokes burst out of the wagon wheels. +Man and wife roll their eyes. +.L 4 6 C +If you are sincere, blood vanishes and fear gives way. +No blame. +.L 5 9 G +If you are sincere and loyally attached, +You are rich in your neighbor. +.L 6 9 +The rain comes, there is rest. +This is due to the lasting effect of character. +Perseverance brings the woman into danger. +The moon is nearly full. +If the superior man persists, +Misfortune comes. +.H 10 "Lu" "Treading [Conduct]" +.X 1 5 +.J +Treading. Treading upon the tail of the tiger. +It does not bite the man. Success. +.I +Heaven above, the lake below: +The image of Treading. +Thus the superior man discriminates between high and low, +And thereby fortifies the thinking of the people. +.L 1 9 +Simple conduct. Progress without blame. +.L 2 9 +Treading a smooth, level course. +The perseverance of a dark man +Brings good fortune. +.L 3 6 C +A one-eyed man is able to see, +A lame man is able to tread. +He treads on the tail of the tiger. +The tiger bites the man. +Misfortune. +Thus does a warrior act on behalf of his great prince. +.L 4 9 +He treads on the tail of the tiger. +Caution and circumspection +Lead ultimately to good fortune. +.L 5 9 G +Resolute conduct. +Perseverance with awareness of danger. +.L 6 9 +Look to your conduct and weigh the favorable signs. +When everything is fulfilled, supreme good fortune comes. +.H 11 "T\'ai" "Peace" +.X 8 1 +.J +Peace. The small departs, +The great approaches. +Good fortune. Success. +.I +Heaven and earth unite: the image of Peace. +Thus the ruler +Divides and completes the course of heaven and earth; +He furthers and regulates the gifts of heaven and earth, +And so aids the people. +.L 1 9 +When ribbon grass is pulled up, the sod comes with it. +Each according to his kind. +Undertakings bring good fortune. +.L 2 9 G +Bearing with the uncultured in gentleness, +Fording the river with resolution, +Not neglecting what is distant, +Not regarding one's companions: +Thus one may manage to walk in the middle. +.L 3 9 +No plain not followed by a slope. +No going not followed by a return. +He who remains persevering in danger +Is without blame. +Do not complain about this truth; +Enjoy the good fortune you still possess. +.L 4 6 +He flutters down, not boasting of his wealth, +Together with his neighbor, +Guileless and sincere. +.L 5 6 G +The sovereign I +Gives his daughter in marriage. +This brings blessing +And supreme good fortune. +.L 6 6 +The wall falls back into the moat. +Use no army now. +Make your commands known within your own town. +Perseverance brings humiliation. +.H 12 "P\'i" "Standstill [Stagnation]" +.X 1 8 +.J +Standstill. Evil people do not further +The perseverance of the superior man. +The great departs; the small approaches. +.I +Heaven and earth do not unite: +The image of Standstill. +Thus the superior man falls back upon his inner worth +In order to escape the difficulties. +He does not permit himself to be honored with revenue. +.L 1 6 +When ribbon grass is pulled up, the sod comes with it. +Each according to his kind. +Perseverance brings good fortune and success. +.L 2 6 C +They bear and endure; +This means good fortune for inferior people. +The standstill serves to help the great man to attain success. +.L 3 6 +They bear shame. +.L 4 9 +He who acts at the command of the highest +Remains without blame. +Those of like mind partake of the blessing. +.L 5 9 G +Standstill is giving way. +Good fortune for the great man. +"What if it should fail, what if it should fail?" +In this way he ties it to a cluster of mulberry shoots. +.L 6 9 +The standstill comes to an end. +First standstill, then good fortune. +.H 13 "T\'ung J\o'^e'n" "Fellowship with Men" +.X 1 3 +.J +Fellowship with Men in the open. +Success. +It furthers one to cross the great water. +The perseverance of the superior man furthers. +.I +Heaven together with fire: +The image of Fellowship with Men. +Thus the superior man organizes the clans +And makes distinctions between things. +.L 1 9 +Fellowship with men at the gate. +No blame. +.L 2 6 G +Fellowship with men in the clan. +Humiliation. +.L 3 9 +He hides weapons in the thicket; +He climbs the high hill in front of it. +For three years he does not rise up. +.L 4 9 +He climbs up on his wall; he cannot attack. +Good fortune. +.L 5 9 G +Men bound in fellowship first weep and lament, +But afterward they laugh. +After great struggles they succeed in meeting. +.L 6 9 +Fellowship with men in the meadow. +No remorse. +.H 14 "Ta Yu" "Possession in Great Measure" +.X 3 1 +.J +Possession in Great Measure. +Supreme success. +.I +Fire in heaven above: +The image of Possession in Great Measure. +Thus the superior man curbs evil and furthers good, +And thereby obeys the benevolent will of heaven. +.L 1 9 +No relationship with what is harmful; +There is no blame in this. +If one remains conscious of difficulty, +One remains without blame. +.L 2 9 +A big wagon for loading. +One may undertake something. +No blame. +.L 3 9 +A prince offers it to the Son of Heaven. +A petty man cannot do this. +.L 4 9 +He makes a difference +Between himself and his neighbor. +No blame. +.L 5 6 G +He whose truth is accessible, yet dignified, +Has good fortune. +.L 6 9 +He is blessed by heaven. +Good fortune. +Nothing that does not further. +.H 15 "Ch\'ien" "Modesty" +.X 8 4 +.J +Modesty creates success. +The superior man carries things through. +.I +Within the earth, a mountain: +The image of Modesty. +Thus the superior man reduces that which is too much, +And augments that which is too little. +He weighs things and makes them equal. +.L 1 6 +A superior man modest about his modesty +May cross the great water. +Good fortune. +.L 2 6 +Modesty that comes to expression. +Perseverance brings good fortune. +.L 3 9 G +A superior man of modesty and merit +Carries things to conclusion. +Good fortune. +.L 4 6 +Nothing that would not further modesty +In movement. +.L 5 6 +No boasting of wealth before one's neighbor. +It is favorable to attack with force. +Nothing that would not further. +.L 6 6 +Modesty that comes to expression. +It is favorable to set armies marching +To chastise one's own city and one's country. +.H 16 "Yu" "Enthusiasm" +.X 7 8 +.J +Enthusiasm. It furthers one to install helpers +And to set armies marching. +.I +Thunder comes resounding out of the earth: +The image of Enthusiasm. +Thus the ancient kings made music +In order to honor merit, +And offered it with splendor +To the Supreme Deity, +Inviting their ancestors to be present. +.L 1 6 +Enthusiasm that expresses itself +Brings misfortune. +.L 2 6 +Firm as a rock. Not a whole day. +Perseverance brings good fortune. +.L 3 6 +Enthusiasm that looks upward creates remorse. +Hesitation brings remorse. +.L 4 9 G +The source of enthusiasm. +He achieves great things. +Doubt not. +You gather friends around you +As a hair clasp gathers the hair. +.L 5 6 +Persistently ill, and still does not die. +.L 6 6 +Deluded enthusiasm. +But if after completion one changes, +There is no blame. +.H 17 "Sui" "Following" +.X 5 7 +.J +Following has supreme success. +Perseverance furthers. No blame. +.I +Thunder in the middle of the lake: +The image of Following. +Thus the superior man at nightfall +Goes indoors for rest and recuperation. +.L 1 9 G +The standard is changing. +Perseverance brings good fortune. +To go out of the door in company +Produces deeds. +.L 2 6 +If one clings to the little boy, +One loses the strong man. +.L 3 6 +If one clings to the strong man, +One loses the little boy. +Through following one finds what one seeks. +It furthers one to remain persevering. +.L 4 9 +Following creates success. +Perseverance brings misfortune. +To go one's way with sincerity brings clarity. +How could there be blame in this? +.L 5 9 G +Sincere in the good. Good fortune. +.L 6 6 +He meets with firm allegiance +And is still further bound. +The king introduces him +To the Western Mountain. +.H 18 "Ku" "Work on What Has Been Spoiled [Decay]" +.X 4 2 +.J +Work on What Has Been Spoiled +Has supreme success. +It furthers one to cross the great water. +Before the starting point, three days. +After the starting point, three days. +.I +The wind blows low on the mountain: +The image of Decay. +Thus the superior man stirs up the people +And strengthens their spirit. +.L 1 6 +Setting right what has been spoiled by the father. +If there is a son, +No blame rests upon the departed father. +Danger. In the end good fortune. +.L 2 9 +Setting right what has been spoiled by the mother. +One must not be too persevering. +.L 3 9 +Setting right what has been spoiled by the father. +There will be little remorse. No great blame. +.L 4 6 +Tolerating what has been spoiled by the father. +In continuing one sees humiliation. +.L 5 6 G +Setting right what has been spoiled by the father. +One meets with praise. +.L 6 9 +He does not serve kings and princes, +Sets himself higher goals. +.H 19 "Lin" "Approach" +.X 8 5 +.J +Approach has supreme success. +Perseverance furthers. +When the eighth month comes, +There will be misfortune. +.I +The earth above the lake: +The image of Approach. +Thus the superior man is inexhaustible +In his will to teach, +And without limits +In his tolerance and protection of the people. +.L 1 9 G +Joint approach. +Perseverance brings good fortune. +.L 2 9 G +Joint approach. +Good fortune. +Everything furthers. +.L 3 6 +Comfortable approach. +Nothing that would further. +If one is induced to grieve over it, +One becomes free of blame. +.L 4 6 +Complete approach. +No blame. +.L 5 6 +Wise approach. +This is right for a great prince. +Good fortune. +.L 6 6 +Greathearted approach. +Good fortune. No blame. +.H 20 "Kuan" "Contemplation (View)" +.X 2 8 +.J +Contemplation. The ablution has been made, +But not yet the offering. +Full of trust they look up to him. +.I +The wind blows over the earth: +The image of Contemplation. +Thus the kings of old visited the regions of the world, +Contemplated the people, +And gave them instruction. +.L 1 6 +Boylike contemplation. +For an inferior man, no blame. +For a superior man, humiliation. +.L 2 6 +Contemplation through the crack of the door. +Furthering for the perseverance of a woman. +.L 3 6 +Contemplation of my life +Decides the choice +Between advance and retreat. +.L 4 6 +Contemplation of the light of the kingdom. +It furthers one to exert influence as the guest of a king. +.L 5 9 G +Contemplation of my life. +The superior man is without blame. +.L 6 9 G +Contemplation of his life. +The superior man is without blame. +.H 21 "Shih Ho" "Biting Through" +.X 3 7 +.J +Biting Through has success. +It is favorable to let justice be administered. +.I +Thunder and lightning: +The image of Biting Through. +Thus the kings of former times made firm the laws +Through clearly defined penalties. +.L 1 9 +His feet are fastened in the stocks, +So that his toes disappear. +No blame. +.L 2 6 +Bites through tender meat, +So that his nose disappears. +No blame. +.L 3 6 +Bites on old dried meat +And strikes on something poisonous. +Slight humiliation. No blame. +.L 4 9 +Bites on dried gristly meat. +Receives metal arrows. +It furthers one to be mindful of difficulties +And to be persevering. +Good fortune. +.L 5 6 G +Bites on dried lean meat. +Receives yellow gold. +Perseveringly aware of danger. +No blame. +.L 6 9 +His neck is fastened in the wooden cangue, +So that his ears disappear. +Misfortune. +.H 22 "Pi" "Grace" +.X 4 3 +.J +Grace has success. +In small matters +It is favorable to undertake something. +.I +Fire at the foot of the mountain: +The image of Grace. +Thus does the superior man proceed +When clearing up current affairs. +But he dare not decide controversial issues in this way. +.L 1 9 +He lends grace to his toes, leaves the carriage, and walks. +.L 2 6 G +Lends grace to the beard on his chin. +.L 3 9 +Graceful and moist. +Constant perseverance brings good fortune. +.L 4 6 +Grace or simplicity? +A white horse comes as if on wings. +He is not a robber, +He will woo at the right time. +.L 5 6 +Grace in hills and gardens. +The roll of silk is meager and small. +Humiliation, but in the end good fortune. +.L 6 9 G +Simple grace. No blame. +.H 23 "Po" "Splitting Apart" +.X 4 8 +.J +Splitting Apart. It does not further one +To go anywhere. +.I +The mountain rests on the earth: +The image of Splitting Apart. +Thus those above can ensure their position +Only by giving generously to those below. +.L 1 6 +The leg of the bed is split. +Those who persevere are destroyed. +Misfortune. +.L 2 6 +The bed is split at the edge. +Those who persevere are destroyed. +Misfortune. +.L 3 6 +He splits with them. No blame. +.L 4 6 +The bed is split up to the skin. +Misfortune. +.L 5 6 +A shoal of fishes. Favor comes through the court ladies. +Everything acts to further. +.L 6 9 G +There is a large fruit still uneaten. +The superior man receives a carriage. +The house of the inferior man is split apart. +.H 24 "Fu" "Return (The Turning Point)" +.X 8 7 +.J +Return. Success. +Going out and coming in without error. +Friends come without blame. +To and fro goes the way. +On the seventh day comes return. +It furthers one to have somewhere to go. +.I +Thunder within the earth: +The image of the Turning Point. +Thus the kings of antiquity closed the passes +At the time of solstice. +Merchants and strangers did not go about, +And the ruler +Did not travel through the provinces. +.L 1 9 G +Return from a short distance. +No need for remorse. +Great good fortune. +.L 2 6 +Quiet return. Good fortune. +.L 3 6 +Repeated return. Danger. No blame. +.L 4 6 +Walking in the midst of others, +One returns alone. +.L 5 6 +Noblehearted return. No remorse. +.L 6 6 +Missing the return. Misfortune. +Misfortune from within and without. +If armies are set marching in this way, +One will in the end suffer a great defeat, +Disastrous for the ruler of the country. +For ten years +It will not be possible to attack again. +.H 25 "Wu Wang" "Innocence (The Unexpected)" +.X 1 7 +.J +Innocence. Supreme success. +Perseverance furthers. +If someone is not as he should be, +He has misfortune, +And it does not further him +To undertake anything. +.I +Under heaven thunder rolls: +All things attain the natural state of innocence. +Thus the kings of old, +Rich in virtue, and in harmony with the time, +Fostered and nourished all beings. +.L 1 9 G +Innocent behavior brings good fortune. +.L 2 6 +If one does not count on the harvest while plowing, +Nor on the use of the ground while clearing it, +It furthers one to undertake something. +.L 3 6 +Undeserved misfortune. +The cow that was tethered by someone +Is the wanderer's gain, the citizen's loss. +.L 4 9 +He who can be persevering +Remains without blame. +.L 5 9 G +Use no medicine in an illness +Incurred through no fault of your own. +It will pass of itself. +.L 6 9 +Innocent action brings misfortune. +Nothing furthers. +.H 26 "Ta Ch\'u" "The Taming Power of the Great" +.X 4 1 +.J +The Taming Power of the Great. +Perseverance furthers. +Not eating at home brings good fortune. +It furthers one to cross the great water. +.I +Heaven within the mountain: +The image of the Taming Power of the Great. +Thus the superior man acquaints himself with many sayings of antiquity +And many deeds of the past, +In order to strengthen his character thereby. +.L 1 9 +Danger is at hand. It furthers one to desist. +.L 2 9 +The axletrees are taken from the wagon. +.L 3 9 +A good horse that follows others. +Awareness of danger, +With perseverance, furthers. +Practice chariot driving and armed defense daily. +It furthers one to have somewhere to go. +.L 4 6 +The headboard of a young bull. +Great good fortune. +.L 5 6 G +The tusk of a gelded boar. +Good fortune. +.L 6 9 G +One attains the way of heaven. Success. +.H 27 "I" "The Corners of the Mouth (Providing Nourishment)" +.X 4 7 +.J +The Corners of the Mouth. +Perseverance brings good fortune. +Pay heed to the providing of nourishment +And to what a man seeks +To fill his own mouth with. +.I +At the foot of the mountain, thunder: +The image of Providing Nourishment. +Thus the superior man is careful of his words +And temperate in eating and drinking. +.L 1 9 +You let your magic tortoise go, +And look at me with the corners of your mouth drooping. +Misfortune. +.L 2 6 +Turning to the summit for nourishment, +Deviating from the path +To seek nourishment from the hill. +Continuing to do this brings misfortune. +.L 3 6 +Turning away from nourishment. +Perseverance brings misfortune. +Do not act thus for ten years. +Nothing serves to further. +.L 4 6 +Turning to the summit +For provision of nourishment +Brings good fortune. +Spying about with sharp eyes +Like a tiger with insatiable craving. +No blame. +.L 5 6 G +Turning away from the path. +To remain persevering brings good fortune. +One should not cross the great water. +.L 6 9 G +The source of nourishment. +Awareness of danger brings good fortune. +It furthers one to cross the great water. +.H 28 "Ta Kuo" "Preponderance of the Great" +.X 5 2 +.J +Preponderance of the Great. +The ridgepole sags to the breaking point. +It furthers one to have somewhere to go. +Success. +.I +The lake rises above the trees: +The image of Preponderance of the Great. +Thus the superior man, when he stands alone, +Is unconcerned, +And if he has to renounce the world, +He is undaunted. +.L 1 6 +To spread white rushes underneath. +No blame. +.L 2 9 G +A dry poplar sprouts at the root. +An older man takes a young wife. +Everything furthers. +.L 3 9 +The ridgepole sags to the breaking point. +Misfortune. +.L 4 9 G +The ridgepole is braced. Good fortune. +If there are ulterior motives, it is humiliating. +.L 5 9 +A withered poplar puts forth flowers. +An older woman takes a husband. +No blame. No praise. +.L 6 6 +One must go through the water. +It goes over one's head. +Misfortune. No blame. +.H 29 "K\'an" "The Abysmal (Water)" +.X 6 6 +.J +The Abysmal repeated. +If you are sincere, you have success in your heart, +And whatever you do succeeds. +.I +Water flows on uninterruptedly and reaches its goal: +The image of the Abysmal repeated. +Thus the superior man walks in lasting virtue +And carries on the business of teaching. +.L 1 6 +Repetition of the Abysmal. +In the abyss one falls into a pit. +Misfortune. +.L 2 9 G +The abyss is dangerous. +One should strive to attain small things only. +.L 3 6 +Forward and backward, abyss on abyss. +In danger like this, pause at first and wait, +Otherwise you will fall into a pit in the abyss. +Do not act in this way. +.L 4 6 +A jug of wine, a bowl of rice with it; +Earthen vessels +Simply handed in through the window. +There is certainly no blame in this. +.L 5 9 G +The abyss is not filled to overflowing, +It is filled only to the rim. +No blame. +.L 6 6 +Bound with cords and ropes, +Shut in between thorn-hedged prison walls: +For three years one does not find the way. +Misfortune. +.H 30 "Li" "The Clinging, Fire" +.X 3 3 +.J +The Clinging. Perseverance furthers. +It brings success. +Care of the cow brings good fortune. +.I +That which is bright rises twice: +The image of Fire. +Thus the great man, by perpetuating this brightness, +Illumines the four quarters of the world. +.L 1 9 +The footprints run crisscross. +If one is seriously intent, no blame. +.L 2 6 G +Yellow light. Supreme good fortune. +.L 3 9 +In the light of the setting sun, +Men either beat the pot and sing +Or loudly bewail the approach of old age. +Misfortune. +.L 4 9 +Its coming is sudden; +It flames up, dies down, is thrown away. +.L 5 6 G +Tears in floods, sighing and lamenting. +Good fortune. +.L 6 9 +The king uses him to march forth and chastise. +Then it is best to kill the leaders +And take captive the followers. No blame. +.H 31 "Hsien" "Influence (Wooing)" +.X 5 4 +.J +Influence. Success. +Perseverance furthers. +To take a maiden to wife brings good fortune. +.I +A lake on the mountain: +The image of Influence. +Thus the superior man encourages people to approach him +By his readiness to receive them. +.L 1 6 +The influence shows itself in the big toe. +.L 2 6 +The influence shows itself in the calves of the legs. +Misfortune. +Tarrying brings good fortune. +.L 3 9 +The influence shows itself in the thighs. +Holds to that which follows it. +To continue is humiliating. +.L 4 9 G +Perseverance brings good fortune. +Remorse disappears. +If a man is agitated in mind, +And his thoughts go hither and thither, +Only those friends +On whom he fixes his conscious thoughts +Will follow. +.L 5 9 G +The influence shows itself in the back of the neck. +No remorse. +.L 6 6 +The influence shows itself in the jaws, cheeks, and tongue. +.H 32 "H\o'^e'ng" "Duration" +.X 7 2 +.J +Duration. Success. No blame. +Perseverance furthers. +It furthers one to have somewhere to go. +.I +Thunder and wind: the image of Duration. +Thus the superior man stands firm +And does not change his direction. +.L 1 6 +Seeking duration too hastily brings misfortune persistently. +Nothing that would further. +.L 2 9 G +Remorse disappears. +.L 3 9 +He who does not give duration to his character +Meets with disgrace. +Persistent humiliation. +.L 4 9 +No game in the field. +.L 5 6 +Giving duration to one's character through perseverance. +This is good fortune for a woman, misfortune for a man. +.L 6 6 +Restlessness as an enduring condition brings misfortune. +.H 33 "Tun" "Retreat" +.X 1 4 +.J +Retreat. Success. +In what is small, perseverance furthers. +.I +Mountain under heaven: the image of Retreat. +Thus the superior man keeps the inferior man at a distance, +Not angrily but with reserve. +.L 1 6 C +At the tail in retreat. This is dangerous. +One must not wish to undertake anything. +.L 2 6 C +He holds him fast with yellow oxhide. +No one can tear him loose. +.L 3 9 +A halted retreat +Is nerve-wracking and dangerous. +To retain people as men- and maidservants +Brings good fortune. +.L 4 9 +Voluntary retreat brings good fortune to the superior man +And downfall to the inferior man. +.L 5 9 G +Friendly retreat. Perseverance brings good fortune. +.L 6 9 +Cheerful retreat. Everything serves to further. +.H 34 "Ta Chuang" "The Power of the Great" +.X 7 1 +.J +The Power of the Great. Perseverance furthers. +.I +Thunder in heaven above: +The image of the Power of the Great. +Thus the superior man does not tread upon paths +That do not accord with established order. +.L 1 9 +Power in the toes. +Continuing brings misfortune. +This is certainly true. +.L 2 9 +Perseverance brings good fortune. +.L 3 9 +The inferior man works through power. +The superior man does not act thus. +To continue is dangerous. +A goat butts against a hedge +And gets its horns entangled. +.L 4 9 G +Perseverance brings good fortune. +Remorse disappears. +The hedge opens; there is no entanglement. +Power depends upon the axle of a big cart. +.L 5 6 +Loses the goat with ease. +No remorse. +.L 6 6 +A goat butts against a hedge. +It cannot go backward, it cannot go forward. +Nothing serves to further. +If one notes the difficulty, this brings good fortune. +.H 35 "Chin" "Progress" +.X 3 8 +.J +Progress. The powerful prince +Is honored with horses in large numbers. +In a single day he is granted audience three times. +.I +The sun rises over the earth: +The image of Progress. +Thus the superior man himself +Brightens his bright virtue. +.L 1 6 +Progressing, but turned back. +Perseverance brings good fortune. +If one meets with no confidence, one should remain calm. +No mistake. +.L 2 6 +Progressing, but in sorrow. +Perseverance brings good fortune. +Then one obtains great happiness from one's ancestress. +.L 3 6 +All are in accord. Remorse disappears. +.L 4 9 +Progress like a hamster. +Perseverance brings danger. +.L 5 6 G +Remorse disappears. +Take not gain and loss to heart. +Undertakings bring good fortune. +Everything serves to further. +.L 6 9 +Making progress with the horns is permissible +Only for the purpose of punishing one's own city. +To be conscious of danger brings good fortune. +No blame. +Perseverance brings humiliation. +.H 36 "Ming I" "Darkening of the Light" +.X 8 3 +.J +Darkening of the Light. In adversity +It furthers one to be persevering. +.I +The light has sunk into the earth: +The image of Darkening of the Light. +Thus does the superior man live with the great mass: +He veils his light, yet still shines. +.L 1 9 +Darkening of the light during flight. +He lowers his wings. +The superior man does not eat for three days +On his wanderings. +But he has somewhere to go. +The host has occasion to gossip about him. +.L 2 6 G +Darkening of the light injures him in the left thigh. +He gives aid with the strength of a horse. +Good fortune. +.L 3 9 +Darkening of the light during the hunt in the south. +Their great leader is captured. +One must not expect perseverance too soon. +.L 4 6 +He penetrates the left side of the belly. +One gets at the very heart of the darkening of the light, +And leaves gate and courtyard. +.L 5 6 G +Darkening of the light as with Prince Chi. +Perseverance furthers. +.L 6 6 C +Not light but darkness. +First he climbed up to heaven, +Then he plunged into the depths of the earth. +.H 37 "Chia J\o'^e'n" "The Family [The Clan]" +.X 2 3 +.J +The Family. The perseverance of the woman furthers. +.I +Wind comes forth from fire: +The image of the Family. +Thus the superior man has substance in his words +And duration in his way of life. +.L 1 9 +Firm seclusion within the family. +Remorse disappears. +.L 2 6 G +She should not follow her whims. +She must attend within to the food. +Perseverance brings good fortune. +.L 3 9 +When tempers flare up in the family, +Too great severity brings remorse. +Good fortune nonetheless. +When woman and child dally and laugh, +It leads in the end to humiliation. +.L 4 6 +She is the treasure of the house. +Great good fortune. +.L 5 9 G +As a king he approaches his family. +Fear not. +Good fortune. +.L 6 9 +His work commands respect. +In the end good fortune comes. +.H 38 "K\'uei" "Opposition" +.X 3 5 +.J +Opposition. In small matters, good fortune. +.I +Above, fire; below, the lake: +The image of Opposition. +Thus amid all fellowship +The superior man retains his individuality. +.L 1 9 +Remorse disappears. +If you lose your horse, do not run after it; +It will come back of its own accord. +When you see evil people, +Guard yourself against mistakes. +.L 2 9 G +One meets his lord in a narrow street. +No blame. +.L 3 6 +One sees the wagon dragged back, +The oxen halted, +A man's hair and nose cut off. +Not a good beginning, but a good end. +.L 4 9 +Isolated through opposition, +One meets a like-minded man +With whom one can associate in good faith. +Despite the danger, no blame. +.L 5 6 G +Remorse disappears. +The companion bites his way through the wrappings. +If one goes to him, +How could it be a mistake? +.L 6 9 +Isolated through opposition, +One sees one's companion as a pig covered with dirt, +As a wagon full of devils. +First one draws a bow against him, +Then one lays the bow aside. +He is not a robber; he will woo at the right time. +As one goes, rain falls; then good fortune comes. +.H 39 "Chien" "Obstruction" +.X 6 4 +.J +Obstruction. The southwest furthers. +The northeast does not further. +It furthers one to see the great man. +Perseverance brings good fortune. +.I +Water on the mountain: +The image of Obstruction. +Thus the superior man turns his attention to himself +And molds his character. +.L 1 6 +Going leads to obstructions, +Coming meets with praise. +.L 2 6 +The king's servant is beset by obstruction upon obstruction, +But it is not his own fault. +.L 3 9 +Going leads to obstructions; +Hence he comes back. +.L 4 6 +Going leads to obstructions, +Coming leads to union. +.L 5 9 G +In the midst of the greatest obstructions, +Friends come. +.L 6 6 +Going leads to obstructions, +Coming leads to great good fortune. +It furthers one to see the great man. +.H 40 "Hsieh" "Deliverance" +.X 7 6 +.J +Deliverance. The southwest furthers. +If there is no longer anything where one has to go, +Return brings good fortune. +If there is still something where one has to go, +Hastening brings good fortune. +.I +Thunder and rain set in: +The image of Deliverance. +Thus the superior man pardons mistakes +And forgives misdeeds. +.L 1 6 +Without blame. +.L 2 9 G +One kills three foxes in the field +And receives a yellow arrow. +Perseverance brings good fortune. +.L 3 6 +If a man carries a burden on his back +And nonetheless rides in a carriage, +He thereby encourages robbers to draw near. +Perseverance leads to humiliation. +.L 4 9 +Deliver yourself from your great toe. +Then the companion comes, +And him you can trust. +.L 5 6 G +If only the superior man can deliver himself, +It brings good fortune. +Thus he proves to inferior men that he is in earnest. +.L 6 6 +The prince shoots at a hawk on a high wall. +He kills it. Everything serves to further. +.H 41 "Sun" "Decrease" +.X 4 5 +.J +Decrease combined with sincerity +Brings about supreme good fortune +Without blame. +One may be persevering in this. +It furthers one to undertake something. +How is this to be carried out? +One may use two small bowls for the sacrifice. +.I +At the foot of the mountain, the lake: +The image of Decrease. +Thus the superior man controls his anger +And restrains his instincts. +.L 1 9 +Going quickly when one's tasks are finished +Is without blame. +But one must reflect on how much one may decrease others. +.L 2 9 +Perseverance furthers. +To undertake something brings misfortune. +Without decreasing oneself, +One is able to bring increase to others. +.L 3 6 C +When three people journey together, +Their number decreases by one. +When one man journeys alone, +He finds a companion. +.L 4 6 +If a man decreases his faults, +It makes the other hasten to come and rejoice. +No blame. +.L 5 6 G +Someone does indeed increase him. +Ten pairs of tortoises cannot oppose it. +Supreme good fortune. +.L 6 9 C +If one is increased without depriving others, +There is no blame. +Perseverance brings good fortune. +It furthers one to undertake something. +One obtains servants +But no longer has a separate home. +.H 42 "I" "Increase" +.X 2 7 +.J +Increase. It furthers one +To undertake something. +It furthers one to cross the great water. +.I +Wind and thunder: the image of Increase. +Thus the superior man: +If he sees good, he imitates it; +If he has faults, he rids himself of them. +.L 1 9 C +It furthers one to accomplish great deeds. +Supreme good fortune. No blame. +.L 2 6 G +Someone does indeed increase him; +Ten pairs of tortoises cannot oppose it. +Constant perseverance brings good fortune. +The king presents him before God. +Good fortune. +.L 3 6 +One is enriched through unfortunate events. +No blame, if you are sincere +And walk in the middle, +And report with a seal to the prince. +.L 4 6 C +If you walk in the middle +And report to the prince, +He will follow. +It furthers one to be used +In the removal of the capital. +.L 5 9 G +If in truth you have a kind heart, ask not. +Supreme good fortune. +Truly, kindness will be recognized as your virtue. +.L 6 9 +He brings increase to no one. +Indeed, someone even strikes him. +He does not keep his heart constantly steady. +Misfortune. +.H 43 "Kuai" "Break-through (Resoluteness)" +.X 5 1 +.J +Break-through. One must resolutely make the matter known +At the court of the king. +It must be announced truthfully. Danger. +It is necessary to notify one's own city. +It does not further to resort to arms. +It furthers one to undertake something. +.I +The lake has risen up to heaven: +The image of Break-through. +Thus the superior man +Dispenses riches downward +And refrains from resting on his virtue. +.L 1 9 +Mighty in the forward-striding toes. +When one goes and is not equal to the task, +One makes a mistake. +.L 2 9 +A cry of alarm. Arms at evening and at night. +Fear nothing. +.L 3 9 +To be powerful in the cheekbones +Brings misfortune. +The superior man is firmly resolved. +He walks alone and is caught in the rain. +He is bespattered, +And people murmur against him. +No blame. +.L 4 9 +There is no skin on his thighs, +And walking comes hard. +If a man were to let himself be led like a sheep, +Remorse would disappear. +But if these words are heard +They will not be believed. +.L 5 9 G +In dealing with weeds, +Firm resolution is necessary. +Walking in the middle +Remains free of blame. +.L 6 6 C +No cry. +In the end misfortune comes. +.H 44 "Kou" "Coming to Meet" +.X 1 2 +.J +Coming to Meet. The maiden is powerful. +One should not marry such a maiden. +.I +Under heaven, wind: +The image of Coming to Meet. +Thus does the prince act when disseminating his commands +And proclaiming them to the four quarters of heaven. +.L 1 6 C +It must be checked with a brake of bronze. +Perseverance brings good fortune. +If one lets it take its course, one experiences misfortune. +Even a lean pig has it in him to rage around. +.L 2 9 G +There is a fish in the tank. No blame. +Does not further guests. +.L 3 9 +There is no skin on his thighs, +And walking comes hard. +If one is mindful of the danger, +No great mistake is made. +.L 4 9 +No fish in the tank. +This leads to misfortune. +.L 5 9 G +A melon covered with willow leaves. +Hidden lines. +Then it drops down to one from heaven. +.L 6 9 +He comes to meet with his horns. +Humiliation. No blame. +.H 45 "Ts\'ui" "Gathering Together [Massing]" +.X 5 8 +.J +Gathering Together. Success. +The king approaches his temple. +It furthers one to see the great man. +This brings success. Perseverance furthers. +To bring great offerings creates good fortune. +It furthers one to undertake something. +.I +Over the earth, the lake: +The image of Gathering Together. +Thus the superior man renews his weapons +In order to meet the unforseen. +.L 1 6 +If you are sincere, but not to the end, +There will sometimes be confusion, sometimes gathering together. +If you call out, +Then after one grasp of the hand you can laugh again. +Regret not. Going is without blame. +.L 2 6 +Letting oneself be drawn +Brings good fortune and remains blameless. +If one is sincere, +It furthers one to bring even a small offering. +.L 3 6 +Gathering together amid sighs. +Nothing that would further. +Going is without blame. +Slight humiliation. +.L 4 9 G +Great good fortune. No blame. +.L 5 9 G +If in gathering together one has position, +This brings no blame. +If there are some who are not yet sincerely in the work, +Sublime and enduring perseverance is needed. +Then remorse disappears. +.L 6 6 +Lamenting and sighing, floods of tears. +No blame. +.H 46 "Sh\o'^e'ng" "Pushing Upward" +.X 8 2 +.J +Pushing Upward has supreme success. +One must see the great man. +Fear not. +Departure toward the south +Brings good fortune. +.I +Within the earth, wood grows: +The image of Pushing Upward. +Thus the superior man of devoted character +Heaps up small things +In order to achieve something high and great. +.L 1 6 C +Pushing upward that meets with confidence +Brings great good fortune. +.L 2 9 +If one is sincere, +It furthers one to bring even a small offering. +No blame. +.L 3 9 +One pushes upward into an empty city. +.L 4 6 +The king offers him Mount Ch'i. +Good fortune. No blame. +.L 5 6 G +Perseverance brings good fortune. +One pushes upward by steps. +.L 6 6 +Pushing upward in darkness. +It furthers one +To be unremittingly persevering. +.H 47 "K\'un" "Oppression (Exhaustion)" +.X 5 6 +.J +Oppression. Success. Perseverance. +The great man brings about good fortune. +No blame. +When one has something to say, +It is not believed. +.I +There is no water in the lake: +The image of Exhaustion. +Thus the superior man stakes his life +On following his will. +.L 1 6 +One sits oppressed under a bare tree +And strays into a gloomy valley. +For three years one sees nothing. +.L 2 9 G +One is oppressed while at meat and drink. +The man with the scarlet knee bands is just coming. +It furthers one to offer sacrifice. +To set forth brings misfortune. +No blame. +.L 3 6 +A man permits himself to be oppressed by stone, +And leans on thorns and thistles. +He enters his house and does not see his wife. +Misfortune. +.L 4 9 +He comes very quietly, oppressed in a golden carriage. +Humiliation, but the end is reached. +.L 5 9 G +His nose and feet are cut off. +Oppression at the hands of the man with the purple knee bands. +Joy comes softly. +It furthers one to make offerings and libations. +.L 6 6 +He is oppressed by creeping vines. +He moves uncertainly and says, "Movement brings remorse." +If one feels remorse over this and makes a start, +Good fortune comes. +.H 48 "Ching" "The Well" +.X 6 2 +.J +The Well. The town may be changed, +But the well cannot be changed. +It neither decreases nor increases. +They come and go and draw from the well. +If one gets down almost to the water +And the rope does not go all the way, +Or the jug breaks, it brings misfortune. +.I +Water over wood: the image of the Well. +Thus the superior man encourages the people at their work, +And exhorts them to help one another. +.L 1 6 +One does not drink the mud of the well. +No animals come to an old well. +.L 2 9 +At the wellhole one shoots fishes. +The jug is broken and leaks. +.L 3 9 +The well is cleaned, but no one drinks from it. +This is my heart's sorrow, +For one might draw from it. +If the king were clear-minded, +Good fortune might be enjoyed in common. +.L 4 6 +The well is being lined. No blame. +.L 5 9 G +In the well there is a clear, cold spring +From which one can drink. +.L 6 6 +One draws from the well +Without hindrance. +It is dependable. +Supreme good fortune. +.H 49 "Ko" "Revolution (Molting)" +.X 5 3 +.J +Revolution. On your own day +You are believed. +Supreme success, +Furthering through perseverance. +Remorse disappears. +.I +Fire in the lake: the image of Revolution. +Thus the superior man +Sets the calendar in order +And makes the seasons clear. +.L 1 9 +Wrapped in the hide of a yellow cow. +.L 2 6 +When one's own day comes, one may create revolution. +Starting brings good fortune. No blame. +.L 3 9 +Starting brings misfortune. +Perseverance brings danger. +When talk of revolution has gone the rounds three times, +One may commit himself, +And men will believe him. +.L 4 9 +Remorse disappears. Men believe him. +Changing the form of government brings good fortune. +.L 5 9 G +The great man changes like a tiger. +Even before he questions the oracle +He is believed. +.L 6 6 +The superior man changes like a panther. +The inferior man molts in the face. +Starting brings misfortune. +To remain persevering brings good fortune. +.H 50 "Ting" "The Caldron" +.X 3 2 +.J +The Caldron. Supreme good fortune. +Success. +.I +Fire over wood: +The image of the Caldron. +Thus the superior man consolidates his fate +By making his position correct. +.L 1 6 +A \fIting\fR with legs upturned. +Furthers removal of stagnating stuff. +One takes a concubine for the sake of her son. +No blame. +.L 2 9 +There is food in the \fIting\fR. +My comrades are envious, +But they cannot harm me. +Good fortune. +.L 3 9 +The handle of the \fIting\fR is altered. +One is impeded in his way of life. +The fat of the pheasant is not eaten. +Once rain falls, remorse is spent. +Good fortune comes in the end. +.L 4 9 +The legs of the \fIting\fR are broken. +The prince's meal is spilled +And his person is soiled. +Misfortune. +.L 5 6 G +The \fIting\fR has yellow handles, golden carrying rings. +Perseverance furthers. +.L 6 9 G +The \fIting\fR has rings of jade. +Great good fortune. +Nothing that would not act to further. +.H 51 "Ch\o'^e'n" "The Arousing (Shock, Thunder)" +.X 7 7 +.J +Shock brings success. +Shock comes\(emoh, oh! +Laughing words\(emha, ha! +The shock terrifies for a hundred miles, +And he does not let fall the sacrificial spoon and chalice. +.I +Thunder repeated: the image of Shock. +Thus in fear and trembling +The superior man sets his life in order +And examines himself. +.L 1 9 G +Shock comes\(emoh, oh! +Then follow laughing words\(emha, ha! +Good fortune. +.L 2 6 +Shock comes bringing danger. +A hundred thousand times +You lose your treasures +And must climb the nine hills. +Do not go in pursuit of them. +After seven days you will get them back. +.L 3 6 +Shock comes and makes one distraught. +If shock spurs to action +One remains free of misfortune. +.L 4 9 +Shock is mired. +.L 5 6 +Shock goes hither and thither. +Danger. +However, nothing at all is lost. +Yet there are things to be done. +.L 6 6 +Shock brings ruin and terrified gazing around. +Going ahead brings misfortune. +If it has not yet touched one's own body +But has reached one's neighbor first, +There is no blame. +One's comrades have something to talk about. +.H 52 "K\o'^e'n" "Keeping Still, Mountain" +.X 4 4 +.J +Keeping Still. Keeping his back still +So that he no longer feels his body. +He goes into his courtyard +And does not see his people. +No blame. +.I +Mountains standing close together: +The image of Keeping Still. +Thus the superior man +Does not permit his thoughts +To go beyond his situation. +.L 1 6 +Keeping his toes still. +No blame. +Continued perseverance furthers. +.L 2 6 +Keeping his calves still. +He cannot rescue him whom he follows. +His heart is not glad. +.L 3 9 +Keeping his hips still. +Making his sacrum stiff. +Dangerous. The heart suffocates. +.L 4 6 +Keeping his trunk still. +No blame. +.L 5 6 +Keeping his jaws still. +The words have order. +Remorse disappears. +.L 6 9 G +Noblehearted keeping still. +Good fortune. +.H 53 "Chien" "Development (Gradual Progress)" +.X 2 4 +.J +Development. The maiden +Is given in marriage. +Good fortune. +Perseverance furthers. +.I +On the mountain, a tree: +The image of Development. +Thus the superior man abides in dignity and virtue, +In order to improve the mores. +.L 1 6 +The wild goose gradually draws near the shore. +The young son is in danger. +There is talk. No blame. +.L 2 6 G +The wild goose gradually draws near the cliff. +Eating and drinking in peace and concord. +Good fortune. +.L 3 9 +The wild goose gradually draws near the plateau. +The man goes forth and does not return. +The woman carries a child but does not bring it forth. +Misfortune. +It furthers one to fight off robbers. +.L 4 6 +The wild goose gradually draws near the tree. +Perhaps it will find a flat branch. No blame. +.L 5 9 G +The wild goose gradually draws near the summit. +For three years the woman has no child. +In the end nothing can hinder her. +Good fortune. +.L 6 9 +The wild goose gradually draws near the cloud heights. +Its feathers can be used for the sacred dance. +Good fortune. +.H 54 "Kuei Mei" "The Marrying Maiden" +.X 7 5 +.J +The Marrying Maiden. +Undertakings bring misfortune. +Nothing that would further. +.I +Thunder over the lake: +The image of the Marrying Maiden. +Thus the superior man +Understands the transitory +In the light of the eternity of the end. +.L 1 9 +The marrying maiden as a concubine. +A lame man who is able to tread. +Undertakings bring good fortune. +.L 2 9 +A one-eyed man who is able to see. +The perseverance of a solitary man furthers. +.L 3 6 C +The marrying maiden as a slave. +She marries as a concubine. +.L 4 9 +The marrying maiden draws out the allotted time. +A late marriage comes in due course. +.L 5 6 G +The sovereign I gave his daughter in marriage. +The embroidered garments of the princess +Were not as gorgeous +As those of the servingmaid. +The moon that is nearly full +Brings good fortune. +.L 6 6 C +The woman holds the basket, but there are no fruits in it. +The man stabs the sheep, but no blood flows. +Nothing that acts to further. +.H 55 "F\o'^e'ng" "Abundance [Fullness]" +.X 7 3 +.J +Abundance has success. +The king attains abundance. +Be not sad. +Be like the sun at midday. +.I +Both thunder and lightning come: +The image of Abundance. +Thus the superior man decides lawsuits +And carries out punishments. +.L 1 9 +When a man meets his destined ruler, +They can be together ten days, +And it is not a mistake. +Going meets with recognition. +.L 2 6 +The curtain is of such fullness +That the polestars can be seen at noon. +Through going one meets with mistrust and hate. +If one rouses him through truth, +Good fortune comes. +.L 3 9 +The underbrush is of such abundance +That the small stars can be seen at noon. +He breaks his right arm. No blame. +.L 4 9 +The curtain is of such fullness +That the polestars can be seen at noon. +He meets his ruler, who is of like kind. +Good fortune. +.L 5 6 G +Lines are coming, +Blessing and fame draw near. +Good fortune. +.L 6 6 +His house is in a state of abundance. +He screens off his family. +He peers through the gate +And no longer perceives anyone. +For three years he sees nothing. +Misfortune. +.H 56 "Lu" "The Wanderer" +.X 3 4 +.J +The Wanderer. Success through smallness. +Perseverance brings good fortune +To the wanderer. +.I +Fire on the mountain: +The image of the Wanderer. +Thus the superior man +Is clear-minded and cautious +In imposing penalties, +And protracts no lawsuits. +.L 1 6 +If the wanderer busies himself with trivial things, +He draws down misfortune upon himself. +.L 2 6 +The wanderer comes to an inn. +He has his property with him. +He wins the steadfastness of a young servant. +.L 3 9 +The wanderer's inn burns down. +He loses the steadfastness of his young servant. +Danger. +.L 4 9 +The wanderer rests in a shelter. +He obtains his property and an ax. +My heart is not glad. +.L 5 6 G +He shoots a pheasant. +It drops with the first arrow. +In the end this brings both praise and office. +.L 6 9 +The bird's nest burns up. +The wanderer laughs at first, +Then must needs lament and weep. +Through carelessness he loses his cow. +Misfortune. +.H 57 "Sun" "The Gentle (The Penetrating, Wind)" +.X 2 2 +.J +The Gentle. Success through what is small. +It furthers one to have somewhere to go. +It furthers one to see the great man. +.I +Winds following one upon the other: +The image of the Gently Penetrating. +Thus the superior man +Spreads his commands abroad +And carries out his undertakings. +.L 1 6 C +In advancing and in retreating, +The perseverance of a warrior furthers. +.L 2 9 +Penetration under the bed. +Priests and magicians are used in great number. +Good fortune. No blame. +.L 3 9 +Repeated penetration. Humiliation. +.L 4 6 C +Remorse vanishes. +During the hunt +Three kinds of game are caught. +.L 5 9 G +Perseverance brings good fortune. +Remorse vanishes. +Nothing that does not further. +No beginning, but an end. +Before the change, three days. +After the change, three days. +Good fortune. +.L 6 9 +Penetration under the bed. +He loses his property and his ax. +Perseverance brings misfortune. +.H 58 "Tui" "The Joyous, Lake" +.X 5 5 +.J +The Joyous. Success. +Perseverance is favorable. +.I +Lakes resting one on the other: +The image of the Joyous. +Thus the superior man joins with his friends +For discussion and practice. +.L 1 9 +Contented joyousness. Good fortune. +.L 2 9 G +Sincere joyousness. Good fortune. +Remorse disappears. +.L 3 6 C +Coming joyousness. Misfortune. +.L 4 9 +Joyousness that is weighed is not at peace. +After ridding himself of mistakes a man has joy. +.L 5 9 G +Sincerity toward disintegrating influences is dangerous. +.L 6 6 C +Seductive joyousness. +.H 59 "Huan" "Dispersion [Dissolution]" +.X 2 6 +.J +Dispersion. Success. +The king approaches his temple. +It furthers one to cross the great water. +Perseverance furthers. +.I +The wind drives over the water: +The image of Dispersion. +Thus the kings of old sacrificed to the Lord +And built temples. +.L 1 6 +He brings help with the strength of a horse. +Good fortune. +.L 2 9 C +At the dissolution +He hurries to that which supports him. +Remorse disappears. +.L 3 6 +He dissolves his self. No remorse. +.L 4 6 C +He dissolves his bond with his group. +Supreme good fortune. +Dispersion leads in turn to accumulation. +This is something that ordinary men do not think of. +.L 5 9 G +His loud cries are as dissolving as sweat. +Dissolution. A king abides without blame. +.L 6 9 +He dissolves his blood. +Departing, keeping at a distance, going out, +Is without blame. +.H 60 "Chieh" "Limitation" +.X 6 5 +.J +Limitation. Success. +Galling limitation must not be persevered in. +.I +Water over lake: the image of Limitation. +Thus the superior man +Creates number and measure, +And examines the nature of virtue and correct conduct. +.L 1 9 +Not going out of the door and the courtyard +Is without blame. +.L 2 9 +Not going out of the gate and the courtyard +Brings misfortune. +.L 3 6 +He who knows no limitation +Will have cause to lament. +No blame. +.L 4 6 +Contented limitation. Success. +.L 5 9 G +Sweet limitation brings good fortune. +Going brings esteem. +.L 6 6 +Galling limitation. +Perseverance brings misfortune. +Remorse disappears. +.H 61 "Chung Fu" "Inner Truth" +.X 2 5 +.J +Inner Truth. Pigs and fishes. +Good fortune. +It furthers one to cross the great water. +Perseverance furthers. +.I +Wind over lake: the image of Inner Truth. +Thus the superior man discusses criminal cases +In order to delay executions. +.L 1 9 +Being prepared brings good fortune. +If there are secret designs, it is disquieting. +.L 2 9 +A crane calling in the shade. +Its young answers it. +I have a good goblet. +I will share it with you. +.L 3 6 C +He finds a comrade. +Now he beats the drum, now he stops. +Now he sobs, now he sings. +.L 4 6 C +The moon nearly at the full. +The team horse goes astray. +No blame. +.L 5 9 G +He possesses truth, which links together. +No blame. +.L 6 9 +Cockcrow penetrating to heaven. +Perseverance brings misfortune. +.H 62 "Hsiao Kuo" "Preponderance of the Small" +.X 7 4 +.J +Preponderance of the Small. Success. +Perseverance furthers. +Small things may be done; great things should not be done. +The flying bird brings the message: +It is not well to strive upward, +It is well to remain below. +Great good fortune. +.I +Thunder on the mountain: +The image of Preponderance of the Small. +Thus in his conduct the superior man gives preponderance to reverence. +In bereavement he gives preponderance to grief. +In his expenditures he gives preponderance to thrift. +.L 1 6 +The bird meets with misfortune through flying. +.L 2 6 G +She passes by her ancestor +And meets her ancestress. +He does not reach his prince +And meets the official. +No blame. +.L 3 9 +If one is not extremely careful, +Somebody may come up from behind and strike him. +Misfortune. +.L 4 9 +No blame. He meets him without passing by. +Going brings danger. One must be on guard. +Do not act. Be constantly persevering. +.L 5 6 G +Dense clouds, +No rain from our western territory. +The prince shoots and hits him who is in the cave. +.L 6 6 +He passes him by, not meeting him. +The flying bird leaves him. +Misfortune. +This means bad luck and injury. +.H 63 "Chi Chi" "After Completion" +.X 6 3 +.J +After Completion. Success in small matters. +Perseverance furthers. +At the beginning good fortune, +At the end disorder. +.I +Water over fire: the image of the condition +In After Completion. +Thus the superior man +Takes thought of misfortune +And arms himself against it in advance. +.L 1 9 +He brakes his wheels. +He gets his tail in the water. +No blame. +.L 2 6 G +The woman loses the curtain of her carriage. +Do not run after it; +On the seventh day you will get it. +.L 3 9 +The Illustrious Ancestor +Disciplines the Devil's Country. +After three years he conquers it. +Inferior people must not be employed. +.L 4 6 +The finest clothes turn to rags. +Be careful all day long. +.L 5 9 +The neighbor in the east who slaughters an ox +Does not attain as much real happiness +As the neighbor in the west +With his small offering. +.L 6 6 +He gets his head in the water. Danger. +.H 64 "Wei Chi" "Before Completion" +.X 3 6 +.J +Before Completion. Success. +But if the little fox, after nearly completing the crossing, +Gets his tail in the water, +There is nothing that would further. +.I +Fire over water: +The image of the condition before transition. +Thus the superior man is careful +In the differentiation of things, +So that each finds its place. +.L 1 6 +He gets his tail in the water. +Humiliating. +.L 2 9 +He brakes his wheels. +Perseverance brings good fortune. +.L 3 6 +Before completion, attack brings misfortune. +It furthers one to cross the great water. +.L 4 9 +Perseverance brings good fortune. +Remorse disappears. +Shock, thus to discipline the Devil's Country. +For three years, great realms are awarded. +.L 5 6 G +Perseverance brings good fortune. +No remorse. +The light of the superior man is true. +Good fortune. +.L 6 9 +There is drinking of wine +In genuine confidence. No blame. +But if one wets his head, +He loses it, in truth. diff --git a/ching/ching/macros b/ching/ching/macros new file mode 100644 index 0000000..fa8a058 --- /dev/null +++ b/ching/ching/macros @@ -0,0 +1,126 @@ +.\" $NetBSD: macros,v 1.1 2005/06/30 13:30:33 perry Exp $ +.\" +.\" Copyright (c) Caldera International Inc. 2001-2002. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" +.\" 1. Redistributions of source code and documentation must retain the +.\" above copyright notice, this list of conditions and the following +.\" disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed or owned by Caldera +.\" International, Inc. +.\" 4. Neither the name of Caldera International, Inc. nor the names of +.\" other contributors may be used to endorse or promote products +.\" derived from this software without specific prior written permission. +.\" +.\" USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA +.\" INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +.\" DISCLAIMED. IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE +.\" FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +.\" BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +.\" WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +.\" OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +.\" IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.ds N6 Six +.ds N9 Nine +.ds L1 at the beginning +.ds L2 in the second place +.ds L3 in the third place +.ds L4 in the fourth place +.ds L5 in the fifth place +.ds L6 at the top +.ds GR () +.ds CR [] +.ds BL \l'2m'\h'1m'\l'2m' +.ds SL \l'5m' +.ds T1 Ch\'ien\ The Creative, Heaven +.ds T2 Sun\ \ \ \ The Gentle, Wind +.ds T3 Li\ \ \ \ \ The Clinging, Flame +.ds T4 K\o'^e'n\ \ \ \ Keeping Still, Mountain +.ds T5 Tui\ \ \ \ The Joyous, Lake +.ds T6 K\'an\ \ \ The Abysmal, Water +.ds T7 Ch\o'^e'n\ \ \ The Arousing, Thunder +.ds T8 K\'un\ \ \ The Receptive, Earth +.de H +.ds LH The Lines +.in 0 +.ta 0.5i 1.0i 1.5i 2.0i +.na +.nf +.sp 2 +\\$1. \\$2 / \\$3 +.. +.de X +.sp +.XX \\$1 "above" "\\*(T\\$1" +.XX \\$2 "below" "\\*(T\\$2" +.. +.de XX +.ie \\$1>4 \\*(BL +.el \\*(SL +.ie (\\$1-1%4)>1 \\*(BL\\c +.el \\*(SL\\c + \\$2 \\$3 +.ie \\$1%2 \\*(SL +.el \\*(BL +.. +.de J +.in 0 +.sp +The Judgement +.na +.nf +.in 0.5i +.sp +.. +.de I +.in 0 +.sp +The Image +.na +.nf +.sp +.in 0.5i +.. +.de LX +.in 0.5i +.ti -0.5i +.if '\\$3'G' \\{\\ +\\*(GR \\$1 \\$2 means:\\} +.if '\\$3'C' \\{\\ +\\*(CR \\$1 \\$2 means:\\} +.if '\\$3'' \\{\\ + \\$1 \\$2 means:\\} +.. +.de L +.if !'\\*(LH'' \\{\\ +.in 0 +.sp +\\*(LH +.rm LH +.in 0.5i\\} +.sp +.LX "\\*(N\\$2" "\\*(L\\$1" \\$3 +.na +.nf +.. +.de LA +.sp +.if '\\$1'6' .LX "When all the lines are" "sixes, it" +.if '\\$1'9' .LX "When all the lines are" "nines, it" +.na +.nf +.. +.po 0.5i diff --git a/ching/include/ching.h b/ching/include/ching.h new file mode 100644 index 0000000..9fcb92b --- /dev/null +++ b/ching/include/ching.h @@ -0,0 +1,44 @@ +/* $NetBSD: ching.h,v 1.1 2005/06/30 13:30:33 perry Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Guy Harris. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ching.h 8.1 (Berkeley) 5/31/93 + */ + +#define OYIN 6 /* yin (broken) moving to yang (solid) */ +#define YYANG 7 /* yang (solid) */ +#define YYIN 8 /* yin (broken) */ +#define OYANG 9 /* yang (solid) moving to yin (broken) */ diff --git a/ching/printching/Makefile b/ching/printching/Makefile new file mode 100644 index 0000000..8ccb39b --- /dev/null +++ b/ching/printching/Makefile @@ -0,0 +1,7 @@ +# $NetBSD: Makefile,v 1.1 2005/06/30 13:30:33 perry Exp $ + +PROG= printching +NOMAN= # defined +BINDIR= /usr/libexec/ching + +.include <bsd.prog.mk> diff --git a/ching/printching/pathnames.h b/ching/printching/pathnames.h new file mode 100644 index 0000000..d996266 --- /dev/null +++ b/ching/printching/pathnames.h @@ -0,0 +1,38 @@ +/* $NetBSD: pathnames.h,v 1.1 2005/06/30 13:30:33 perry Exp $ */ + +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pathnames.h 8.1 (Berkeley) 5/31/93 + */ + +#define _PATH_HEX "/usr/share/games/ching/hexagrams" diff --git a/ching/printching/printching.c b/ching/printching/printching.c new file mode 100644 index 0000000..bde1068 --- /dev/null +++ b/ching/printching/printching.c @@ -0,0 +1,326 @@ +/* $NetBSD: printching.c,v 1.5 2011/08/31 16:24:55 plunky Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Guy Harris. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1988, 1993\ + The Regents of the University of California. All rights reserved."); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)ching.phx.c 8.1 (Berkeley) 5/31/93"; +#else +__RCSID("$NetBSD: printching.c,v 1.5 2011/08/31 16:24:55 plunky Exp $"); +#endif +#endif /* not lint */ + +/* + * printching - Print NROFF/TROFF source of change, given the line values. + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "ching.h" +#include "pathnames.h" + +static int changes(void); +static int codem(int a); +static int doahex(void); +static void phx(int hexagram, int flag); + +static const struct { + int lines; /* encoded value of lines */ + int trinum; /* trigram number */ +} table[] = { + { 777, 0 }, /* 1 */ + { 887, 1 }, /* 4 */ + { 878, 2 }, /* 6 */ + { 788, 3 }, /* 7 */ + { 888, 4 }, /* 8 */ + { 778, 5 }, /* 5 */ + { 787, 6 }, /* 3 */ + { 877, 7 }, /* 2 */ +}; + +/* + * Gives hexagram number from two component trigrams. + */ +static const int crosstab[8][8] = { + {1, 34, 5, 26, 11, 9, 14, 43}, + {25, 51, 3, 27, 24, 42, 21, 17}, + {6, 40, 29, 4, 7, 59, 64, 47}, + {33, 62, 39, 52, 15, 53, 56, 31}, + {12, 16, 8, 23, 2, 20, 35, 45}, + {44, 32, 48, 18, 46, 57, 50, 28}, + {13, 55, 63, 22, 36, 37, 30, 49}, + {10, 54, 60, 41, 19, 61, 38, 58} +}; + +static int trigrams[6]; +static int moving[6]; + +static FILE *chingf; /* stream to read the hexagram file */ + +/*ARGSUSED*/ +int +main(int argc, char **argv) +{ + char *hexptr; /* pointer to string of lines */ + char hexstr[6+1]; /* buffer for reading lines in */ + int i; + + if (argc < 2) + hexptr = fgets(hexstr, 6+1, stdin); + else + hexptr = argv[1]; + if (hexptr == NULL || strlen(hexptr) != 6) { + fprintf(stderr, "What kind of a change is THAT?!?\n"); + exit(1); + } + for (i = 0; i < 6; i++) { + trigrams[i] = hexptr[i] - '0'; + if (trigrams[i] == 6 || trigrams[i] == 9) + moving[i] = 1; + else + moving[i] = 0; + } + if ((chingf = fopen(_PATH_HEX, "r")) == NULL) { + fprintf(stderr, "ching: can't read %s\n", _PATH_HEX); + exit(2); + } + phx(doahex(), 0); + if (changes()) + phx(doahex(), 1); + exit(0); +} + +/* + * Compute the hexagram number, given the trigrams. + */ +static int +doahex(void) +{ + int lower, upper; /* encoded values of lower and upper trigrams */ + int lnum = 0, unum = 0; /* indices of upper and lower trigrams */ + int i; + + lower = codem(0); + upper = codem(3); + for (i = 0; i < 8; i++) { + if (table[i].lines == lower) + lnum = table[i].trinum; + if (table[i].lines == upper) + unum = table[i].trinum; + } + return(crosstab[lnum][unum]); +} + +/* + * Encode a trigram as a 3-digit number; the digits, from left to right, + * represent the lines. 7 is a solid (yang) line, 8 is a broken (yin) line. + */ +static int +codem(int a) +{ + int code, i; + int factor[3]; + + factor[0] = 1; + factor[1] = 10; + factor[2] = 100; + code = 0; + + for (i = a; i < a + 3; i++) { + switch(trigrams[i]) { + + case YYANG: + case OYANG: + code += factor[i%3]*7; + break; + + case OYIN: + case YYIN: + code += factor[i%3]*8; + break; + } + } + return(code); +} + +/* + * Compute the changes based on moving lines; return 1 if any lines moved, + * 0 if no lines moved. + */ +static int +changes(void) +{ + int cflag; + int i; + + cflag = 0; + for (i = 0; i < 6; i++) { + if (trigrams[i] == OYIN) { + trigrams[i] = YYANG; + cflag++; + } else if (trigrams[i] == OYANG) { + trigrams[i] = YYIN; + cflag++; + } + } + return(cflag); +} + +/* + * Print the NROFF/TROFF source of a hexagram, given the hexagram number; + * if flag is 0, print the entire source; if flag is 1, ignore the meanings + * of the lines. + */ +static void +phx(int hexagram, int flag) +{ + char textln[128+1]; /* buffer for text line */ + char *lp; /* pointer into buffer */ + int thishex; /* number of hexagram just read */ + int lineno; /* number of line read in */ + int allmoving; /* 1 if all lines are moving */ + int i; + + /* + * Search for the hexagram; it begins with a line of the form + * .H <hexagram number> <other data>. + */ + rewind(chingf); + for (;;) { + if (fgets(textln, sizeof(textln), chingf) == NULL) { + fprintf(stderr, "ching: Hexagram %d missing\n", + hexagram); + exit(3); + } + lp = &textln[0]; + if (*lp++ != '.' || *lp++ != 'H') + continue; + while (*lp++ == ' ') + ; + lp--; + thishex = atoi(lp); + if (thishex < 1 || thishex > 64) + continue; + if (thishex == hexagram) + break; + } + + /* + * Print up to the line commentary, which ends with a line of the form + * .L <position> <value> + */ + fputs(textln, stdout); + for (;;) { + if (fgets(textln, sizeof(textln), chingf) == NULL) { + fprintf(stderr, "ching: Hexagram %d malformed\n", + hexagram); + exit(3); + } + lp = &textln[0]; + if (*lp++ == '.') { + if (*lp++ == 'L') + break; + } + fputs(textln, stdout); + } + + /* + * Now print the line commentaries, if this is the first hexagram. + */ + if (flag) + return; + + /* + * If a line is moving, print its commentary. + * The text of the commentary ends with a line either of the form + * .L <position> <value> + * or of the form + * .LA <value> + * or of the form + * .H <hexagram number> <other arguments> + */ + allmoving = 1; + for (i = 0; i < 6; i++) { + while (*lp++ == ' ') + ; + lp--; + lineno = atoi(lp); + if (i + 1 != lineno) { + fprintf(stderr, "ching: Hexagram %d malformed\n", + hexagram); + exit(3); + } + if (moving[i]) + fputs(textln, stdout); + else + allmoving = 0; + for (;;) { + if (fgets(textln, sizeof(textln), chingf) == NULL) + break; + lp = &textln[0]; + if (*lp++ == '.' && (*lp == 'L' || *lp == 'H')) { + lp++; + break; + } + if (moving[i]) + fputs(textln, stdout); + } + } + + /* + * If all the lines are moving, print the commentary for that; it + * ends with a line of the form + * .H <hexagram number> <other arguments> + */ + if (*lp == 'A' && allmoving) { + fputs(textln, stdout); + for (;;) { + if (fgets(textln, sizeof(textln), chingf) == NULL) + break; + lp = &textln[0]; + if (*lp++ == '.' || *lp++ == 'H') + break; + fputs(textln, stdout); + } + } +} diff --git a/colorbars/Makefile b/colorbars/Makefile new file mode 100644 index 0000000..09b228f --- /dev/null +++ b/colorbars/Makefile @@ -0,0 +1,9 @@ +# $NetBSD: Makefile,v 1.4 2013/12/07 02:24:12 dholland Exp $ + +PROG= colorbars +MAN= colorbars.6 +DPADD= ${LIBCURSES} ${LIBTERMINFO} +# 20150209 bkw: remove -lterminfo +LDADD= -lcurses + +.include <bsd.prog.mk> diff --git a/colorbars/colorbars.6 b/colorbars/colorbars.6 new file mode 100644 index 0000000..af1fa92 --- /dev/null +++ b/colorbars/colorbars.6 @@ -0,0 +1,47 @@ +.\" $NetBSD: colorbars.6,v 1.3 2012/06/09 23:15:13 njoly Exp $ +.\" +.\" Copyright (c) 2012 Nathanial Sloss <nathanialsloss@yahoo.com.au> +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd June 5, 2012 +.Dt COLORBARS 6 +.Os +.Sh NAME +.Nm colorbars +.Nd display ANSI color bars +.Sh SYNOPSIS +.Nm +.Sh DESCRIPTION +The +.Nm +command displays ANSI color bars on a color capable terminal using +.Xr curses 3 . +.Sh SEE ALSO +.Xr curses 3 +.Sh HISTORY +.Nm +appeared in +.Nx 7.0 . +.Sh AUTHORS +Nathanial Sloss diff --git a/colorbars/colorbars.c b/colorbars/colorbars.c new file mode 100644 index 0000000..d3de2a4 --- /dev/null +++ b/colorbars/colorbars.c @@ -0,0 +1,124 @@ +/* $NetBSD: colorbars.c,v 1.1 2012/06/06 00:13:36 christos Exp $ */ + +/*- + * Copyright (c) 2012 Nathanial Sloss <nathanialsloss@yahoo.com.au> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include <sys/cdefs.h> +__RCSID("$NetBSD: colorbars.c,v 1.1 2012/06/06 00:13:36 christos Exp $"); + +#include <curses.h> +#include <stdio.h> +#include <string.h> +#include <err.h> +#include <stdlib.h> + +int +main(void) +{ + static struct colorInfo { + const char *name; + int color; + } colorInfo[] = { + { "Black", COLOR_BLACK }, + { "Red", COLOR_RED }, + { "Green", COLOR_GREEN }, + { "Yellow", COLOR_YELLOW }, + { "Blue", COLOR_BLUE }, + { "Magenta", COLOR_MAGENTA }, + { "Cyan", COLOR_CYAN }, + { "White", COLOR_WHITE }, + }; + size_t lengths[__arraycount(colorInfo)]; + + static const size_t numcolors = __arraycount(colorInfo); + size_t labelwidth; + + int colorOK; + int spacing, offsetx, labeloffsety, labeloffsetx; + + if (!initscr()) + errx(EXIT_FAILURE, "Cannot initialize curses"); + + colorOK = has_colors(); + if (!colorOK) { + endwin(); + errx(EXIT_FAILURE, "Terminal cannot display color"); + } + + if (COLS < 45 || LINES < 10) { + endwin(); + errx(EXIT_FAILURE, "Terminal size must be at least 45x10."); + } + + spacing = COLS / numcolors; + offsetx = (COLS - (spacing * numcolors)) / 2; + + + start_color(); + + labelwidth = 0; + for (size_t i = 0; i < numcolors; i++) { + lengths[i] = strlen(colorInfo[i].name); + if (lengths[i] > labelwidth) + labelwidth = lengths[i]; + init_pair(i, COLOR_WHITE, colorInfo[i].color); + } + + labeloffsetx = spacing / 2; + labeloffsety = (LINES - 1 - labelwidth) / 2; + clear(); + + move(0, 0); + + for (size_t i = 0; i < numcolors; i++) { + int xoffs = offsetx + spacing * i; + + attrset(COLOR_PAIR(i)); + + for (int line = 0; line < LINES - 1; line++) + for (int xpos = 0; xpos < spacing; xpos++) + mvprintw(line, xoffs + xpos, " "); + + attrset(COLOR_PAIR(0)); + + xoffs += labeloffsetx; + for (size_t line = 0; line < lengths[i]; line++) + mvprintw(line + labeloffsety, xoffs, "%c", + colorInfo[i].name[line]); + } + + attrset(COLOR_PAIR(0)); + + mvprintw(LINES - 1, 0, "ANSI Color chart - Press any key to exit: "); + + refresh(); + + getch(); + + endwin(); + + return EXIT_SUCCESS; +} + diff --git a/dab/Makefile b/dab/Makefile new file mode 100644 index 0000000..03491e3 --- /dev/null +++ b/dab/Makefile @@ -0,0 +1,14 @@ +# $NetBSD: Makefile,v 1.7 2010/02/03 15:34:38 roy Exp $ + +DPADD+=${LIBCURSES} ${LIBTERMINFO} ${LIBM} +LDADD+=-lcurses -lm + +# 20150209 bkw: hackery because slack's old pmake doesn't +# support PROG_CXX. +CC=c++ +PROG=dab +MAN=dab.6 +SRCS=algor.cc board.cc main.cc human.cc box.cc player.cc gamescreen.cc \ + ttyscrn.cc random.cc + +.include <bsd.prog.mk> diff --git a/dab/algor.cc b/dab/algor.cc new file mode 100644 index 0000000..86fc6e1 --- /dev/null +++ b/dab/algor.cc @@ -0,0 +1,310 @@ +/* $NetBSD: algor.cc,v 1.5 2012/02/29 23:39:53 joerg Exp $ */ + +/*- + * Copyright (c) 2003 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * algor.C: Computer algorithm + */ +#include "defs.h" +RCSID("$NetBSD: algor.cc,v 1.5 2012/02/29 23:39:53 joerg Exp $") + +#include "algor.h" +#include "board.h" +#include "box.h" +#include "random.h" + +ALGOR::ALGOR(const char c) : PLAYER(c) +{ +#ifdef notyet + // Single Edges = (x + y) * 2 + _edge1 = (_b.nx() * _b.ny()) * 2; + // Shared Edges = (x * (y - 1)) + ((x - 1) * y) + _edge2 = (_b.nx() * (_b.ny() - 1)) + ((_b.nx() - 1) * _b.ny()); + // Maximum Edges filled before closure = x * y * 2 + _maxedge = _b.nx() * _b.ny() * 2; +#endif +} + +// Find the first closure, i.e. a box that has 3 edges +int ALGOR::find_closure(size_t& y, size_t& x, int& dir, BOARD& b) +{ + RANDOM rdy(b.ny()), rdx(b.nx()); + + for (y = rdy(); y < b.ny(); y = rdy()) { + rdx.clear(); + for (x = rdx(); x < b.nx(); x = rdx()) { + BOX box(y, x, b); + if (box.count() == 3) { + for (dir = BOX::first; dir < BOX::last; dir++) + if (!box.isset(dir)) + return 1; + b.abort("find_closure: 3 sided box[%zu,%zu] has no free sides", + y, x); + } + } + } + return 0; +} + +#if 0 +size_t ALGOR::find_single() +{ + size_t ne; + + // Find the number of single edges in use + for (size_t x = 0; x < b.nx(); x++) { + BOX tbox(0, x, b); + ne += tbox.isset(BOX::top); + BOX bbox(b.ny() - 1, x, b); + ne += bbox.isset(BOX::bottom); + } + for (size_t y = 0; y < _b.ny(); y++) { + BOX lbox(y, 0, b); + ne += lbox.isset(BOX::left); + BOX rbox(y,_b.nx() - 1, b); + ne += rbox.isset(BOX::right); + } + return ne; +} +#endif + + +// Count a closure, by counting all boxes that we can close in the current +// move +size_t ALGOR::count_closure(size_t& y, size_t& x, int& dir, BOARD& b) +{ + size_t i = 0; + size_t tx, ty; + int tdir, mv; + + while (find_closure(ty, tx, tdir, b)) { + if (i == 0) { + // Mark the beginning of the closure + x = tx; + y = ty; + dir = tdir; + } + if ((mv = b.domove(ty, tx, tdir, getWho())) == -1) + b.abort("count_closure: Invalid move (%zu, %zu, %d)", y, x, dir); + else + i += mv; + } + return i; +} + + +/* + * Find the largest closure, by closing all possible closures. + * return the number of boxes closed in the maximum closure, + * and the first box of the maximum closure in (x, y, dir) + */ +size_t ALGOR::find_max_closure(size_t& y, size_t& x, int& dir, const BOARD& b) +{ + BOARD nb(b); + int maxdir = -1; + size_t nbox, maxbox = 0; + size_t maxx = ~0, maxy = ~0; + size_t tx = 0, ty = 0; /* XXX: GCC */ + int tdir = 0; /* XXX: GCC */ + + while ((nbox = count_closure(ty, tx, tdir, nb)) != 0) + if (nbox > maxbox) { + // This closure is better, update max + maxbox = nbox; + maxx = tx; + maxy = ty; + maxdir = tdir; + } + + // Return the max found + y = maxy; + x = maxx; + dir = maxdir; + return maxbox; +} + + +// Find if a turn does not result in a capture on the given box +// and return the direction if found. +int ALGOR::try_good_turn(BOX& box, size_t y, size_t x, int& dir, BOARD& b) +{ + // Sanity check; we must have a good box + if (box.count() >= 2) + b.abort("try_good_turn: box[%zu,%zu] has more than 2 sides occupied", + y, x); + + // Make sure we don't make a closure in an adjacent box. + // We use a random direction to randomize the game + RANDOM rd(BOX::last); + for (dir = rd(); dir < BOX::last; dir = rd()) + if (!box.isset(dir)) { + size_t by = y + BOX::edges[dir].y; + size_t bx = x + BOX::edges[dir].x; + if (!b.bounds(by, bx)) + return 1; + + BOX nbox(by, bx, b); + if (nbox.count() < 2) + return 1; + } + + return 0; +} + + +// Try to find a turn that does not result in an opponent closure, and +// return it in (x, y, dir); if not found return 0. +int ALGOR::find_good_turn(size_t& y, size_t& x, int& dir, const BOARD& b) +{ + BOARD nb(b); + RANDOM rdy(b.ny()), rdx(b.nx()); + + for (y = rdy(); y < b.ny(); y = rdy()) { + rdx.clear(); + for (x = rdx(); x < b.nx(); x = rdx()) { + BOX box(y, x, nb); + if (box.count() < 2 && try_good_turn(box, y, x, dir, nb)) + return 1; + } + } + return 0; +} + +// On a box with 2 edges, return the first or the last free edge, depending +// on the order specified +int ALGOR::try_bad_turn(BOX& box, size_t& y, size_t& x, int& dir, BOARD& b, + int last) +{ + if (4 - box.count() <= last) + b.abort("try_bad_turn: Called at [%zu,%zu] for %d with %d", + y, x, last, box.count()); + for (dir = BOX::first; dir < BOX::last; dir++) + if (!box.isset(dir)) { + if (!last) + return 1; + else + last--; + } + return 0; +} + +// Find a box that has 2 edges and return the first free edge of that +// box or the last free edge of that box +int ALGOR::find_bad_turn(size_t& y, size_t& x, int& dir, BOARD& b, int last) +{ + RANDOM rdy(b.ny()), rdx(b.nx()); + for (y = rdy(); y < b.ny(); y = rdy()) { + rdx.clear(); + for (x = rdx(); x < b.nx(); x = rdx()) { + BOX box(y, x, b); + if ((4 - box.count()) > last && + try_bad_turn(box, y, x, dir, b, last)) + return 1; + } + } + return 0; +} + +size_t ALGOR::find_min_closure1(size_t& y, size_t& x, int& dir, const BOARD& b, + int last) +{ + BOARD nb(b); + int tdir, mindir = -1, mv; + // number of boxes per closure + size_t nbox, minbox = nb.nx() * nb.ny() + 1; + size_t tx, ty, minx = ~0, miny = ~0; + int xdir = 0; /* XXX: GCC */ + + while (find_bad_turn(ty, tx, tdir, nb, last)) { + + // Play a bad move that would cause the opponent's closure + if ((mv = nb.domove(ty, tx, tdir, getWho())) != 0) + b.abort("find_min_closure1: Invalid move %d (%zu, %zu, %d)", mv, + ty, tx, tdir); + + // Count the opponent's closure + if ((nbox = count_closure(y, x, xdir, nb)) == 0) + b.abort("find_min_closure1: no closure found"); + + if (nbox <= minbox) { + // This closure has fewer boxes + minbox = nbox; + minx = tx; + miny = ty; + mindir = tdir; + } + } + + y = miny; + x = minx; + dir = mindir; + return minbox; +} + + +// Search for the move that makes the opponent close the least number of +// boxes; returns 1 if a move found, 0 otherwise +size_t ALGOR::find_min_closure(size_t& y, size_t& x, int& dir, const BOARD& b) +{ + size_t x1, y1; + int dir1; + size_t count = b.ny() * b.nx() + 1, count1; + + for (size_t i = 0; i < 3; i++) + if (count > (count1 = find_min_closure1(y1, x1, dir1, b, i))) { + count = count1; + y = y1; + x = x1; + dir = dir1; + } + + return count != b.ny() * b.nx() + 1; +} + +// Return a move in (y, x, dir) +void ALGOR::play(const BOARD& b, size_t& y, size_t& x, int& dir) +{ + // See if we can close the largest closure available + if (find_max_closure(y, x, dir, b)) + return; + +#ifdef notyet + size_t sgl = find_single(); + size_t dbl = find_double(); +#endif + + // See if we can play an edge without giving the opponent a box + if (find_good_turn(y, x, dir, b)) + return; + + // Too bad, find the move that gives the opponent the fewer boxes + if (find_min_closure(y, x, dir, b)) + return; +} diff --git a/dab/algor.h b/dab/algor.h new file mode 100644 index 0000000..00b3c25 --- /dev/null +++ b/dab/algor.h @@ -0,0 +1,76 @@ +/* $NetBSD: algor.h,v 1.5 2012/06/15 10:51:25 joerg Exp $ */ + +/*- + * Copyright (c) 2003 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * algor.h: Computer's algorithm + */ + +#ifndef _H_ALGOR +#define _H_ALGOR + +#include "player.h" + +class BOARD; +class BOX; + +class ALGOR : public PLAYER { + public: + ALGOR(const char c); + virtual ~ALGOR() {} + // Return a proposed move in (y, x, dir) + void play(const BOARD& b, size_t& y, size_t& x, int& dir); + + private: + // Closure searches + int find_closure(size_t& y, size_t& x, int& dir, BOARD& b); + size_t find_max_closure(size_t& y, size_t& x, int& dir, const BOARD& b); + size_t find_min_closure1(size_t& y, size_t& x, int& dir, const BOARD& b, + int last); + size_t find_min_closure(size_t& y, size_t& x, int& dir, const BOARD& b); + + // Move searches + int find_good_turn(size_t& y, size_t& x, int& dir, const BOARD& b); + int find_bad_turn(size_t& y, size_t& x, int& dir, BOARD& b, int last); + + // Move Attempts + int try_bad_turn(BOX& box, size_t& y, size_t& x, int& dir, BOARD& b, + int last); + int try_good_turn(BOX& box, size_t y, size_t x, int& dir, BOARD& b); + + // Utils + size_t count_closure(size_t& y, size_t& x, int& dir, BOARD& b); + +#ifdef notyet + size_t find_single(void); +#endif +}; + +#endif diff --git a/dab/board.cc b/dab/board.cc new file mode 100644 index 0000000..b8340a0 --- /dev/null +++ b/dab/board.cc @@ -0,0 +1,253 @@ +/* $NetBSD: board.cc,v 1.4 2008/04/28 20:22:53 martin Exp $ */ + +/*- + * Copyright (c) 2003 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * board.C: Board manipulations + */ +#include "defs.h" +RCSID("$NetBSD: board.cc,v 1.4 2008/04/28 20:22:53 martin Exp $") + +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include "board.h" +#include "gamescreen.h" +#include "box.h" +#include "player.h" + +BOARD::BOARD(size_t y, size_t x, GAMESCREEN* scrn) : + _ny(y), + _nx(x), + _scrn(scrn) +{ + _ty = 2 * _ny + 1; + _tx = 2 * _nx + 1; + + _b = new int*[_ty]; + + for (y = 0; y < _ty; y++) + _b[y] = new int[_tx]; + + init(); +} + +BOARD::BOARD(const BOARD& b) : + _ty(b._ty), + _tx(b._tx), + _ny(b._ny), + _nx(b._nx), + _scrn(NULL) +{ + _b = new int*[_ty]; + + for (size_t y = 0; y < _ty; y++) { + _b[y] = new int[_tx]; + static_cast<void>(memcpy(_b[y], b._b[y], _tx * sizeof(int))); + } +} + +BOARD::~BOARD() +{ + size_t y; + + for (y = 0; y < _ty; y++) + delete[] _b[y]; + + delete[] _b; +} + +// Clear all boxes and reset state for a new game +void BOARD::init(void) +{ + size_t x, y; + + for (y = 0; y < _ny; y++) + for (x = 0; x < _nx; x++) { + BOX box(y, x, *this); + box.reset(); + } +} + +/* + * Make a move for player with initial 'c', adding an edge at box(x, y) + * and the specified direction. + * returns: + * -1: Invalid move + * n: Number of closures n E [0..2] + */ +int BOARD::domove(size_t y, size_t x, int dir, char c) +{ + int closed = 0; + + // Check if out of bounds + if (!bounds(y, x)) + return -1; + + BOX box1(y, x, *this); + + // Check if the edge is already there + if (box1.isset(dir)) + return -1; + + box1.set(dir); + + if (box1.count() == 4) { + // New box; name it and count it + box1.name() = c; + closed++; + } + + box1.paint(); + + // Check other box + x += BOX::edges[dir].x; + y += BOX::edges[dir].y; + + if (bounds(y, x)) { + BOX box2(y, x, *this); + if (box2.count() == 4) { + box2.name() = c; + box2.paint(); + closed++; + } + } + return closed; +} + +// Return true if the board is full +int BOARD::full(void) const +{ + for (size_t y = 0; y < _ny; y++) + for (size_t x = 0; x < _nx; x++) { + BOX box(y, x, const_cast<BOARD&>(*this)); + if (box.count() != 4) + return 0; + } + return 1; +} + +// Return if the coordinates are within bounds; we don't check for < 0, +// since size_t is unsigned +int BOARD::bounds(size_t y, size_t x) const +{ + return x < _nx && y < _ny; +} + +// Paint all boxes, effectively redrawing the board +void BOARD::paint(void) const +{ + for (size_t y = 0; y < _ny; y++) + for (size_t x = 0; x < _nx; x++) { + BOX box(y, x, const_cast<BOARD&>(*this)); + box.paint(); + } +} + +// Clear the screen +void BOARD::clean(void) const +{ + if (!_scrn) + return; + _scrn->clean(); +} + +// Move cursor to x, y +void BOARD::setpos(size_t y, size_t x) const +{ + if (!_scrn) + return; + _scrn->moveto(y, x); + _scrn->redraw(); +} + +// Return character indicating move +int BOARD::getmove(void) const +{ + if (!_scrn) + return 'q'; + _scrn->redraw(); + return _scrn->getinput(); +} + +// Ring the bell +void BOARD::bell(void) const +{ + if (!_scrn) + return; + _scrn->bell(); +} + +// Post the score in the current game for player i +void BOARD::score(size_t i, const PLAYER& p) +{ + if (_scrn == NULL) + return; + _scrn->score(i, p); +} + +// Post the number of games won for player i +void BOARD::games(size_t i, const PLAYER& p) +{ + if (_scrn == NULL) + return; + _scrn->games(i, p); +} + +// Post the total score for player i +void BOARD::total(size_t i, const PLAYER& p) +{ + if (_scrn == NULL) + return; + _scrn->total(i, p); +} + +// Post the total score for player i +void BOARD::ties(const PLAYER& p) +{ + if (_scrn == NULL) + return; + _scrn->ties(p); +} + +// Internal algorithm error; post and abort +void BOARD::abort(const char* s, ...) const +{ + for (size_t i = 0; i < _ny; i++) + fprintf(stderr, "\n"); + + va_list ap; + fprintf(stderr, "Algorithm internal error: "); + va_start(ap, s); + vfprintf(stderr, s, ap); + va_end(ap); + fprintf(stderr, "\n"); + ::abort(); +} diff --git a/dab/board.h b/dab/board.h new file mode 100644 index 0000000..67dc3e0 --- /dev/null +++ b/dab/board.h @@ -0,0 +1,86 @@ +/* $NetBSD: board.h,v 1.4 2012/02/29 23:39:53 joerg Exp $ */ + +/*- + * Copyright (c) 2003 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * board.h: Board functions + */ + +#ifndef _H_BOARD +#define _H_BOARD + +#include <stdlib.h> + +class GAMESCREEN; +class PLAYER; + +class BOARD { + public: + // Constructors and destructor + BOARD(size_t y, size_t x, GAMESCREEN* scrn);// For the main screen + BOARD(const BOARD& b); // For scratch screens + ~BOARD(); + + // member access + size_t nx(void) const { return _nx; } + size_t ny(void) const { return _ny; } + size_t tx(void) const { return _tx; } + size_t ty(void) const { return _ty; } + GAMESCREEN* getScrn(void) const { return _scrn; } + int& data(size_t y, size_t x) { return _b[y][x]; } + + // Computing + int domove(size_t y, size_t x, int dir, char c); // Play move + void init(void); // Initialize a new game + int full(void) const; // True if no more moves + int bounds(size_t y, size_t x) const; // True if in bounds + + // Screen updates + void paint(void) const; // Redraw screen + void clean(void) const; // Clear screen + void setpos(size_t y, size_t x) const; // move cursor to pos + int getmove(void) const; // Return move + void bell(void) const; // Beep! + void score(size_t i, const PLAYER& p); // Post score + void games(size_t i, const PLAYER& p); // Post games + void total(size_t i, const PLAYER& p); // Post totals + void ties(const PLAYER& p); // Post ties + __printflike(2, 3) __dead + void abort(const char *s, ...) const; // Algorithm error + + + private: + size_t _ty, _tx; // number of symbols in x and y dimension + size_t _ny, _nx; // number of boxes in the x and y dimension + int** _b; // board array of symbols + GAMESCREEN* _scrn; // screen access, if we have one +}; + +#endif diff --git a/dab/box.cc b/dab/box.cc new file mode 100644 index 0000000..1a00c1d --- /dev/null +++ b/dab/box.cc @@ -0,0 +1,150 @@ +/* $NetBSD: box.cc,v 1.3 2008/04/28 20:22:53 martin Exp $ */ + +/*- + * Copyright (c) 2003 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * box.C: Box computations + */ +#include "defs.h" +RCSID("$NetBSD: box.cc,v 1.3 2008/04/28 20:22:53 martin Exp $") + +#include "box.h" +#include "board.h" +#include "gamescreen.h" +#include <curses.h> + +const POINT BOX::edges[BOX::last] = + { { 0, -1 }, { 0, 1 }, { -1, 0 }, { 1, 0 } }; +const POINT BOX::corners[BOX::last] = + { { -1, -1 }, { -1, 1 }, { 1, -1 }, { 1, 1 } }; +const int BOX::syms[BOX::last] = + { GAMESCREEN::GS_HLINE, GAMESCREEN::GS_HLINE, + GAMESCREEN::GS_VLINE, GAMESCREEN::GS_VLINE }; + +BOX::BOX(size_t py, size_t px, BOARD& b) : + _b(b) +{ + _centery = py * 2 + 1; + _centerx = px * 2 + 1; +} + +void BOX::addcorner(size_t y, size_t x) +{ + char sym; + _b.getScrn()->moveto(y, x); + if (x == 0) { + if (y == 0) + sym = GAMESCREEN::GS_ULCORNER; + else if (y == _b.ty() - 1) + sym = GAMESCREEN::GS_LLCORNER; + else + sym = GAMESCREEN::GS_LTEE; + } else if (x == _b.tx() - 1) { + if (y == 0) + sym = GAMESCREEN::GS_URCORNER; + else if (y == _b.ty() - 1) + sym = GAMESCREEN::GS_LRCORNER; + else + sym = GAMESCREEN::GS_RTEE; + } else if (y == 0) + sym = GAMESCREEN::GS_TTEE; + else if (y == _b.ty() - 1) + sym = GAMESCREEN::GS_BTEE; + else + sym = GAMESCREEN::GS_PLUS; + + _b.getScrn()->addedge(sym); +} + +// Paint a box +void BOX::paint(void) +{ + int e; + if (_b.getScrn() == NULL) + return; + + _b.getScrn()->moveto(_centery, _centerx); + _b.getScrn()->addsym(name()); + + for (e = BOX::first; e < BOX::last; e++) { + addcorner(_centery + corners[e].y, _centerx + corners[e].x); + _b.getScrn()->moveto(_centery + edges[e].y, _centerx + edges[e].x); + _b.getScrn()->addedge(edge(static_cast<EDGE>(e))); + } + _b.getScrn()->redraw(); +} + +// Return the name +int& BOX::name(void) +{ + return _b.data(_centery, _centerx); +} + +// Set an edge +void BOX::set(int e) +{ + _b.data(_centery + edges[e].y, _centerx + edges[e].x) = syms[e]; +} + +// Clear an edge +void BOX::clr(int e) +{ + _b.data(_centery + edges[e].y, _centerx + edges[e].x) = ' '; +} + +// Test an edge +int BOX::isset(int e) const +{ + return _b.data(_centery + edges[e].y, _centerx + edges[e].x) != ' '; +} + +// Return the edge +int& BOX::edge(int e) +{ + return _b.data(_centery + edges[e].y, _centerx + edges[e].x); +} + +// Count the number of edges set in the box +int BOX::count(void) const +{ + int cnt = 0; + + for (int e = BOX::first; e < BOX::last; e++) + cnt += isset(static_cast<EDGE>(e)); + return cnt; +} + +// Clear the box +void BOX::reset(void) +{ + for (int e = BOX::first; e < BOX::last; e++) + clr(static_cast<EDGE>(e)); + name() = ' '; +} diff --git a/dab/box.h b/dab/box.h new file mode 100644 index 0000000..fd13bde --- /dev/null +++ b/dab/box.h @@ -0,0 +1,93 @@ +/* $NetBSD: box.h,v 1.2 2008/04/28 20:22:53 martin Exp $ */ + +/*- + * Copyright (c) 2003 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * box.C: Single box utilities; A box is an entity with four edges, four + * corners, and a center that maps directly to a board + */ + +#ifndef _H_BOX +#define _H_BOX + +#include <stdlib.h> + +class BOARD; + +class POINT { + public: + int x; + int y; +}; + +class BOX { + public: + enum EDGE { + first = 0, + top = 0, + bottom = 1, + left = 2, + right = 3, + last = 4, + }; + + BOX(size_t py, size_t px, BOARD& b); + + void reset(void); // Clear a box + void paint(void); // Paint a box + + // Member access + int& name(void); + int& edge(int e); + + // Edge maniputations + void set(int e); + void clr(int e); + int isset(int e) const; + + int count(void) const; // Count the number of edges in use + + // Useful constants + // Relative coordinates of the edges from the center of the box. + static const POINT edges[BOX::last]; + // Relative coordinates of the corners from the center of the box. + static const POINT corners[BOX::last]; + // Character symbols of the four edges + static const int syms[BOX::last]; + + private: + void addcorner(size_t y, size_t x); // add a corner character + + size_t _centerx; // Coordinates of the center in board units + size_t _centery; + BOARD& _b; // The board we refer to +}; + +#endif diff --git a/dab/dab.6 b/dab/dab.6 new file mode 100644 index 0000000..039c79a --- /dev/null +++ b/dab/dab.6 @@ -0,0 +1,112 @@ +.\" $NetBSD: dab.6,v 1.6 2012/10/06 19:39:51 christos Exp $ +.\" +.\" Copyright (c) 2003 Thomas Klausner. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd October 7, 2012 +.Dt DAB 6 +.Os +.Sh NAME +.Nm dab +.Nd Dots and Boxes game +.Sh SYNOPSIS +.Nm +.Op Fl aw +.Op Fl n Ar ngames +.Op Fl p Ao Ar c|h Ac Ns Ao Ar c|h Ac +.Op Ar xdim Oo Ar ydim Oc +.Sh DESCRIPTION +.Nm +is a game where each player tries to complete the most +boxes. +A turn consists of putting one border of a box; the player +setting the fourth and final border of a box gets the +point for the box and has another turn. +.Pp +The keys used are the vi keys: +.Ic k +for up, +.Ic j +for down, +.Ic h +for left, and +.Ic l +for right. +To switch between even and odd rows, use one of the following +keys: +.Ic u +.Pq diagonal right up , +.Ic y +.Pq diagonal left up , +.Ic b +.Pq diagonal left down , +.Ic n +.Pq diagonal right down ; +.Aq Ic space +sets a new border, +.Ic CTRL-L +and +.Ic CTRL-R +redraw the screen, and +.Ic q +quits the game. +.Pp +Support options are: +.Bl -tag -width XXnXngamesXXXXX +.It Fl a +Don't use the alternate character set. +.It Fl n Ar ngames +.Ar ngames +games will be played. +.Pq Especially useful in Fl p Ar cc No mode. +.It Fl p Ao Ar c|h Ac Ns Ao Ar c|h Ac +Select which of the two players is a human +or a computer. +The first argument is the first player; +.Ic c +stands for computer and +.Ic h +for human. +.It Fl w +Wait for a character press between games. +.El +.Pp +.Ar xdim +and +.Ar ydim +define the size of the board in the x and y +dimensions. +If the dimensions specified are +.Dv 0 +then the maximum dimensions for the size of the screen are +used. +.Sh SEE ALSO +.Rs +.%A Elwyn R. Berlekamp +.%T The Dots and Boxes Game: Sophisticated Child's Play +.%D 2000 +.%I A K Peters +.%U http://www.akpeters.com/book.asp?bID=111 +.Re +.Sh AUTHORS +.An Christos Zoulas +.Aq christos@NetBSD.org diff --git a/dab/defs.h b/dab/defs.h new file mode 100644 index 0000000..23ebbe5 --- /dev/null +++ b/dab/defs.h @@ -0,0 +1,43 @@ +/* $NetBSD: defs.h,v 1.3 2011/05/23 23:06:41 joerg Exp $ */ + +/*- + * Copyright (c) 2003 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * defs.h: Macro defines + */ + +#ifndef _H_DEFS +#define _H_DEFS + +#include <sys/cdefs.h> + +#define RCSID(id) __RCSID(id); + +#endif diff --git a/dab/gamescreen.cc b/dab/gamescreen.cc new file mode 100644 index 0000000..a86ac71 --- /dev/null +++ b/dab/gamescreen.cc @@ -0,0 +1,43 @@ +/* $NetBSD: gamescreen.cc,v 1.2 2008/04/28 20:22:53 martin Exp $ */ + +/*- + * Copyright (c) 2003 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * gamescreen.C: Common screen functions + */ +#include "defs.h" +RCSID("$NetBSD: gamescreen.cc,v 1.2 2008/04/28 20:22:53 martin Exp $") + +#include "gamescreen.h" + +// Nothing to do +GAMESCREEN::~GAMESCREEN() +{ +} diff --git a/dab/gamescreen.h b/dab/gamescreen.h new file mode 100644 index 0000000..c52c85b --- /dev/null +++ b/dab/gamescreen.h @@ -0,0 +1,72 @@ +/* $NetBSD: gamescreen.h,v 1.3 2010/12/08 17:08:07 joerg Exp $ */ + +/*- + * Copyright (c) 2003 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * screen.h: Screen base class + */ + +#ifndef _H_GAMESCREEN +#define _H_GAMESCREEN + +#include <stdlib.h> + +class PLAYER; + +class GAMESCREEN { + public: + enum EDGE { + GS_HLINE, + GS_VLINE, + GS_ULCORNER, + GS_URCORNER, + GS_LLCORNER, + GS_LRCORNER, + GS_LTEE, + GS_RTEE, + GS_TTEE, + GS_BTEE, + GS_PLUS + }; + virtual ~GAMESCREEN(); + virtual void clean(void) = 0; // Clear screen + virtual void moveto(size_t y, size_t x) = 0; // Move to x, y + virtual void addsym(const int sym) = 0; // Add character symbol + virtual void addedge(const int sym) = 0; // Add character symbol + virtual void redraw(void) = 0; // Refresh + virtual int getinput(void) = 0; // Get user input + virtual void bell(void) = 0; // Beep + virtual void score(size_t l, const PLAYER& p) = 0; // Post current score + virtual void games(size_t l, const PLAYER& p) = 0; // Post games won + virtual void total(size_t l, const PLAYER& p) = 0; // Post total score + virtual void ties(const PLAYER& p) = 0; // Post tie games +}; + +#endif diff --git a/dab/human.cc b/dab/human.cc new file mode 100644 index 0000000..1c3e3e1 --- /dev/null +++ b/dab/human.cc @@ -0,0 +1,149 @@ +/* $NetBSD: human.cc,v 1.3 2008/04/28 20:22:54 martin Exp $ */ + +/*- + * Copyright (c) 2003 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * human.C: Human interface for dots, using rogue-like keys. + */ +#include "defs.h" +RCSID("$NetBSD: human.cc,v 1.3 2008/04/28 20:22:54 martin Exp $") + +#include "human.h" +#include "board.h" +#include "box.h" +#include "ttyscrn.h" + +#define CONTROL(a) ((a) & 037) + +extern GAMESCREEN *sc; + +HUMAN::HUMAN(const char c) : + PLAYER(c), + _curx(0), + _cury(1) +{ +} + +void HUMAN::play(const BOARD& b, size_t& y, size_t& x, int& dir) +{ + int mv; + b.setpos(_cury, _curx); + + for (;;) { + switch (mv = b.getmove()) { + case 'h': case 'H': + _curx -= 2; + break; + + case 'l': case 'L': + _curx += 2; + break; + + case 'k': case 'K': + _cury -= 2; + break; + + case 'j': case 'J': + _cury += 2; + break; + + case 'u': case 'U': + _curx += 1; + _cury -= 1; + break; + + case 'y': case 'Y': + _curx -= 1; + _cury -= 1; + break; + + case 'b': case 'B': + _curx -= 1; + _cury += 1; + break; + + case 'n': case 'N': + _curx += 1; + _cury += 1; + break; + + case 'q': case 'Q': + // Cleanup + delete sc; + exit(0); + + case CONTROL('L'): case CONTROL('R'): + b.clean(); + b.paint(); + break; + + case ' ': + { + x = _curx / 2; + y = _cury / 2; + + if (_cury & 1) { + if (_curx == 0) + dir = BOX::left; + else { + x--; + dir = BOX::right; + } + } + + if (_curx & 1) { + if (_cury == 0) + dir = BOX::top; + else { + y--; + dir = BOX::bottom; + } + } + } + return; + + default: + break; + } + + // We add 2 before the comparison to avoid underflow + if ((2 + _curx) - (_curx & 1) < 2) + _curx = (b.nx() * 2) + (_curx & 1); + if (_curx >= (b.nx() * 2) + 1) + _curx = (_curx & 1); + + if ((2 + _cury) - (_cury & 1) < 2) + _cury = (b.ny() * 2) + (_cury & 1); + if (_cury >= (b.ny() * 2) + 1) + _cury = (_cury & 1); + + b.setpos(_cury, _curx); + } +} diff --git a/dab/human.h b/dab/human.h new file mode 100644 index 0000000..5d51a50 --- /dev/null +++ b/dab/human.h @@ -0,0 +1,53 @@ +/* $NetBSD: human.h,v 1.3 2008/04/28 20:22:54 martin Exp $ */ + +/*- + * Copyright (c) 2003 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * human.h: Human input interface + */ + +#ifndef _H_HUMAN +#define _H_HUMAN +#include <stdlib.h> +#include "player.h" + +class BOARD; + +class HUMAN : public PLAYER { + public: + HUMAN(const char c); + virtual ~HUMAN() {} + // Return move in y, x, and dir + void play(const BOARD& b, size_t& y, size_t& x, int& dir); + private: + size_t _curx, _cury; // Current cursor position +}; + +#endif diff --git a/dab/main.cc b/dab/main.cc new file mode 100644 index 0000000..49a8850 --- /dev/null +++ b/dab/main.cc @@ -0,0 +1,191 @@ +/* $NetBSD: main.cc,v 1.6 2012/10/06 19:39:51 christos Exp $ */ + +/*- + * Copyright (c) 2003 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * main.C: Main dots program + */ +#include "defs.h" +RCSID("$NetBSD: main.cc,v 1.6 2012/10/06 19:39:51 christos Exp $") + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <err.h> +#include "algor.h" +#include "board.h" +#include "human.h" +#include "ttyscrn.h" + +GAMESCREEN *sc; + +// Print the command line usage +static void usage(char* pname) +{ + char* p = strrchr(pname, '/'); + if (p) + p++; + else + p = pname; + (void)::fprintf(stderr, + "Usage: %s [-w] [-p <c|h><c|h>] [-n <ngames>] [<ydim> [<xdim>]]\n", p); +} + +// Play a single game +static void play(BOARD& b, PLAYER* p[2]) +{ + // Initialize + b.init(); + p[0]->init(); + p[1]->init(); + b.paint(); + + // Alternate turns between players, scoring each turn + for (size_t i = 0;; i = (i + 1) & 1) { + b.score(i, *p[i]); + if (!p[i]->domove(b)) { + // No more moves, game over + break; + } + b.score(i, *p[i]); + } + + // Find who won + p[0]->wl(p[1]->getScore()); + p[1]->wl(p[0]->getScore()); + + // Post scores + b.score(0, *p[0]); + b.score(1, *p[1]); + + // Post totals + b.total(0, *p[0]); + b.total(1, *p[1]); + + // Post games + b.games(0, *p[0]); + b.games(1, *p[1]); + + // Post ties + b.ties(*p[0]); +} + +int main(int argc, char** argv) +{ + size_t ny, nx, nn = 1, wt = 0; + const char* nc = "ch"; + int c; + int acs = 1; + + while ((c = getopt(argc, argv, "awp:n:")) != -1) + switch (c) { + case 'a': + acs = 0; + break; + case 'w': + wt++; + break; + + case 'p': + nc = optarg; + break; + + case 'n': + nn = atoi(optarg); + break; + + default: + usage(argv[0]); + return 1; + } + + // Get the size of the board if specified + switch (argc - optind) { + case 0: + ny = nx = 3; + break; + + case 1: + ny = nx = atoi(argv[optind]); + break; + + case 2: + nx = atoi(argv[optind]); + ny = atoi(argv[optind+1]); + break; + + default: + usage(argv[0]); + return 1; + } + + + PLAYER* p[2]; + + // Allocate players + for (size_t i = 0; i < 2; i++) { + char n = nc[1] == nc[0] ? i + '0' : nc[i]; + switch (nc[i]) { + case 'c': + p[i] = new ALGOR(n); + break; + + case 'h': + p[i] = new HUMAN(n); + break; + + default: + usage(argv[0]); + return 1; + } + } + + sc = TTYSCRN::create(acs, &ny, &nx); + if (sc == NULL) + ::errx(1, "Dimensions too large for current screen."); + + BOARD b(ny, nx, sc); + + // Play games + while (nn--) { + play(b, p); + if (wt) + b.getmove(); + } + + if (wt == 0) + b.getmove(); + // Cleanup + delete sc; + delete p[0]; + delete p[1]; + return 0; +} diff --git a/dab/player.cc b/dab/player.cc new file mode 100644 index 0000000..f7fd3d3 --- /dev/null +++ b/dab/player.cc @@ -0,0 +1,91 @@ +/* $NetBSD: player.cc,v 1.2 2008/04/28 20:22:54 martin Exp $ */ + +/*- + * Copyright (c) 2003 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * player.C: Player base class + */ + +#include "defs.h" +RCSID("$NetBSD: player.cc,v 1.2 2008/04/28 20:22:54 martin Exp $") + +#include "board.h" +#include "player.h" + +PLAYER::PLAYER(char who) : + _who(who), + _score(0), + _total(0), + _games(0), + _ties(0) +{ +} + +void PLAYER::init(void) +{ + _score = 0; +} + +void PLAYER::wl(size_t sc) +{ + _total += _score; + _games += sc < _score; + _ties += sc == _score; +} + +int PLAYER::domove(BOARD& b) +{ + size_t y, x; + int dir; + int score; + + for (;;) { + if (b.full()) + return 0; + + play(b, y, x, dir); + + switch (score = b.domove(y, x, dir, _who)) { + case 0: + // No closure + return 1; + + case -1: + // Not a valid move + b.bell(); + break; + + default: + // Closure, play again + _score += score; + break; + } + } +} diff --git a/dab/player.h b/dab/player.h new file mode 100644 index 0000000..fe61b67 --- /dev/null +++ b/dab/player.h @@ -0,0 +1,70 @@ +/* $NetBSD: player.h,v 1.3 2008/04/28 20:22:54 martin Exp $ */ + +/*- + * Copyright (c) 2003 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * player.h: Player base class + */ +#ifndef _H_PLAYER +#define _H_PLAYER + +class BOARD; + +#include <stdlib.h> + +class PLAYER { + public: + PLAYER(char who); + virtual ~PLAYER() {} + virtual void play(const BOARD& b, size_t& y, size_t& x, int& dir) = 0; + + // Helper functions + void init(void); + int domove(BOARD& b); + + // Member access + char getWho(void) const { return _who; } + + // Display + size_t getScore(void) const { return _score; } + size_t getTotal(void) const { return _total; } + size_t getGames(void) const { return _games; } + size_t getTies(void) const { return _ties; } + void wl(size_t sc); + + private: + char _who; + size_t _score; + size_t _total; + size_t _games; + size_t _ties; +}; + +#endif diff --git a/dab/random.cc b/dab/random.cc new file mode 100644 index 0000000..2204c45 --- /dev/null +++ b/dab/random.cc @@ -0,0 +1,79 @@ +/* $NetBSD: random.cc,v 1.3 2008/04/28 20:22:54 martin Exp $ */ + +/*- + * Copyright (c) 2003 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * random.C: Randomizer for the dots program + */ + +#include "defs.h" +RCSID("$NetBSD: random.cc,v 1.3 2008/04/28 20:22:54 martin Exp $") + +#include <time.h> +#include <string.h> +#include "random.h" + +RANDOM::RANDOM(size_t ns) : + _bs(ns) +{ + _bm = new char[(_bs >> 3) + 1]; + clear(); +} + +RANDOM::~RANDOM() +{ + delete[] _bm; +} + +// Reinitialize +void RANDOM::clear(void) +{ + _nv = 0; + ::srand48(::time(NULL)); + (void) ::memset(_bm, 0, (_bs >> 3) + 1); +} + +// Return the next random value +size_t RANDOM::operator() (void) +{ + // No more values + if (_nv == _bs) + return _bs; + + for (;;) { + size_t r = ::lrand48(); + size_t z = r % _bs; + if (!isset(z)) { + set(z); + _nv++; + return z; + } + } +} diff --git a/dab/random.h b/dab/random.h new file mode 100644 index 0000000..86b24c6 --- /dev/null +++ b/dab/random.h @@ -0,0 +1,66 @@ +/* $NetBSD: random.h,v 1.3 2008/04/28 20:22:54 martin Exp $ */ + +/*- + * Copyright (c) 2003 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * random.h: Randomizer; returns a random sequence of values from [0..fx) + * returning each value exactly once. After fx calls it returns fx. + */ + +#ifndef _H_RANDOM +#define _H_RANDOM + +#include <stdlib.h> + +class RANDOM { + public: + // Constructor and destructor + RANDOM(size_t fx); + ~RANDOM(); + + size_t operator () (void); // Next random + void clear(void); // Reset + + private: + + int isset(size_t z) { + return (_bm[z >> 3] & (1 << (z & 7))) != 0; + } + + void set(size_t z) { + _bm[z >> 3] |= (1 << (z & 7)); + } + + char* _bm; // Bitmap indicating the numbers used + size_t _nv; // Number of values returned so far + size_t _bs; // Maximum value +}; + +#endif diff --git a/dab/test.cc b/dab/test.cc new file mode 100644 index 0000000..a9e0165 --- /dev/null +++ b/dab/test.cc @@ -0,0 +1,50 @@ +/* $NetBSD: test.cc,v 1.2 2008/04/28 20:22:54 martin Exp $ */ + +/*- + * Copyright (c) 2003 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * test.C: Test program for randomizer + */ + +#include "defs.h" +RCSID("$NetBSD: test.cc,v 1.2 2008/04/28 20:22:54 martin Exp $") + +#include <iostream> +#include "random.h" + +int +main(void) +{ + RANDOM rd(10); + + for (size_t x = rd(); x < 10; x = rd()) + std::cout << "x=" << x << std::endl; + return 0; +} diff --git a/dab/ttyscrn.cc b/dab/ttyscrn.cc new file mode 100644 index 0000000..eeed5fb --- /dev/null +++ b/dab/ttyscrn.cc @@ -0,0 +1,233 @@ +/* $NetBSD: ttyscrn.cc,v 1.5 2012/10/06 19:39:51 christos Exp $ */ + +/*- + * Copyright (c) 2003 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * ttyscrn.C: Curses screen implementation for dots + */ + +#include "defs.h" +RCSID("$NetBSD: ttyscrn.cc,v 1.5 2012/10/06 19:39:51 christos Exp $") + +#include <stdio.h> +#include <curses.h> +#include <sys/ioctl.h> + +#include "player.h" +#include "ttyscrn.h" + +void TTYSCRN::clean(void) +{ + clear(); +} + +void TTYSCRN::moveto(size_t y, size_t x) +{ + move(y + TTYSCRN::offsy, x + TTYSCRN::offsx); +} + +void TTYSCRN::addsym(const int sym) +{ + addch(sym); +} + +void TTYSCRN::addedge(const int sym) +{ + int nsym; +#ifdef A_ALTCHARSET + if (_acs) { + switch (sym) { + case GS_HLINE: + nsym = ACS_HLINE; + break; + case GS_VLINE: + nsym = ACS_VLINE; + break; + case GS_ULCORNER: + nsym = ACS_ULCORNER; + break; + case GS_URCORNER: + nsym = ACS_URCORNER; + break; + case GS_LLCORNER: + nsym = ACS_LLCORNER; + break; + case GS_LRCORNER: + nsym = ACS_LRCORNER; + break; + case GS_LTEE: + nsym = ACS_LTEE; + break; + case GS_RTEE: + nsym = ACS_RTEE; + break; + case GS_TTEE: + nsym = ACS_TTEE; + break; + case GS_BTEE: + nsym = ACS_BTEE; + break; + case GS_PLUS: + nsym = ACS_PLUS; + break; + case ' ': + addsym(' '); + return; + default: + ::abort(); + } + attron(A_ALTCHARSET); + addch(nsym); + attroff(A_ALTCHARSET); + return; + } +#endif + switch (sym) { + case GS_HLINE: + nsym = '-'; + break; + case GS_VLINE: + nsym = '|'; + break; + case GS_ULCORNER: + nsym = '.'; + break; + case GS_URCORNER: + nsym = '.'; + break; + case GS_LLCORNER: + nsym = '.'; + break; + case GS_LRCORNER: + nsym = '.'; + break; + case GS_LTEE: + nsym = '.'; + break; + case GS_RTEE: + nsym = '.'; + break; + case GS_TTEE: + nsym = '.'; + break; + case GS_BTEE: + nsym = '.'; + break; + case GS_PLUS: + nsym = '+'; + break; + case ' ': + addsym(' '); + return; + default: + ::abort(); + } + addsym(nsym); +} + +void TTYSCRN::redraw(void) +{ + refresh(); + doupdate(); +} + +void TTYSCRN::bell(void) +{ + putc('\007', stdout); +} + +int TTYSCRN::getinput(void) +{ + return getch(); +} + +void TTYSCRN::score(size_t s, const PLAYER& p) +{ + mvwprintw(stdscr, _sy + s + TTYSCRN::offsscore, _sx, "S %c:%5zd", p.getWho(), + p.getScore()); +} + +void TTYSCRN::total(size_t s, const PLAYER& p) +{ + mvwprintw(stdscr, _sy + s + TTYSCRN::offstotal, _sx, "T %c:%5zd", p.getWho(), + p.getTotal()); +} + +void TTYSCRN::games(size_t s, const PLAYER& p) +{ + mvwprintw(stdscr, _sy + s + TTYSCRN::offsgames, _sx, "G %c:%5zd", p.getWho(), + p.getGames()); +} + +void TTYSCRN::ties(const PLAYER& p) +{ + mvwprintw(stdscr, _sy + TTYSCRN::offsties, _sx, "G =:%5zd", p.getTies()); +} + +TTYSCRN* TTYSCRN::create(int acs, size_t *y, size_t *x) +{ + int tx, ty; + + initscr(); + + tx = getmaxx(stdscr); + ty = getmaxy(stdscr); + + if (tx == ERR || ty == ERR + || static_cast<size_t>(tx) < *x * 2 + TTYSCRN::offsx + 14 + || static_cast<size_t>(ty) < *y * 2 + TTYSCRN::offsy) { + endwin(); + return NULL; + } + if (*x == 0) + *x = (tx - 14 - TTYSCRN::offsx) / 2; + if (*y == 0) + *y = (ty - TTYSCRN::offsy) / 2; + cbreak(); + noecho(); + + + TTYSCRN* that = new TTYSCRN; + + that->_tx = tx; + that->_ty = ty; + that->_sx = tx - 12; + that->_sy = TTYSCRN::offsy; + that->_acs = acs; + + return that; +} + +TTYSCRN::~TTYSCRN(void) +{ + nocbreak(); + echo(); + endwin(); +} diff --git a/dab/ttyscrn.h b/dab/ttyscrn.h new file mode 100644 index 0000000..addd91e --- /dev/null +++ b/dab/ttyscrn.h @@ -0,0 +1,74 @@ +/* $NetBSD: ttyscrn.h,v 1.4 2012/10/06 19:39:51 christos Exp $ */ + +/*- + * Copyright (c) 2003 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * ttyscrn.h: Curses based screen for dots + */ + +#ifndef _H_TTYSCRN +#define _H_TTYSCRN + +#include "gamescreen.h" + +class TTYSCRN : public GAMESCREEN { + public: + // Constructor that can fail + static TTYSCRN* create(int acs, size_t *y, size_t *x); + ~TTYSCRN(); + + // Screen virtuals + void clean(void); + void moveto(size_t y, size_t x); + void addsym(const int sym); + void addedge(const int sym); + void redraw(void); + void bell(void); + int getinput(void); + void score(size_t s, const PLAYER& p); + void games(size_t s, const PLAYER& p); + void total(size_t s, const PLAYER& p); + void ties(const PLAYER& p); + + private: + enum { + offsx = 2, // board x offset from top left corner + offsy = 2, // board y offset from top left corner + offsscore = 0, // score y offset from top of the board + offstotal = 3, // total y offset from top of the board + offsgames = 6, // games y offset from top of the board + offsties = 8 // ties y offset from top of the board + }; + size_t _sx, _sy; // board size + size_t _tx, _ty; // tty size + int _acs; // do we want acs? +}; + +#endif diff --git a/dm/Makefile b/dm/Makefile new file mode 100644 index 0000000..7514eb8 --- /dev/null +++ b/dm/Makefile @@ -0,0 +1,18 @@ +# $NetBSD: Makefile,v 1.11 2002/09/18 03:23:00 lukem Exp $ +# @(#)Makefile 8.1 (Berkeley) 5/31/93 + +.include <bsd.own.mk> + +# -DLOG log games +PROG= dm +# 20150209 bkw: removed utmpentry.c reference +SRCS= dm.c +MAN= dm.8 dm.conf.5 +# shouldn't be necessary; just in case. +BINGRP= games +BINMODE=2555 + +.PATH.c: ${NETBSDSRCDIR}/usr.bin/who +CPPFLAGS+=-I${NETBSDSRCDIR}/usr.bin/who -DSUPPORT_UTMPX -DSUPPORT_UTMP + +.include <bsd.prog.mk> @@ -0,0 +1,106 @@ +.\" $NetBSD: dm.8,v 1.9 2003/08/07 09:37:11 agc Exp $ +.\" +.\" Copyright (c) 1987, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)dm.8 8.1 (Berkeley) 5/31/93 +.\" +.Dd May 31, 1993 +.Dt DM 8 +.Os +.Sh NAME +.Nm dm +.Nd dungeon master +.Sh SYNOPSIS +.Nm ln +.Fl s Cm dm Ar game +.Sh DESCRIPTION +.Nm +is a program used to regulate game playing. +.Nm +expects to be invoked with the name of a game that a user wishes to play. +This is done by creating symbolic links to +.Nm , +in the directory +.Pa /usr/games +for all of the regulated games. +The actual binaries for these games should be placed in a +.Dq hidden +directory, +.Pa /usr/games/hide , +that may only be accessed by the +.Nm +program. +.Nm +determines if the requested game is available and, if so, runs it. +The file +.Pa /etc/dm.conf +controls the conditions under which games may be run. +.Pp +The file +.Pa /etc/nogames +may be used to +.Dq turn off +game playing. +If the file exists, no game playing is allowed; the contents of the file +will be displayed to any user requesting a game. +.Sh FILES +.Bl -tag -width /var/log/games.log -compact +.It Pa /etc/dm.conf +configuration file +.It Pa /etc/nogames +turns off game playing +.It Pa /usr/games/hide +directory of ``real'' binaries +.It Pa /var/log/games.log +game logging file +.El +.Sh SEE ALSO +.Xr dm.conf 5 +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.3 tahoe . +.Sh SECURITY CONSIDERATIONS +Two issues result from +.Nm +running the games setgid +.Dq games . +First, all games that allow users to run +.Ux +commands should carefully +set both the real and effective group ids immediately before executing +those commands. +Probably more important is that +.Nm +never be setgid anything but +.Dq games +so that compromising a game will result only in +the user's ability to play games at will. +Secondly, games which previously had no reason to run setgid and which +accessed user files may have to be modified. @@ -0,0 +1,335 @@ +/* $NetBSD: dm.c,v 1.29 2009/08/27 00:22:28 dholland Exp $ */ + +/* + * Copyright (c) 1987, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1987, 1993\ + The Regents of the University of California. All rights reserved."); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)dm.c 8.1 (Berkeley) 5/31/93"; +#else +__RCSID("$NetBSD: dm.c,v 1.29 2009/08/27 00:22:28 dholland Exp $"); +#endif +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/file.h> +#include <sys/time.h> +#include <sys/resource.h> + +#include <err.h> +#include <ctype.h> +#include <errno.h> +#include <pwd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#include <utmp.h> /* 20150209 bkw: see users() */ +#include "pathnames.h" + +static time_t now; /* current time value */ +static int priority = 0; /* priority game runs at */ +static char *game, /* requested game */ + *gametty; /* from tty? */ + +static void c_day(const char *, const char *, const char *); +static void c_game(const char *, const char *, const char *, const char *); +static void c_tty(const char *); +static const char *hour(int); +static double load(void); +static void nogamefile(void); +static void play(char **) __dead; +static void read_config(void); +static int users(void); + +#ifdef LOG +static void logfile(void); +#endif + +int +main(int argc __attribute__((unused)), char *argv[]) +{ + char *cp; + + nogamefile(); + game = (cp = strrchr(*argv, '/')) ? ++cp : *argv; + + if (!strcmp(game, "dm")) + exit(0); + + gametty = ttyname(0); + unsetenv("TZ"); + (void)time(&now); + read_config(); +#ifdef LOG + logfile(); +#endif + play(argv); + /*NOTREACHED*/ + return (0); +} + +/* + * play -- + * play the game + */ +static void +play(char **args) +{ + char pbuf[MAXPATHLEN]; + + snprintf(pbuf, sizeof(pbuf), "%s%s", _PATH_HIDE, game); + if (priority > 0) /* < 0 requires root */ + (void)setpriority(PRIO_PROCESS, 0, priority); + execv(pbuf, args); + err(1, "%s", pbuf); +} + +/* + * read_config -- + * read through config file, looking for key words. + */ +static void +read_config(void) +{ + FILE *cfp; + char lbuf[BUFSIZ], f1[40], f2[40], f3[40], f4[40], f5[40]; + + if (!(cfp = fopen(_PATH_CONFIG, "r"))) + return; + while (fgets(lbuf, sizeof(lbuf), cfp)) + switch (*lbuf) { + case 'b': /* badtty */ + if (sscanf(lbuf, "%39s%39s", f1, f2) != 2 || + strcasecmp(f1, "badtty")) + break; + c_tty(f2); + break; + case 'g': /* game */ + if (sscanf(lbuf, "%39s%39s%39s%39s%39s", + f1, f2, f3, f4, f5) != 5 || strcasecmp(f1, "game")) + break; + c_game(f2, f3, f4, f5); + break; + case 't': /* time */ + if (sscanf(lbuf, "%39s%39s%39s%39s", f1, f2, f3, f4) != 4 || + strcasecmp(f1, "time")) + break; + c_day(f2, f3, f4); + } + (void)fclose(cfp); +} + +/* + * c_day -- + * if day is today, see if okay to play + */ +static void +c_day(const char *s_day, const char *s_start, const char *s_stop) +{ + static const char *const days[] = { + "sunday", "monday", "tuesday", "wednesday", + "thursday", "friday", "saturday", + }; + static struct tm *ct; + int start, stop; + + if (!ct) + ct = localtime(&now); + if (strcasecmp(s_day, days[ct->tm_wday])) + return; + if (!isdigit((unsigned char)*s_start) || + !isdigit((unsigned char)*s_stop)) + return; + start = atoi(s_start); + stop = atoi(s_stop); + if (ct->tm_hour >= start && ct->tm_hour < stop) { + if (start == 0 && stop == 24) + errx(0, "Sorry, games are not available today."); + else + errx(0, "Sorry, games are not available from %s to %s today.", + hour(start), hour(stop)); + } +} + +/* + * c_tty -- + * decide if this tty can be used for games. + */ +static void +c_tty(const char *tty) +{ + static int first = 1; + static char *p_tty; + + if (first) { + p_tty = strrchr(gametty, '/'); + first = 0; + } + + if (!strcmp(gametty, tty) || (p_tty && !strcmp(p_tty, tty))) + errx(1, "Sorry, you may not play games on %s.", gametty); +} + +/* + * c_game -- + * see if game can be played now. + */ +static void +c_game(const char *s_game, const char *s_load, const char *s_users, + const char *s_priority) +{ + static int found; + + if (found) + return; + if (strcmp(game, s_game) && strcasecmp("default", s_game)) + return; + ++found; + if (isdigit((unsigned char)*s_load) && atoi(s_load) < load()) + errx(0, "Sorry, the load average is too high right now."); + if (isdigit((unsigned char)*s_users) && atoi(s_users) <= users()) + errx(0, "Sorry, there are too many users logged on right now."); + if (isdigit((unsigned char)*s_priority)) + priority = atoi(s_priority); +} + +/* + * load -- + * return 15 minute load average + */ +static double +load(void) +{ + double avenrun[3]; + + if (getloadavg(avenrun, sizeof(avenrun)/sizeof(avenrun[0])) < 0) + err(1, "getloadavg() failed"); + return (avenrun[2]); +} + +/* + * users -- + * return current number of users + * todo: check idle time; if idle more than X minutes, don't + * count them. + * 20150209 bkw: replaced BSD original with dm.c users() from + * Linux port of bsd-games-2.13 + */ +static int +users() +{ + + int nusers, utmp; + struct utmp buf; + + if ((utmp = open(_PATH_UTMP, O_RDONLY, 0)) < 0) + err(1, "%s", _PATH_UTMP); + for (nusers = 0; read(utmp, (char *)&buf, sizeof(struct utmp)) > 0;) + if (buf.ut_name[0] != '\0') + ++nusers; + return (nusers); +} + +static void +nogamefile(void) +{ + int fd, n; + char buf[BUFSIZ]; + + if ((fd = open(_PATH_NOGAMES, O_RDONLY, 0)) >= 0) { +#define MESG "Sorry, no games right now.\n\n" + (void)write(2, MESG, sizeof(MESG) - 1); + while ((n = read(fd, buf, sizeof(buf))) > 0) + (void)write(2, buf, n); + exit(1); + } +} + +/* + * hour -- + * print out the hour in human form + */ +static const char * +hour(int h) +{ + static const char *const hours[] = { + "midnight", "1am", "2am", "3am", "4am", "5am", + "6am", "7am", "8am", "9am", "10am", "11am", + "noon", "1pm", "2pm", "3pm", "4pm", "5pm", + "6pm", "7pm", "8pm", "9pm", "10pm", "11pm", "midnight" }; + + if (h < 0 || h > 24) + return ("BAD TIME"); + else + return (hours[h]); +} + +#ifdef LOG +/* + * logfile -- + * log play of game + */ +static void +logfile(void) +{ + struct passwd *pw; + FILE *lp; + uid_t uid; + int lock_cnt; + + if (lp = fopen(_PATH_LOG, "a")) { + for (lock_cnt = 0;; ++lock_cnt) { + if (!flock(fileno(lp), LOCK_EX)) + break; + if (lock_cnt == 4) { + warnx("log lock"); + (void)fclose(lp); + return; + } + sleep(1); + } + if (pw = getpwuid(uid = getuid())) + fputs(pw->pw_name, lp); + else + fprintf(lp, "%u", uid); + fprintf(lp, "\t%s\t%s\t%s", game, gametty, ctime(&now)); + (void)flock(fileno(lp), LOCK_UN); + (void)fclose(lp); + } +} +#endif /* LOG */ diff --git a/dm/dm.conf.5 b/dm/dm.conf.5 new file mode 100644 index 0000000..b7933ce --- /dev/null +++ b/dm/dm.conf.5 @@ -0,0 +1,114 @@ +.\" $NetBSD: dm.conf.5,v 1.8 2003/08/07 09:37:11 agc Exp $ +.\" +.\" Copyright (c) 1988, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)dm.conf.5 8.1 (Berkeley) 5/31/93 +.\" +.Dd May 31, 1993 +.Dt DM.CONF 5 +.Os +.Sh NAME +.Nm dm.conf +.Nd dungeon master configuration file +.Sh DESCRIPTION +The +.Nm +file is the configuration file for the +.Xr dm 8 +program. +It consists of lines beginning with one of three keywords, +.Em badtty , +.Em game , +and +.Em time . +All other lines are ignored. +.Pp +Any tty listed after the keyword +.Em badtty +may not have games played on it. +Entries consist of two white-space separated fields: the string +.Em badtty +and the ttyname as returned by +.Xr ttyname 3 . +For example, to keep the uucp dialout, +.Dq tty19 , +from being used for games, the entry would be: +.Bd -literal -offset indent +badtty /dev/tty19 +.Ed +.Pp +Any day/hour combination listed after the keyword +.Em time +will disallow games during those hours. +Entries consist of four white-space separated fields: the string +.Em time , +the unabbreviated day of the week and the beginning and ending time +of a period of the day when games may not be played. +The time fields are in a 0 based, 24-hour clock. +For example, the following entry allows games playing before 8AM +and after 5PM on Mondays: +.Bd -literal -offset indent +time Monday 8 17 +.Ed +.Pp +Any game listed after the keyword +.Em game +will set parameters for a specific game. +Entries consist of five white-space separated fields: the keyword +.Em game , +the name of a game, the highest system load average at which the +game may be played, the maximum users allowed if the game is to be +played, and the priority at which the game is to be run. +Any of these fields may start with a non-numeric character, resulting +in no game limitation or priority based on that field. +.Pp +The game +.Em default +controls the settings for any game not otherwise listed, and must be the last +.Em game +entry in the file. +Priorities may not be negative. +For example, the following entries limits the game +.Dq hack +to running only when the system has 10 or less users and a load average of 5 +or less; all other games may be run any time the system has 15 or less users. +.Bd -literal -offset indent +game hack 5 10 * +game default * 15 * +.Ed +.Sh FILES +.Bl -tag -width /etc/dm.conf -compact +.It Pa /etc/dm.conf +The +.Xr dm 8 +configuration file. +.El +.Sh SEE ALSO +.Xr setpriority 2 , +.Xr ttyname 3 , +.Xr dm 8 diff --git a/dm/pathnames.h b/dm/pathnames.h new file mode 100644 index 0000000..9acdc5c --- /dev/null +++ b/dm/pathnames.h @@ -0,0 +1,37 @@ +/* $NetBSD: pathnames.h,v 1.4 2003/08/07 09:37:12 agc Exp $ */ + +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pathnames.h 8.1 (Berkeley) 5/31/93 + */ + +#define _PATH_CONFIG "/etc/dm.conf" +#define _PATH_HIDE "/usr/games/hide/" +#define _PATH_LOG "/var/log/games.log" +#define _PATH_NOGAMES "/etc/nogames" diff --git a/grdc/Makefile b/grdc/Makefile new file mode 100644 index 0000000..b528e25 --- /dev/null +++ b/grdc/Makefile @@ -0,0 +1,9 @@ +# $FreeBSD: src/games/grdc/Makefile,v 1.4.2.2 2001/10/02 11:51:49 ru Exp $ +# $DragonFly: src/games/grdc/Makefile,v 1.4 2006/10/08 16:22:35 pavalos Exp $ + +PROG= grdc +MAN= grdc.6 +DPADD= ${LIBNCURSES} +LDADD= -lncurses + +.include <bsd.prog.mk> diff --git a/grdc/grdc.6 b/grdc/grdc.6 new file mode 100644 index 0000000..40c045f --- /dev/null +++ b/grdc/grdc.6 @@ -0,0 +1,43 @@ +.\" $FreeBSD: src/games/grdc/grdc.6,v 1.2.12.1 2001/10/02 11:51:49 ru Exp $ +.\" $DragonFly: src/games/grdc/grdc.6,v 1.4 2006/05/12 14:05:39 swildner Exp $ +.Dd September 25, 2001 +.Dt GRDC 6 +.Os +.Sh NAME +.Nm grdc +.Nd grand digital clock (curses) +.Sh SYNOPSIS +.Nm +.Op Fl s +.Op Fl d Ar msecs +.Op Ar n +.Sh DESCRIPTION +.Nm +displays a digital clock made of reverse-video blanks +centered on a curses-compatible terminal. +.Pp +By default, the clock runs indefinitely. +When the optional numeric argument +.Ar n +is given, it stops after +.Ar n +seconds. +.Pp +The optional +.Fl s +flag makes digits scroll as they change. +Over slow links, the scrolling option may have trouble keeping up. +.Pp +The default time taken to scroll the digits into view is 120 +milliseconds. +The +.Fl d +option, which implies +.Fl s , +may be given to explicitly set the scroll duration. +The maximum scroll duration is effectively 5 seconds. +.Sh AUTHORS +.An -nosplit +.An Amos Shapir , +modified for curses by +.An John Lupien . diff --git a/grdc/grdc.c b/grdc/grdc.c new file mode 100644 index 0000000..2c9df85 --- /dev/null +++ b/grdc/grdc.c @@ -0,0 +1,266 @@ +/* + * Grand digital clock for curses compatible terminals + * Usage: grdc [-s] [-d msecs] [n] -- run for n seconds (default infinity) + * Flags: -s: scroll (default scroll duration 120msec) + * -d msecs: specify scroll duration (implies -s) + * + * modified 10-18-89 for curses (jrl) + * 10-18-89 added signal handling + * 03-23-04 added centering, scroll delay (cap) + * + * $FreeBSD: src/games/grdc/grdc.c,v 1.8.2.1 2001/10/02 11:51:49 ru Exp $ + */ + +#include <err.h> +#include <time.h> +#include <signal.h> +#include <ncurses.h> +#include <stdlib.h> +#ifndef NONPOSIX +#include <unistd.h> +#endif + +#define XLENGTH 58 +#define YDEPTH 7 + +time_t now; +struct tm *tm; + +short disp[11] = { + 075557, 011111, 071747, 071717, 055711, + 074717, 074757, 071111, 075757, 075717, 002020 +}; +long old[6], next[6], new[6], mask; + +volatile sig_atomic_t sigtermed; + +int hascolor = 0; +long int scroll_msecs = 120; +int xbase, ybase, xmax, ymax; + +static void set(int, int); +static void standt(int); +static void sighndl(int); +static void usage(void); +static void draw_row(int, int); +static void snooze(long int); + +void +sighndl(int signo) +{ + sigtermed = signo; +} + +int +main(int argc, char **argv) +{ + int i, s, k; + int n; + int ch; + int scrol; + int forever; + + n = scrol = 0; + forever = 1; + + while ((ch = getopt(argc, argv, "d:s")) != -1) + switch (ch) { + case 'd': + scroll_msecs = atol(optarg); + if (scroll_msecs < 0) + errx(1, "scroll duration may not be negative"); + /* FALLTHROUGH */ + case 's': + scrol = 1; + break; + case '?': + default: + usage(); + /* NOTREACHED */ + } + argc -= optind; + argv += optind; + + if (argc > 1) { + usage(); + /* NOTREACHED */ + } + + if (argc > 0) { + n = atoi(*argv); + forever = 0; + } + + initscr(); + + getmaxyx(stdscr, ymax, xmax); + if (ymax < YDEPTH + 2 || xmax < XLENGTH + 4) { + endwin(); + errx(1, "terminal too small"); + } + xbase = (xmax - XLENGTH) / 2 + 2; + ybase = (ymax - YDEPTH) / 2 + 1; + + signal(SIGINT, sighndl); + signal(SIGTERM, sighndl); + signal(SIGHUP, sighndl); + + cbreak(); + noecho(); + curs_set(0); + + hascolor = has_colors(); + + if (hascolor) { + start_color(); + init_pair(1, COLOR_BLACK, COLOR_RED); + init_pair(2, COLOR_RED, COLOR_BLACK); + init_pair(3, COLOR_WHITE, COLOR_BLACK); + attrset(COLOR_PAIR(2)); + } + + clear(); + refresh(); + + if (hascolor) { + attrset(COLOR_PAIR(3)); + + mvaddch(ybase - 2, xbase - 3, ACS_ULCORNER); + hline(ACS_HLINE, XLENGTH); + mvaddch(ybase - 2, xbase - 2 + XLENGTH, ACS_URCORNER); + + mvaddch(ybase + YDEPTH - 1, xbase - 3, ACS_LLCORNER); + hline(ACS_HLINE, XLENGTH); + mvaddch(ybase + YDEPTH - 1, xbase - 2 + XLENGTH, ACS_LRCORNER); + + move(ybase - 1, xbase - 3); + vline(ACS_VLINE, YDEPTH); + + move(ybase - 1, xbase - 2 + XLENGTH); + vline(ACS_VLINE, YDEPTH); + + attrset(COLOR_PAIR(2)); + refresh(); + } + do { + mask = 0; + time(&now); + tm = localtime(&now); + set(tm->tm_sec % 10, 0); + set(tm->tm_sec / 10, 4); + set(tm->tm_min % 10, 10); + set(tm->tm_min / 10, 14); + set(tm->tm_hour % 10, 20); + set(tm->tm_hour / 10, 24); + set(10, 7); + set(10, 17); + for(k = 0; k < 6; k++) { + if (scrol) { + snooze(scroll_msecs / 6); + for(i = 0; i < 5; i++) + new[i] = (new[i] & ~mask) | + (new[i+1] & mask); + new[5] = (new[5] & ~mask) | (next[k] & mask); + } else + new[k] = (new[k] & ~mask) | (next[k] & mask); + next[k] = 0; + for (s = 1; s >= 0; s--) { + standt(s); + for (i = 0; i < 6; i++) { + draw_row(i, s); + } + if (!s) { + move(ybase, 0); + refresh(); + } + } + } + move(ybase, 0); + refresh(); + snooze(1000 - (scrol ? scroll_msecs : 0)); + } while (forever ? 1 : --n); + standend(); + clear(); + refresh(); + endwin(); + return(0); +} + +void +snooze(long int msecs) +{ + struct timespec ts; + + ts.tv_sec = 0; + ts.tv_nsec = 1000000 * msecs; + + nanosleep(&ts, NULL); + + if (sigtermed) { + standend(); + clear(); + refresh(); + endwin(); + errx(1, "terminated by signal %d", (int)sigtermed); + } +} + +void +draw_row(int i, int s) +{ + long a, t; + int j; + + if ((a = (new[i] ^ old[i]) & (s ? new : old)[i]) != 0) { + for (j = 0, t = 1 << 26; t; t >>= 1, j++) { + if (a & t) { + if (!(a & (t << 1))) { + move(ybase + i, xbase + 2 * j); + } + addstr(" "); + } + } + } + if (!s) { + old[i] = new[i]; + } +} + +void +set(int t, int n) +{ + int i, m; + + m = 7 << n; + for (i = 0; i < 5; i++) { + next[i] |= ((disp[t] >> (4 - i) * 3) & 07) << n; + mask |= (next[i] ^ old[i]) & m; + } + if (mask & m) + mask |= m; +} + +void +standt(int on) +{ + if (on) { + if (hascolor) { + attron(COLOR_PAIR(1)); + } else { + attron(A_STANDOUT); + } + } else { + if (hascolor) { + attron(COLOR_PAIR(2)); + } else { + attroff(A_STANDOUT); + } + } +} + +void +usage(void) +{ + fprintf(stderr, "usage: grdc [-s] [-d msecs] [n]\n"); + exit(1); +} diff --git a/hack/COPYRIGHT b/hack/COPYRIGHT new file mode 100644 index 0000000..71a9449 --- /dev/null +++ b/hack/COPYRIGHT @@ -0,0 +1,6 @@ +This entire subtree is copyright the Stichting Mathematisch Centrum. +The following copyright notice applies to all files found here. None of +these files contain AT&T proprietary source code. +_____________________________________________________________________________ + +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ diff --git a/hack/Makefile b/hack/Makefile new file mode 100644 index 0000000..53b4975 --- /dev/null +++ b/hack/Makefile @@ -0,0 +1,52 @@ +# @(#)Makefile 8.1 (Berkeley) 5/31/93 +# $FreeBSD: src/games/hack/Makefile,v 1.20.2.4 2002/08/07 16:31:41 ru Exp $ +# $DragonFly: src/games/hack/Makefile,v 1.6 2006/10/08 16:22:35 pavalos Exp $ + +# 20150209: hackery with makedefs and hack.onames.h to +# make Slackware's old pmake do this right. + +PROG= hack +SRCS= alloc.c hack.Decl.c hack.apply.c hack.bones.c hack.c hack.cmd.c \ + hack.do.c hack.do_name.c hack.do_wear.c hack.dog.c hack.eat.c \ + hack.end.c hack.engrave.c hack.fight.c hack.invent.c hack.ioctl.c \ + hack.lev.c hack.main.c hack.makemon.c hack.mhitu.c hack.mklev.c \ + hack.mkmaze.c hack.mkobj.c hack.mkshop.c hack.mon.c hack.monst.c \ + hack.o_init.c hack.objnam.c hack.options.c hack.pager.c hack.potion.c \ + hack.pri.c hack.read.c hack.rip.c hack.rumors.c hack.save.c \ + hack.search.c hack.shk.c hack.shknam.c hack.steal.c hack.termcap.c \ + hack.timeout.c hack.topl.c hack.track.c hack.trap.c hack.tty.c \ + hack.u_init.c hack.unix.c hack.vault.c hack.version.c hack.wield.c \ + hack.wizard.c hack.worm.c hack.worn.c hack.zap.c rnd.c \ + hack.onames.h +MAN= hack.6 +DPADD= ${LIBTERMCAP} +LDADD= -ltermcap -lbsd +CFLAGS+= -I${.CURDIR} -I. +FILES= rumors help hh data +FILESMODE_rumors= 440 +FILESGRP= ${BINGRP} +FILESDIR= /var/games/hackdir +HIDEGAME=hidegame +CLEANFILES=hack.onames.h makedefs makedefs.no + +build-tools: makedefs + +alloc.c: hack.onames.h + +hack.onames.h: makedefs def.objects.h + ./makedefs ${.CURDIR}/def.objects.h > hack.onames.h + +makedefs: makedefs.c + +beforeinstall: + mkdir -p ${DESTDIR}/var/games/hackdir + ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \ + ${DESTDIR}/var/games/hackdir/perm +.if !exists(${DESTDIR}/var/games/hackdir/record) + ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m 460 /dev/null \ + ${DESTDIR}/var/games/hackdir/record +.endif +# rm -f ${DESTDIR}/var/games/hackdir/bones* \ +# ${DESTDIR}/var/games/hackdir/save/* + +.include <bsd.prog.mk> diff --git a/hack/Makequest b/hack/Makequest new file mode 100644 index 0000000..9271c28 --- /dev/null +++ b/hack/Makequest @@ -0,0 +1,196 @@ +# Hack or Quest Makefile. + +# on some systems the termcap library is in -ltermcap +TERMLIB = -ltermlib + + +# make hack +GAME = quest +GAMEDIR = /usr/games/lib/questdir +CFLAGS = -g -DQUEST +HACKCSRC = hack.Decl.c\ + hack.apply.c hack.bones.c hack.c hack.cmd.c hack.do.c\ + hack.do_name.c hack.do_wear.c hack.dog.c hack.eat.c hack.end.c\ + hack.engrave.c hack.fight.c hack.invent.c hack.ioctl.c\ + hack.lev.c hack.main.c hack.makemon.c hack.mhitu.c\ + hack.mklev.c hack.mkmaze.c hack.mkobj.c hack.mkshop.c\ + hack.mon.c hack.monst.c hack.o_init.c hack.objnam.c\ + hack.options.c hack.pager.c hack.potion.c hack.pri.c\ + hack.read.c hack.rip.c hack.rumors.c hack.save.c\ + hack.search.c hack.shk.c hack.shknam.c hack.steal.c\ + hack.termcap.c hack.timeout.c hack.topl.c\ + hack.track.c hack.trap.c hack.tty.c hack.unix.c\ + hack.u_init.c hack.vault.c\ + hack.wield.c hack.wizard.c hack.worm.c hack.worn.c hack.zap.c\ + hack.version.c rnd.c alloc.c + +CSOURCES = $(HACKCSRC) makedefs.c + +HSOURCES = hack.h hack.mfndpos.h config.h\ + def.edog.h def.eshk.h def.flag.h def.func_tab.h def.gold.h\ + def.mkroom.h\ + def.monst.h def.obj.h def.objclass.h def.objects.h\ + def.permonst.h def.rm.h def.trap.h def.wseg.h + +SOURCES = $(CSOURCES) $(HSOURCES) + +AUX = data help hh rumors hack.6 hack.sh + +DISTR = $(SOURCES) $(AUX) READ_ME Makefile date.h hack.onames.h + +HOBJ = hack.Decl.o hack.apply.o hack.bones.o hack.o hack.cmd.o hack.do.o\ + hack.do_name.o hack.do_wear.o hack.dog.o hack.eat.o hack.end.o\ + hack.engrave.o hack.fight.o hack.invent.o hack.ioctl.o\ + hack.lev.o hack.main.o hack.makemon.o hack.mhitu.o hack.mklev.o\ + hack.mkmaze.o hack.mkobj.o hack.mkshop.o hack.mon.o\ + hack.monst.o hack.o_init.o hack.objnam.o hack.options.o\ + hack.pager.o hack.potion.o hack.pri.o\ + hack.read.o hack.rip.o hack.rumors.o hack.save.o\ + hack.search.o hack.shk.o hack.shknam.o hack.steal.o\ + hack.termcap.o hack.timeout.o hack.topl.o\ + hack.track.o hack.trap.o\ + hack.tty.o hack.unix.o hack.u_init.o hack.vault.o hack.wield.o\ + hack.wizard.o hack.worm.o hack.worn.o hack.zap.o\ + hack.version.o rnd.o alloc.o + +$(GAME): $(HOBJ) Makefile + @echo "Loading ..." + @ld -X -o $(GAME) /lib/crt0.o $(HOBJ) $(TERMLIB) -lc + +all: $(GAME) lint + @echo "Done." + +makedefs: makedefs.c + cc -o makedefs makedefs.c + + +hack.onames.h: makedefs def.objects.h + makedefs > hack.onames.h + +lint: +# lint cannot have -p here because (i) capitals are meaningful: +# [Ww]izard, (ii) identifiers may coincide in the first six places: +# doweararm() versus dowearring(). +# _flsbuf comes from <stdio.h>, a bug in the system libraries. + @echo lint -axbh -DLINT ... + @lint -axbh -DLINT $(HACKCSRC) | sed '/_flsbuf/d' + + +diff: + @- for i in $(SOURCES) $(AUX) ; do \ + cmp -s $$i $D/$$i || \ + ( echo diff $D/$$i $$i ; diff $D/$$i $$i ; echo ) ; done + +distribution: Makefile + @- for i in READ_ME $(SOURCES) $(AUX) Makefile date.h hack.onames.h\ + ; do \ + cmp -s $$i $D/$$i || \ + ( echo cp $$i $D ; cp $$i $D ) ; done +# the distribution directory also contains the empty files perm and record. + + +install: + rm -f $(GAMEDIR)/$(GAME) + cp $(GAME) $(GAMEDIR)/$(GAME) + chmod 04511 $(GAMEDIR)/$(GAME) + rm -f $(GAMEDIR)/bones* +# cp hack.6 /usr/man/man6 + +clean: + rm -f *.o + + +depend: +# For the moment we are lazy and disregard /usr/include files because +# the sources contain them conditionally. Perhaps we should use cpp. +# ( /bin/grep '^#[ ]*include' $$i | sed -n \ +# -e 's,<\(.*\)>,"/usr/include/\1",' \ +# + for i in ${CSOURCES}; do \ + ( /bin/grep '^#[ ]*include[ ]*"' $$i | sed -n \ + -e 's/[^"]*"\([^"]*\)".*/\1/' \ + -e H -e '$$g' -e '$$s/\n/ /g' \ + -e '$$s/.*/'$$i': &/' -e '$$s/\.c:/.o:/p' \ + >> makedep); done + for i in ${HSOURCES}; do \ + ( /bin/grep '^#[ ]*include[ ]*"' $$i | sed -n \ + -e 's/[^"]*"\([^"]*\)".*/\1/' \ + -e H -e '$$g' -e '$$s/\n/ /g' \ + -e '$$s/.*/'$$i': &\ + touch '$$i/p \ + >> makedep); done + @echo '/^# DO NOT DELETE THIS LINE/+2,$$d' >eddep + @echo '$$r makedep' >>eddep + @echo 'w' >>eddep + @cp Makefile Makefile.bak + ed - Makefile < eddep + @rm -f eddep makedep + @echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile + @echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile + @echo '# see make depend above' >> Makefile + - diff Makefile Makefile.bak + @rm -f Makefile.bak + +# DO NOT DELETE THIS LINE + +hack.Decl.o: hack.h def.mkroom.h +hack.apply.o: hack.h def.edog.h def.mkroom.h +hack.bones.o: hack.h +hack.o: hack.h +hack.cmd.o: hack.h def.func_tab.h +hack.do.o: hack.h +hack.do_name.o: hack.h +hack.do_wear.o: hack.h +hack.dog.o: hack.h hack.mfndpos.h def.edog.h def.mkroom.h +hack.eat.o: hack.h +hack.end.o: hack.h +hack.engrave.o: hack.h +hack.fight.o: hack.h +hack.invent.o: hack.h def.wseg.h +hack.ioctl.o: config.h +hack.lev.o: hack.h def.mkroom.h def.wseg.h +hack.main.o: hack.h +hack.makemon.o: hack.h +hack.mhitu.o: hack.h +hack.mklev.o: hack.h def.mkroom.h +hack.mkmaze.o: hack.h def.mkroom.h +hack.mkobj.o: hack.h +hack.mkshop.o: hack.h def.mkroom.h def.eshk.h +hack.mon.o: hack.h hack.mfndpos.h +hack.monst.o: hack.h def.eshk.h +hack.o_init.o: config.h def.objects.h hack.onames.h +hack.objnam.o: hack.h +hack.options.o: config.h hack.h +hack.pager.o: hack.h +hack.potion.o: hack.h +hack.pri.o: hack.h def.wseg.h +hack.read.o: hack.h +hack.rip.o: hack.h +hack.rumors.o: hack.h +hack.save.o: hack.h +hack.search.o: hack.h +hack.shk.o: hack.h hack.mfndpos.h def.mkroom.h def.eshk.h +hack.shknam.o: hack.h +hack.steal.o: hack.h +hack.termcap.o: config.h def.flag.h +hack.timeout.o: hack.h +hack.topl.o: hack.h +hack.track.o: hack.h +hack.trap.o: hack.h def.mkroom.h +hack.tty.o: hack.h +hack.unix.o: hack.h def.mkroom.h +hack.u_init.o: hack.h +hack.vault.o: hack.h def.mkroom.h +hack.wield.o: hack.h +hack.wizard.o: hack.h +hack.worm.o: hack.h def.wseg.h +hack.worn.o: hack.h +hack.zap.o: hack.h +hack.version.o: date.h +hack.h: config.h def.objclass.h def.monst.h def.gold.h def.trap.h def.obj.h def.flag.h def.rm.h def.permonst.h hack.onames.h + touch hack.h +def.objects.h: config.h def.objclass.h + touch def.objects.h +# DEPENDENCIES MUST END AT END OF FILE +# IF YOU PUT STUFF HERE IT WILL GO AWAY +# see make depend above diff --git a/hack/OWNER b/hack/OWNER new file mode 100644 index 0000000..be2d1e5 --- /dev/null +++ b/hack/OWNER @@ -0,0 +1,2 @@ +Andries Brouwer +mcvax!aeb diff --git a/hack/Original_READ_ME b/hack/Original_READ_ME new file mode 100644 index 0000000..81ca07a --- /dev/null +++ b/hack/Original_READ_ME @@ -0,0 +1,61 @@ +This is export hack, my first semester programming project. + +To set it up for your system, you will have to do the following: + 1: create a hack uid, to own the top ten list, etc. + 2: create a hack directory "/usr/lib/game/hack" is the default. + 2.5: make the directory 700 mode. /* sav files go in there...*/ + 3: modify hack.main.c to use the new directory. + 4: modify hack.main.c so it uses the new hack gid. Gid accounts can +go into magic mode without the password, can get cores with ^G, etc. +(make sure gid isn't checked anywhere else...) + 5: recompile hack. + 6: put it in games after making it set-uid hack. + 8: fix the bugs I undoubtedly left in it. + 9: tell me what you think of it. + + Hack uses the UCB file /etc/termcap to get your terminal escape codes. +If you don't use it, you will have to make extensive changes to hack.pri.c + +If you find any bugs (That you think I don't know about), or have any +awesome new changes (Like a better save (One that works!)), or have ANY +questions, write me + Jay Fenlason + 29 East St. + Sudbury Mass. + 01776 + +or call me at (617) 443-5036. Since I have both a modem and a teen-age +sister, Good Luck. + + +Hack is split (roughly) into several source files that do different things. +I have tried to fit all the procedures having to do with a certain segment +of the game into a single file, but the job is not the best in the world. +The rough splits are: + +hack.c General random stuff and things I never got around to moving. +hack.main.c main() and other random procedures, also the lock file stuff. +hack.mon.c Monsters, moving, attacking, etc. +hack.do.c drink, eat, read, wield, save, etc. +hack.do1.c zap, wear, remove, etc... +hack.pri.c stuff having to do with the screen, most of the terminal + independent stuff is in here. +hack.lev.c temp files and calling of mklev. + +Because of the peculiar restraints on our system, I make mklev (create +a level) a separate procedure execd by hack when needed. The source for +mklev is (Naturaly) mklev.c. You may want to put mklev back into hack. +Good luck. + +Most of hack was written by me, with help from + Kenny Woodland (KW) (general random things including + the original BUZZ()) + Mike Thome (MT) (The original chamelian) + and Jon Payne (JP) (The original lock file kludge and + the massive CURS()) + +This entire program would not have been possible without the SFSU Logo +Workshop. I am eternally grateful to all of our students (Especially K.L.), +without whom I would never have seen Rogue. I am especially grateful to +Mike Clancy, without whose generous help I would never have gotten to play +ROGUE. diff --git a/hack/READ_ME b/hack/READ_ME new file mode 100644 index 0000000..cfe6ca2 --- /dev/null +++ b/hack/READ_ME @@ -0,0 +1,92 @@ +Hack is a display oriented dungeons & dragons - like game. +Both display and command structure resemble rogue. +(For a game with the same structure but entirely different display - +a real cave instead of dull rectangles - try Quest) + +Hack was originally written by Jay Fenlason (at lincolnsudbury: + 29 East St., Sudbury Mass., 01776) with help from + Kenny Woodland, Mike Thome and Jon Payne. +Basically it was an implementation of Rogue, however, with 52+ instead of 26 + monster types. +The current version is more than thrice as large (with such new features as + the dog, the long worms, the shops, etc.) and almost entirely rewritten + (only the display routines are the original ones - I must rewrite these + too one day; especially when you are blind strange things still happen). + +Files for hack: + hack The actual game + record Top 100 list (just start with an empty file) + news Tells about recent changes in hack, or bugs found ... + (Just start with no news file.) + data Auxiliary file used by hack to give you the names + and sometimes some more information on the + objects and monsters. + help Introductory information (no doubt outdated). + hh Compactified version of help. + perm An empty file used for locking purposes. + rumors Texts for fortune cookies. + (Some of these contain information on the game, + others are just plain stupid. Additional rumors + are appreciated.) + hack.sh A shell script. + (We have hack.sh in /usr/games/hack and + hack in /usr/games/lib/hackdir/hack and all the other + hack stuff in /usr/games/lib/hackdir - perhaps this + will make the script clear. + There is no need for you to use it.) + READ_ME This file. + Original_READ_ME Jay Fenlason's READ_ME + +System files used: + /etc/termcap Used in conjunction with the environment variable + $TERM. + /bin/cat + /usr/ucb/more + /bin/sh Used when $SHELL is undefined. + +How to install hack: +0. Compile the sources. Perhaps you should first look at the file config.h + and define BSD if you are on a BSDtype system, + define STUPID if your C-compiler chokes on complicated expressions. + Make sure schar and uchar represent signed and unsigned types. + If your C compiler doesnt allow initialization of bit fields + change Bitfield. When config.h looks reasonable, say 'make'. + (Perhaps you have to change TERMLIB in the makefile.) +1. If it didnt exist already, introduce a loginname `play' . +2. The program hack resides in a directory so that it is executable + for everybody and is suid play: + ---s--s--x 1 play 206848 Apr 3 00:17 hack + Perhaps you wish to restrict playing to certain hours, or have games + running under nice; in that case you might write a program play.c + such that the program play is suid play and executable for everybody + while all the games in /usr/games are readable or executable for + play only; all the program play does is asking for the name of a game, + checking that time-of-day and system load do not forbid playing, + and then executing the game. Thus: + -r-sr-sr-x 1 play 13312 May 24 12:52 play + ---x------ 1 play 206848 Apr 3 00:17 hack + If you are worried about security you might let play do + chroot("/usr/games") so that no player can get access to the rest + of the system via shell escapes and the likes. + If you #define SECURE in config.h then hack will not setuid(getuid()) + before executing a chdir(). Hack will always do setuid(getuid()) with + a fork. If you do not define UNIX then hack will not fork. +3. The rest of the stuff belonging to hack sits in a subdirectory hackdir + (on our system /usr/games/lib/hackdir) with modes + drwx------ 3 play 1024 Aug 9 09:03 hackdir + Here all the temporary files will be created (with names like xlock.17 + or user.5). +4. If you are not really short on file space, creating a subdirectory + hackdir/save (modes again drwx------) will enable users to save their + unfinished games. + +The program hack is called +$ hack [-d hackdir] [maxnrofplayers] +(for playing) or +$ hack [-d hackdir] -s [listofusers | limit | all] +(for seeing part of the scorelist). +The shell file hack (in this kit called hack.sh) takes care of +calling hack with the right arguments. + +Send complaints, bug reports, suggestions for improvements to +mcvax!aeb - in real life Andries Brouwer. diff --git a/hack/alloc.c b/hack/alloc.c new file mode 100644 index 0000000..5d72997 --- /dev/null +++ b/hack/alloc.c @@ -0,0 +1,38 @@ +/* alloc.c - version 1.0.2 */ +/* $FreeBSD: src/games/hack/alloc.c,v 1.4 1999/11/16 02:57:01 billf Exp $ */ +/* $DragonFly: src/games/hack/alloc.c,v 1.4 2006/08/21 19:45:32 pavalos Exp $ */ + +#include "hack.h" + +#ifdef LINT + +/* + a ridiculous definition, suppressing + "possible pointer alignment problem" for (long *) malloc() + "enlarg defined but never used" + "ftell defined (in <stdio.h>) but never used" + from lint +*/ +long * +alloc(size_t n) +{ + long dummy = ftell(stderr); + + if (n) + dummy = 0; /* make sure arg is used */ + return (&dummy); +} + +#else + +void * +alloc(size_t lth) +{ + void *ptr; + + if ((ptr = malloc(lth)) == NULL) + panic("Cannot get %zd bytes", lth); + return (ptr); +} + +#endif /* LINT */ diff --git a/hack/config.h b/hack/config.h new file mode 100644 index 0000000..2173dcd --- /dev/null +++ b/hack/config.h @@ -0,0 +1,124 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* config.h - version 1.0.3 */ +/* $DragonFly: src/games/hack/config.h,v 1.3 2006/08/21 19:45:32 pavalos Exp $ */ + +#include <stdbool.h> +#include "pathnames.h" + +#ifndef CONFIG /* make sure the compiler doesnt see the typedefs twice */ + +#define CONFIG +#define UNIX /* delete if no fork(), exec() available */ +#define CHDIR /* delete if no chdir() available */ + +/* #define STUPID */ /* avoid some complicated expressions if + your C compiler chokes on them */ +/* #define PYRAMID_BUG */ /* avoid a bug on the Pyramid */ +/* #define NOWAITINCLUDE */ /* neither <wait.h> nor <sys/wait.h> exists */ + +#define WIZARD "bruno" /* the person allowed to use the -D option */ +#define RECORD "record"/* the file containing the list of topscorers */ +#define NEWS "news" /* the file containing the latest hack news */ +#define HELP "help" /* the file containing a description of the commands */ +#define SHELP "hh" /* abbreviated form of the same */ +#define RUMORFILE "rumors" /* a file with fortune cookies */ +#define DATAFILE "data" /* a file giving the meaning of symbols used */ +#define FMASK 0660 /* file creation mask */ +#define HLOCK "perm" /* an empty file used for locking purposes */ +#define LLOCK "safelock" /* link to previous */ + +#ifdef UNIX +/* + * Define DEF_PAGER as your default pager, e.g. "/bin/cat" or "/usr/ucb/more" + * If defined, it can be overridden by the environment variable PAGER. + * Hack will use its internal pager if DEF_PAGER is not defined. + * (This might be preferable for security reasons.) + * #define DEF_PAGER ".../mydir/mypager" + */ + +/* + * If you define MAIL, then the player will be notified of new mail + * when it arrives. If you also define DEF_MAILREADER then this will + * be the default mail reader, and can be overridden by the environment + * variable MAILREADER; otherwise an internal pager will be used. + * A stat system call is done on the mailbox every MAILCKFREQ moves. + */ +/* #define MAIL */ +#define DEF_MAILREADER _PATH_MAIL /* or e.g. /bin/mail */ +#define MAILCKFREQ 100 + + +#define SHELL /* do not delete the '!' command */ +#define SUSPEND /* let ^Z suspend the game */ +#endif /* UNIX */ + +#ifdef CHDIR +/* + * If you define HACKDIR, then this will be the default playground; + * otherwise it will be the current directory. + */ +#ifdef QUEST +#define HACKDIR _PATH_QUEST +#else /* QUEST */ +#define HACKDIR _PATH_HACK +#endif /* QUEST */ + +/* + * Some system administrators are stupid enough to make Hack suid root + * or suid daemon, where daemon has other powers besides that of reading or + * writing Hack files. In such cases one should be careful with chdir's + * since the user might create files in a directory of his choice. + * Of course SECURE is meaningful only if HACKDIR is defined. + */ +#define SECURE /* do setuid(getuid()) after chdir() */ + +/* + * If it is desirable to limit the number of people that can play Hack + * simultaneously, define HACKDIR, SECURE and MAX_NR_OF_PLAYERS. + * #define MAX_NR_OF_PLAYERS 100 + */ +#endif /* CHDIR */ + +/* size of terminal screen is (at least) (ROWNO+2) by COLNO */ +#define COLNO 80 +#define ROWNO 22 + +/* + * small signed integers (8 bits suffice) + * typedef char schar; + * will do when you have signed characters; otherwise use + * typedef short int schar; + */ +typedef short schar; + +/* + * small unsigned integers (8 bits suffice - but 7 bits do not) + * - these are usually object types; be careful with inequalities! - + * typedef unsigned char uchar; + * will be satisfactory if you have an "unsigned char" type; otherwise use + * typedef unsigned short int uchar; + */ +typedef unsigned char uchar; + +/* + * small integers in the range 0 - 127, usually coordinates + * although they are nonnegative they must not be declared unsigned + * since otherwise comparisons with signed quantities are done incorrectly + */ +typedef schar xchar; +typedef bool boolean; /* 0 or 1 */ +#define TRUE 1 +#define FALSE 0 + +/* + * Declaration of bitfields in various structs; if your C compiler + * doesnt handle bitfields well, e.g., if it is unable to initialize + * structs containing bitfields, then you might use + * #define Bitfield(x,n) uchar x + * since the bitfields used never have more than 7 bits. (Most have 1 bit.) + */ +#define Bitfield(x,n) unsigned x:n + +#define SIZE(x) (int)(sizeof(x) / sizeof(x[0])) + +#endif /* CONFIG */ diff --git a/hack/data b/hack/data new file mode 100644 index 0000000..5faa384 --- /dev/null +++ b/hack/data @@ -0,0 +1,232 @@ + Hack & Quest data file - version 1.0.3 +@ human (or you) +- a wall +| a wall ++ a door +. the floor of a room + a dark part of a room +# a corridor +} water filled area +< the staircase to the previous level +> the staircase to the next level +^ a trap +$ a pile, pot or chest of gold +%% a piece of food +! a potion +* a gem +? a scroll += a ring +/ a wand +[ a suit of armor +) a weapon +( a useful item (camera, key, rope etc.) +0 an iron ball +_ an iron chain +` an enormous rock +" an amulet +, a trapper +: a chameleon +; a giant eel +' a lurker above +& a demon +A a giant ant +B a giant bat +C a centaur; + Of all the monsters put together by the Greek imagination + the Centaurs (Kentauroi) constituted a class in themselves. + Despite a strong streak of sensuality in their make-up, + their normal behaviour was moral, and they took a kindly + thought of man's welfare. The attempted outrage of Nessos on + Deianeira, and that of the whole tribe of Centaurs on the + Lapith women, are more than offset by the hospitality of + Pholos and by the wisdom of Cheiron, physician, prophet, + lyrist, and the instructor of Achilles. Further, the Cen- + taurs were peculiar in that their nature, which united the + body of a horse with the trunk and head of a man, involved + an unthinkable duplication of vital organs and important + members. So grotesque a combination seems almost un-Greek. + These strange creatures were said to live in the caves and + clefts of the mountains, myths associating them especially + with the hills of Thessaly and the range of Erymanthos. + [Mythology of all races, Vol. 1, pp. 270-271] +D a dragon; + In the West the dragon was the natural enemy of man. Although + preferring to live in bleak and desolate regions, whenever it was + seen among men it left in its wake a trail of destruction and + disease. Yet any attempt to slay this beast was a perilous under- + taking. For the dragon's assailant had to contend not only with + clouds of sulphurous fumes pouring from its fire-breathing nos- + trils, but also with the thrashings of its tail, the most deadly + part of its serpent-like body. + [From: Mythical Beasts by Deirdre Headon (The Leprechaun Library)] +E a floating eye +F a freezing sphere +G a gnome; + ... And then a gnome came by, carrying a bundle, an old fellow + three times as large as an imp and wearing clothes of a sort, + especially a hat. And he was clearly just as frightened as the + imps though he could not go so fast. Ramon Alonzo saw that there + must be some great trouble that was vexing magical things; and, + since gnomes speak the language of men, and will answer if spoken + to gently, he raised his hat, and asked of the gnome his name. + The gnome did not stop his hasty shuffle a moment as he answered + 'Alaraba' and grabbed the rim of his hat but forgot to doff it. + 'What is the trouble, Alaraba?' said Ramon Alonzo. + 'White magic. Run!' said the gnome ... + [From: The Charwoman's Shadow, by Lord Dunsany.] +H a hobgoblin; + Hobgoblin. Used by the Puritans and in later times for + wicked goblin spirits, as in Bunyan's 'Hobgoblin nor foul + friend', but its more correct use is for the friendly spir- + its of the brownie type. In 'A midsummer night's dream' a + fairy says to Shakespeare's Puck: + Those that Hobgoblin call you, and sweet Puck, + You do their work, and they shall have good luck: + Are you not he? + and obviously Puck would not wish to be called a hobgoblin + if that was an ill-omened word. + Hobgoblins are on the whole, good-humoured and ready to be + helpful, but fond of practical joking, and like most of the + fairies rather nasty people to annoy. Boggarts hover on the + verge of hobgoblindom. Bogles are just over the edge. + One Hob mentioned by Henderson, was Hob Headless who haunted + the road between Hurworth and Neasham, but could not cross + the little river Kent, which flowed into the Tess. He was + exorcised and laid under a large stone by the roadside for + ninety-nine years and a day. If anyone was so unwary as to + sit on that stone, he would be unable to quit it for ever. + The ninety-nine years is nearly up, so trouble may soon be + heard of on the road between Hurworth and Neasham. + [Katharine Briggs, A dictionary of Fairies] +I an invisible stalker +J a jackal +K a kobold +L a leprechaun; + The Irish Leprechaun is the Faeries' shoemaker and is known + under various names in different parts of Ireland: Cluri- + caune in Cork, Lurican in Kerry, Lurikeen in Kildare and Lu- + rigadaun in Tipperary. Although he works for the Faeries, + the Leprechaun is not of the same species. He is small, has + dark skin and wears strange clothes. His nature has some- + thing of the manic-depressive about it: first he is quite + happy, whistling merrily as he nails a sole on to a shoe; a + few minutes later, he is sullen and morose, drunk on his + home-made heather ale. The Leprechaun's two great loves are + tobacco and whiskey, and he is a first-rate con-man, impos- + sible to out-fox. No one, no matter how clever, has ever + managed to cheat him out of his hidden pot of gold or his + magic shilling. At the last minute he always thinks of some + way to divert his captor's attention and vanishes in the + twinkling of an eye. + [From: A Field Guide to the Little People + by Nancy Arrowsmith & George Moorse. ] +M a mimic +N a nymph +O an orc +P a purple worm +Q a quasit +R a rust monster +S a snake +T a troll +U an umber hulk +V a vampire +W a wraith +X a xorn +Y a yeti +Z a zombie +a an acid blob +b a giant beetle +c a cockatrice; + Once in a great while, when the positions of the stars are + just right, a seven-year-old rooster will lay an egg. Then, + along will come a snake, to coil around the egg, or a toad, + to squat upon the egg, keeping it warm and helping it to + hatch. When it hatches, out comes a creature called basil- + isk, or cockatrice, the most deadly of all creatures. A sin- + gle glance from its yellow, piercing toad's eyes will kill + both man and beast. Its power of destruction is said to be + so great that sometimes simply to hear its hiss can prove + fatal. Its breath is so venomous that it causes all vege- + tation to wither. + There is, however, one creature which can withstand the + basilisk's deadly gaze, and this is the weasel. No one knows + why this is so, but although the fierce weasel can slay the + basilisk, it will itself be killed in the struggle. Perhaps + the weasel knows the basilisk's fatal weakness: if it ever + sees its own reflection in a mirror it will perish instant- + ly. But even a dead basilisk is dangerous, for it is said + that merely touching its lifeless body can cause a person to + sicken and die. + [From: Mythical Beasts by Deirdre Headon (The Leprechaun + Library) and other sources. ] +d a dog +e an ettin +f a fog cloud +g a gelatinous cube +h a homunculus +i an imp; + ... imps ... little creatures of two feet high that could + gambol and jump prodigiously; ... + [From: The Charwoman's Shadow, by Lord Dunsany.] + + An 'imp' is an off-shoot or cutting. Thus an 'ymp tree' was + a grafted tree, or one grown from a cutting, not from seed. + 'Imp' properly means a small devil, an off-shoot of Satan, + but the distinction between goblins or bogles and imps from + hell is hard to make, and many in the Celtic countries as + well as the English Puritans regarded all fairies as devils. + The fairies of tradition often hover uneasily between the + ghostly and the diabolic state. + [Katharine Briggs, A dictionary of Fairies] +j a jaguar +k a killer bee +l a leocrotta +m a minotaur +n a nurse +o an owlbear +p a piercer +q a quivering blob +r a giant rat +s a scorpion +t a tengu; + The tengu was the most troublesome creature of Japanese + legend. Part bird and part man, with red beak for a nose + and flashing eyes, the tengu was notorious for stirring up + feuds and prolonging enmity between families. Indeed, the + belligerent tengus were supposed to have been man's first + instructors in the use of arms. + [From: Mythical Beasts by Deirdre Headon + (The Leprechaun Library). ] +u a unicorn; + Men have always sought the elusive unicorn, for the single + twisted horn which projected from its forehead was thought + to be a powerful talisman. It was said that the unicorn had + simply to dip the tip of its horn in a muddy pool for the + water to become pure. Men also believed that to drink from + this horn was a protection against all sickness, and that if + the horn was ground to a powder it would act as an antidote + to all poisons. Less than 200 years ago in France, the horn + of a unicorn was used in a ceremony to test the royal food + for poison. + Although only the size of a small horse, the unicorn is a + very fierce beast, capable of killing an elephant with a + single thrust from its horn. Its fleetness of foot also + makes this solitary creature difficult to capture. However, + it can be tamed and captured by a maiden. Made gentle by the + sight of a virgin, the unicorn can be lured to lay its head + in her lap, and in this docile mood, the maiden may secure + it with a golden rope. + [From: Mythical Beasts by Deirdre Headon + (The Leprechaun Library). ] +v a violet fungi +w a long worm; + From its teeth the crysknife can be manufactured. +~ the tail of a long worm +x a xan; + The xan were animals sent to prick the legs of the Lords of Xibalba. +y a yellow light +z a zruty; + The zruty are wild and gigantic beings, living in the wildernesses + of the Tatra mountains. +1 The wizard of Yendor +2 The mail daemon diff --git a/hack/date.h b/hack/date.h new file mode 100644 index 0000000..9a7ef76 --- /dev/null +++ b/hack/date.h @@ -0,0 +1,2 @@ + +char datestring[] = "Tue Jul 23 1985"; diff --git a/hack/def.edog.h b/hack/def.edog.h new file mode 100644 index 0000000..a5c2b46 --- /dev/null +++ b/hack/def.edog.h @@ -0,0 +1,12 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* def.edog.h - version 1.0.2 */ + +struct edog { + long hungrytime; /* at this time dog gets hungry */ + long eattime; /* dog is eating */ + long droptime; /* moment dog dropped object */ + unsigned dropdist; /* dist of drpped obj from @ */ + unsigned apport; /* amount of training */ + long whistletime; /* last time he whistled */ +}; +#define EDOG(mp) ((struct edog *)(&(mp->mextra[0]))) diff --git a/hack/def.eshk.h b/hack/def.eshk.h new file mode 100644 index 0000000..2ebf280 --- /dev/null +++ b/hack/def.eshk.h @@ -0,0 +1,24 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* def.eshk.h - version 1.0.2 : added 'following' */ + +#define BILLSZ 200 +struct bill_x { + unsigned bo_id; + unsigned useup:1; + unsigned bquan:7; + unsigned price; /* price per unit */ +}; + +struct eshk { + long int robbed; /* amount stolen by most recent customer */ + boolean following; /* following customer since he owes us sth */ + schar shoproom; /* index in rooms; set by inshop() */ + coord shk; /* usual position shopkeeper */ + coord shd; /* position shop door */ + int shoplevel; /* level of his shop */ + int billct; + struct bill_x bill[BILLSZ]; + int visitct; /* nr of visits by most recent customer */ + char customer[PL_NSIZ]; /* most recent customer */ + char shknam[PL_NSIZ]; +}; diff --git a/hack/def.flag.h b/hack/def.flag.h new file mode 100644 index 0000000..221f33d --- /dev/null +++ b/hack/def.flag.h @@ -0,0 +1,42 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* def.flag.h - version 1.0.3 */ + +struct flag { + unsigned ident; /* social security number for each monster */ + unsigned debug:1; /* in debugging mode */ +#define wizard flags.debug + unsigned toplin:2; /* a top line (message) has been printed */ + /* 0: top line empty; 2: no --More-- reqd. */ + unsigned cbreak:1; /* in cbreak mode, rogue format */ + unsigned standout:1; /* use standout for --More-- */ + unsigned nonull:1; /* avoid sending nulls to the terminal */ + unsigned time:1; /* display elapsed 'time' */ + unsigned nonews:1; /* suppress news printing */ + unsigned notombstone:1; + unsigned end_top, end_around; /* describe desired score list */ + unsigned end_own:1; /* idem (list all own scores) */ + unsigned no_rest_on_space:1; /* spaces are ignored */ + unsigned beginner:1; + unsigned female:1; + unsigned invlet_constant:1; /* let objects keep their + inventory symbol */ + unsigned move:1; + unsigned mv:1; + unsigned run:3; /* 0: h (etc), 1: H (etc), 2: fh (etc) */ + /* 3: FH, 4: ff+, 5: ff-, 6: FF+, 7: FF- */ + unsigned nopick:1; /* do not pickup objects */ + unsigned echo:1; /* 1 to echo characters */ + unsigned botl:1; /* partially redo status line */ + unsigned botlx:1; /* print an entirely new bottom line */ + unsigned nscrinh:1; /* inhibit nscr() in pline(); */ + unsigned made_amulet:1; + unsigned no_of_wizards:2;/* 0, 1 or 2 (wizard and his shadow) */ + /* reset from 2 to 1, but never to 0 */ + unsigned moonphase:3; +#define NEW_MOON 0 +#define FULL_MOON 4 + +}; + +extern struct flag flags; + diff --git a/hack/def.func_tab.h b/hack/def.func_tab.h new file mode 100644 index 0000000..ceb952c --- /dev/null +++ b/hack/def.func_tab.h @@ -0,0 +1,13 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* def.func_tab.h - version 1.0.2 */ +/* $DragonFly: src/games/hack/def.func_tab.h,v 1.3 2006/08/21 19:45:32 pavalos Exp $ */ + +struct func_tab { + char f_char; + int (*f_funct)(void); +}; + +struct ext_func_tab { + const char *ef_txt; + int (*ef_funct)(void); +}; diff --git a/hack/def.gold.h b/hack/def.gold.h new file mode 100644 index 0000000..c279a45 --- /dev/null +++ b/hack/def.gold.h @@ -0,0 +1,12 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* def.gold.h - version 1.0.2 */ +/* $DragonFly: src/games/hack/def.gold.h,v 1.2 2006/08/21 19:45:32 pavalos Exp $ */ + +struct gold { + struct gold *ngold; + xchar gx,gy; + long amount; +}; + +extern struct gold *fgold; +#define newgold() alloc(sizeof(struct gold)) diff --git a/hack/def.mkroom.h b/hack/def.mkroom.h new file mode 100644 index 0000000..f57f34c --- /dev/null +++ b/hack/def.mkroom.h @@ -0,0 +1,26 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* def.mkroom.h - version 1.0.3 */ + +struct mkroom { + schar lx, hx, ly, hy; /* usually xchar, but hx may be -1 */ + schar rtype, rlit, doorct, fdoor; +}; + +#define MAXNROFROOMS 15 +extern struct mkroom rooms[MAXNROFROOMS + 1]; + +#define DOORMAX 100 +extern coord doors[DOORMAX]; + +/* various values of rtype */ +/* 0: ordinary room; 8-15: various shops */ +/* Note: some code assumes that >= 8 means shop, so be careful when adding + new roomtypes */ +#define SWAMP 3 +#define VAULT 4 +#define BEEHIVE 5 +#define MORGUE 6 +#define ZOO 7 +#define SHOPBASE 8 +#define WANDSHOP 9 +#define GENERAL 15 diff --git a/hack/def.monst.h b/hack/def.monst.h new file mode 100644 index 0000000..8424396 --- /dev/null +++ b/hack/def.monst.h @@ -0,0 +1,60 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* def.monst.h - version 1.0.2 */ +/* $DragonFly: src/games/hack/def.monst.h,v 1.3 2006/08/21 19:45:32 pavalos Exp $ */ + +struct monst { + struct monst *nmon; + struct permonst *data; + unsigned m_id; + xchar mx, my; + xchar mdx, mdy; /* if mdispl then pos where last displayed */ +#define MTSZ 4 + coord mtrack[MTSZ]; /* monster track */ + schar mhp, mhpmax; + char mappearance; /* nonzero for undetected 'M's and for '1's */ + Bitfield(mimic, 1); /* undetected mimic */ + Bitfield(mdispl, 1); /* mdx, mdy valid */ + Bitfield(minvis, 1); /* invisible */ + Bitfield(cham, 1); /* shape-changer */ + Bitfield(mhide, 1); /* hides beneath objects */ + Bitfield(mundetected, 1); /* not seen in present hiding place */ + Bitfield(mspeed, 2); + Bitfield(msleep, 1); + Bitfield(mfroz, 1); + Bitfield(mconf, 1); + Bitfield(mflee, 1); /* fleeing */ + Bitfield(mfleetim, 7); /* timeout for mflee */ + Bitfield(mcan, 1); /* has been cancelled */ + Bitfield(mtame, 1); /* implies peaceful */ + Bitfield(mpeaceful, 1); /* does not attack unprovoked */ + Bitfield(isshk, 1); /* is shopkeeper */ + Bitfield(isgd, 1); /* is guard */ + Bitfield(mcansee, 1); /* cansee 1, temp.blinded 0, blind 0 */ + Bitfield(mblinded, 7); /* cansee 0, temp.blinded n, blind 0 */ + Bitfield(mtrapped, 1); /* trapped in a pit or bear trap */ + Bitfield(mnamelth, 6); /* length of name (following mxlth) */ +#ifndef NOWORM + Bitfield(wormno, 5); /* at most 31 worms on any level */ +#endif /* NOWORM */ + unsigned mtrapseen; /* bitmap of traps we've been trapped in */ + long mlstmv; /* prevent two moves at once */ + struct obj *minvent; + long mgold; + unsigned mxlth; /* length of following data */ + /* in order to prevent alignment problems mextra should + be (or follow) a long int */ + long mextra[1]; /* monster dependent info */ +}; + +#define newmonst(xl) alloc((unsigned)(xl) + sizeof(struct monst)) + +extern struct monst *fmon; +extern struct monst *fallen_down; + +/* these are in mspeed */ +#define MSLOW 1 /* slow monster */ +#define MFAST 2 /* speeded monster */ + +#define NAME(mtmp) (((char *)mtmp->mextra) + mtmp->mxlth) +#define MREGEN "TVi1" +#define UNDEAD "ZVW " diff --git a/hack/def.obj.h b/hack/def.obj.h new file mode 100644 index 0000000..cbdf091 --- /dev/null +++ b/hack/def.obj.h @@ -0,0 +1,48 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* def.obj.h - version 1.0.3 */ + +struct obj { + struct obj *nobj; + unsigned o_id; + unsigned o_cnt_id; /* id of container object is in */ + xchar ox ,oy; + xchar odx, ody; + uchar otyp; + uchar owt; + uchar quan; /* use oextra for tmp gold objects */ + schar spe; /* quality of weapon, armor or ring (+ or -) + number of charges for wand ( >= -1 ) + special for uball and amulet %% BAH */ + char olet; + char invlet; + Bitfield(oinvis, 1); /* not yet implemented */ + Bitfield(odispl, 1); + Bitfield(known, 1); /* exact nature known */ + Bitfield(dknown, 1); /* color or text known */ + Bitfield(cursed, 1); + Bitfield(unpaid, 1); /* on some bill */ + Bitfield(rustfree, 1); + Bitfield(onamelth, 6); + long age; /* creation date */ + long owornmask; +#define W_ARM 01L +#define W_ARM2 02L +#define W_ARMH 04L +#define W_ARMS 010L +#define W_ARMG 020L +#define W_ARMOR (W_ARM | W_ARM2 | W_ARMH | W_ARMS | W_ARMG) +#define W_RINGL 010000L /* make W_RINGL = RING_LEFT (see uprop) */ +#define W_RINGR 020000L +#define W_RING (W_RINGL | W_RINGR) +#define W_WEP 01000L +#define W_BALL 02000L +#define W_CHAIN 04000L + long oextra[1]; /* used for name of ordinary objects - length + is flexible; amount for tmp gold objects */ +}; + +extern struct obj *fobj; + +#define newobj(xl) alloc((unsigned)(xl) + sizeof(struct obj)) +#define ONAME(otmp) ((char *)otmp->oextra) +#define OGOLD(otmp) (otmp->oextra[0]) diff --git a/hack/def.objclass.h b/hack/def.objclass.h new file mode 100644 index 0000000..e388554 --- /dev/null +++ b/hack/def.objclass.h @@ -0,0 +1,62 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* def.objclass.h - version 1.0.3 */ +/* $DragonFly: src/games/hack/def.objclass.h,v 1.3 2006/08/21 19:45:32 pavalos Exp $ */ + +#ifndef _DEF_OBJCLASS_H_ +#define _DEF_OBJCLASS_H_ +/* definition of a class of objects */ + +struct objclass { + const char *oc_name; /* actual name */ + const char *oc_descr; /* description when name unknown */ + char *oc_uname; /* called by user */ + Bitfield(oc_name_known, 1); + Bitfield(oc_merge, 1); /* merge otherwise equal objects */ + char oc_olet; + schar oc_prob; /* probability for mkobj() */ + schar oc_delay; /* delay when using such an object */ + uchar oc_weight; + schar oc_oc1, oc_oc2; + int oc_oi; +#define nutrition oc_oi /* for foods */ +#define a_ac oc_oc1 /* for armors - only used in ARM_BONUS */ +#define ARM_BONUS(obj) ((10 - objects[obj->otyp].a_ac) + obj->spe) +#define a_can oc_oc2 /* for armors */ +#define bits oc_oc1 /* for wands and rings */ + /* wands */ +#define NODIR 1 +#define IMMEDIATE 2 +#define RAY 4 + /* rings */ +#define SPEC 1 /* +n is meaningful */ +#define wldam oc_oc1 /* for weapons and PICK_AXE */ +#define wsdam oc_oc2 /* for weapons and PICK_AXE */ +#define g_val oc_oi /* for gems: value on exit */ +}; + +extern struct objclass objects[]; + +/* definitions of all object-symbols */ + +#define ILLOBJ_SYM '\\' +#define AMULET_SYM '"' +#define FOOD_SYM '%' +#define WEAPON_SYM ')' +#define TOOL_SYM '(' +#define BALL_SYM '0' +#define CHAIN_SYM '_' +#define ROCK_SYM '`' +#define ARMOR_SYM '[' +#define POTION_SYM '!' +#define SCROLL_SYM '?' +#define WAND_SYM '/' +#define RING_SYM '=' +#define GEM_SYM '*' +/* Other places with explicit knowledge of object symbols: + * hack.mklev.c: "=/)%?![<>"[rn2(9)]; + * hack.mkobj.c: char mkobjstr[] = "))[[!!!!????%%%%/=**))[[!!!!????%%%%/=**(%"; + * hack.apply.c: otmp = getobj("0#%", "put in"); + * hack.eat.c: otmp = getobj("%", "eat"); + * hack.invent.c: else if (strchr("!%?[()= /\"0", sym)) { + */ +#endif /* _DEF_OBJCLASS_H_ */ diff --git a/hack/def.objects.h b/hack/def.objects.h new file mode 100644 index 0000000..a2416fb --- /dev/null +++ b/hack/def.objects.h @@ -0,0 +1,290 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* def.objects.h - version 1.0.3 */ +/* $DragonFly: src/games/hack/def.objects.h,v 1.2 2006/08/21 19:45:32 pavalos Exp $ */ + +/* objects have letter " % ) ( 0 _ ` [ ! ? / = * */ +#include <string.h> +#include "config.h" +#include "def.objclass.h" + +struct objclass objects[] = { + + { "strange object", NULL, NULL, 1, 0, + ILLOBJ_SYM, 0, 0, 0, 0, 0, 0 }, + { "amulet of Yendor", NULL, NULL, 1, 0, + AMULET_SYM, 100, 0, 2, 0, 0, 0 }, + +#define FOOD(name,prob,delay,weight,nutrition) { name, NULL, NULL, 1, 1,\ + FOOD_SYM, prob, delay, weight, 0, 0, nutrition } + +/* dog eats foods 0-4 but prefers 1 above 0,2,3,4 */ +/* food 4 can be read */ +/* food 5 improves your vision */ +/* food 6 makes you stronger (like Popeye) */ +/* foods CORPSE up to CORPSE+52 are cadavers */ + + FOOD("food ration", 50, 5, 4, 800), + FOOD("tripe ration", 20, 1, 2, 200), + FOOD("pancake", 3, 1, 1, 200), + FOOD("dead lizard", 3, 0, 1, 40), + FOOD("fortune cookie", 7, 0, 1, 40), + FOOD("carrot", 2, 0, 1, 50), + FOOD("tin", 7, 0, 1, 0), + FOOD("orange", 1, 0, 1, 80), + FOOD("apple", 1, 0, 1, 50), + FOOD("pear", 1, 0, 1, 50), + FOOD("melon", 1, 0, 1, 100), + FOOD("banana", 1, 0, 1, 80), + FOOD("candy bar", 1, 0, 1, 100), + FOOD("egg", 1, 0, 1, 80), + FOOD("clove of garlic", 1, 0, 1, 40), + FOOD("lump of royal jelly", 0, 0, 1, 200), + + FOOD("dead human", 0, 4, 40, 400), + FOOD("dead giant ant", 0, 1, 3, 30), + FOOD("dead giant bat", 0, 1, 3, 30), + FOOD("dead centaur", 0, 5, 50, 500), + FOOD("dead dragon", 0, 15, 150, 1500), + FOOD("dead floating eye", 0, 1, 1, 10), + FOOD("dead freezing sphere", 0, 1, 1, 10), + FOOD("dead gnome", 0, 1, 10, 100), + FOOD("dead hobgoblin", 0, 2, 20, 200), + FOOD("dead stalker", 0, 4, 40, 400), + FOOD("dead jackal", 0, 1, 10, 100), + FOOD("dead kobold", 0, 1, 10, 100), + FOOD("dead leprechaun", 0, 4, 40, 400), + FOOD("dead mimic", 0, 4, 40, 400), + FOOD("dead nymph", 0, 4, 40, 400), + FOOD("dead orc", 0, 2, 20, 200), + FOOD("dead purple worm", 0, 7, 70, 700), + FOOD("dead quasit", 0, 2, 20, 200), + FOOD("dead rust monster", 0, 5, 50, 500), + FOOD("dead snake", 0, 1, 10, 100), + FOOD("dead troll", 0, 4, 40, 400), + FOOD("dead umber hulk", 0, 5, 50, 500), + FOOD("dead vampire", 0, 4, 40, 400), + FOOD("dead wraith", 0, 1, 1, 10), + FOOD("dead xorn", 0, 7, 70, 700), + FOOD("dead yeti", 0, 7, 70, 700), + FOOD("dead zombie", 0, 1, 3, 30), + FOOD("dead acid blob", 0, 1, 3, 30), + FOOD("dead giant beetle", 0, 1, 1, 10), + FOOD("dead cockatrice", 0, 1, 3, 30), + FOOD("dead dog", 0, 2, 20, 200), + FOOD("dead ettin", 0, 1, 3, 30), + FOOD("dead fog cloud", 0, 1, 1, 10), + FOOD("dead gelatinous cube", 0, 1, 10, 100), + FOOD("dead homunculus", 0, 2, 20, 200), + FOOD("dead imp", 0, 1, 1, 10), + FOOD("dead jaguar", 0, 3, 30, 300), + FOOD("dead killer bee", 0, 1, 1, 10), + FOOD("dead leocrotta", 0, 5, 50, 500), + FOOD("dead minotaur", 0, 7, 70, 700), + FOOD("dead nurse", 0, 4, 40, 400), + FOOD("dead owlbear", 0, 7, 70, 700), + FOOD("dead piercer", 0, 2, 20, 200), + FOOD("dead quivering blob", 0, 1, 10, 100), + FOOD("dead giant rat", 0, 1, 3, 30), + FOOD("dead giant scorpion", 0, 1, 10, 100), + FOOD("dead tengu", 0, 3, 30, 300), + FOOD("dead unicorn", 0, 3, 30, 300), + FOOD("dead violet fungi", 0, 1, 10, 100), + FOOD("dead long worm", 0, 5, 50, 500), +/* %% wt of long worm should be proportional to its length */ + FOOD("dead xan", 0, 3, 30, 300), + FOOD("dead yellow light", 0, 1, 1, 10), + FOOD("dead zruty", 0, 6, 60, 600), + +/* weapons ... - ROCK come several at a time */ +/* weapons ... - (ROCK-1) are shot using idem+(BOW-ARROW) */ +/* weapons AXE, SWORD, THSWORD are good for worm-cutting */ +/* weapons (PICK-)AXE, DAGGER, CRYSKNIFE are good for tin-opening */ +#define WEAPON(name,prob,wt,ldam,sdam) { name, NULL, NULL, 1, 0 /*%%*/,\ + WEAPON_SYM, prob, 0, wt, ldam, sdam, 0 } + + WEAPON("arrow", 7, 0, 6, 6), + WEAPON("sling bullet", 7, 0, 4, 6), + WEAPON("crossbow bolt", 7, 0, 4, 6), + WEAPON("dart", 7, 0, 3, 2), + WEAPON("rock", 6, 1, 3, 3), + WEAPON("boomerang", 2, 3, 9, 9), + WEAPON("mace", 9, 3, 6, 7), + WEAPON("axe", 6, 3, 6, 4), + WEAPON("flail", 6, 3, 6, 5), + WEAPON("long sword", 8, 3, 8, 12), + WEAPON("two handed sword", 6, 4, 12, 6), + WEAPON("dagger", 6, 3, 4, 3), + WEAPON("worm tooth", 0, 4, 2, 2), + WEAPON("crysknife", 0, 3, 10, 10), + WEAPON("spear", 6, 3, 6, 8), + WEAPON("bow", 6, 3, 4, 6), + WEAPON("sling", 5, 3, 6, 6), + WEAPON("crossbow", 6, 3, 4, 6), + + { "whistle", "whistle", NULL, 0, 0, + TOOL_SYM, 90, 0, 2, 0, 0, 0 }, + { "magic whistle", "whistle", NULL, 0, 0, + TOOL_SYM, 10, 0, 2, 0, 0, 0 }, + { "expensive camera", NULL, NULL, 1, 1, + TOOL_SYM, 0, 0, 3, 0, 0, 0 }, + { "ice box", "large box", NULL, 0, 0, + TOOL_SYM, 0, 0, 40, 0, 0, 0 }, + { "pick-axe", NULL, NULL, 1, 1, + TOOL_SYM, 0, 0, 5, 6, 3, 0 }, + { "can opener", NULL, NULL, 1, 1, + TOOL_SYM, 0, 0, 1, 0, 0, 0 }, + { "heavy iron ball", NULL, NULL, 1, 0, + BALL_SYM, 100, 0, 20, 0, 0, 0 }, + { "iron chain", NULL, NULL, 1, 0, + CHAIN_SYM, 100, 0, 20, 0, 0, 0 }, + { "enormous rock", NULL, NULL, 1, 0, + ROCK_SYM, 100, 0, 200 /* > MAX_CARR_CAP */, 0, 0, 0 }, + +#define ARMOR(name,prob,delay,ac,can) { name, NULL, NULL, 1, 0,\ + ARMOR_SYM, prob, delay, 8, ac, can, 0 } + ARMOR("helmet", 3, 1, 9, 0), + ARMOR("plate mail", 5, 5, 3, 2), + ARMOR("splint mail", 8, 5, 4, 1), + ARMOR("banded mail", 10, 5, 4, 0), + ARMOR("chain mail", 10, 5, 5, 1), + ARMOR("scale mail", 10, 5, 6, 0), + ARMOR("ring mail", 15, 5, 7, 0), + /* the armors below do not rust */ + ARMOR("studded leather armor", 13, 3, 7, 1), + ARMOR("leather armor", 17, 3, 8, 0), + ARMOR("elven cloak", 5, 0, 9, 3), + ARMOR("shield", 3, 0, 9, 0), + ARMOR("pair of gloves", 1, 1, 9, 0), + +#define POTION(name,color) { name, color, NULL, 0, 1,\ + POTION_SYM, 0, 0, 2, 0, 0, 0 } + + POTION("restore strength", "orange"), + POTION("booze", "bubbly"), + POTION("invisibility", "glowing"), + POTION("fruit juice", "smoky"), + POTION("healing", "pink"), + POTION("paralysis", "puce"), + POTION("monster detection", "purple"), + POTION("object detection", "yellow"), + POTION("sickness", "white"), + POTION("confusion", "swirly"), + POTION("gain strength", "purple-red"), + POTION("speed", "ruby"), + POTION("blindness", "dark green"), + POTION("gain level", "emerald"), + POTION("extra healing", "sky blue"), + POTION("levitation", "brown"), + POTION(NULL, "brilliant blue"), + POTION(NULL, "clear"), + POTION(NULL, "magenta"), + POTION(NULL, "ebony"), + +#define SCROLL(name,text,prob) { name, text, NULL, 0, 1,\ + SCROLL_SYM, prob, 0, 3, 0, 0, 0 } + SCROLL("mail", "KIRJE", 0), + SCROLL("enchant armor", "ZELGO MER", 6), + SCROLL("destroy armor", "JUYED AWK YACC", 5), + SCROLL("confuse monster", "NR 9", 5), + SCROLL("scare monster", "XIXAXA XOXAXA XUXAXA", 4), + SCROLL("blank paper", "READ ME", 3), + SCROLL("remove curse", "PRATYAVAYAH", 6), + SCROLL("enchant weapon", "DAIYEN FOOELS", 6), + SCROLL("damage weapon", "HACKEM MUCHE", 5), + SCROLL("create monster", "LEP GEX VEN ZEA", 5), + SCROLL("taming", "PRIRUTSENIE", 1), + SCROLL("genocide", "ELBIB YLOH",2), + SCROLL("light", "VERR YED HORRE", 10), + SCROLL("teleportation", "VENZAR BORGAVVE", 5), + SCROLL("gold detection", "THARR", 4), + SCROLL("food detection", "YUM YUM", 1), + SCROLL("identify", "KERNOD WEL", 18), + SCROLL("magic mapping", "ELAM EBOW", 5), + SCROLL("amnesia", "DUAM XNAHT", 3), + SCROLL("fire", "ANDOVA BEGARIN", 5), + SCROLL("punishment", "VE FORBRYDERNE", 1), + SCROLL(NULL, "VELOX NEB", 0), + SCROLL(NULL, "FOOBIE BLETCH", 0), + SCROLL(NULL, "TEMOV", 0), + SCROLL(NULL, "GARVEN DEH", 0), + +#define WAND(name,metal,prob,flags) { name, metal, NULL, 0, 0,\ + WAND_SYM, prob, 0, 3, flags, 0, 0 } + + WAND("light", "iridium", 10, NODIR), + WAND("secret door detection", "tin", 5, NODIR), + WAND("create monster", "platinum", 5, NODIR), + WAND("wishing", "glass", 1, NODIR), + WAND("striking", "zinc", 9, IMMEDIATE), + WAND("slow monster", "balsa", 5, IMMEDIATE), + WAND("speed monster", "copper", 5, IMMEDIATE), + WAND("undead turning", "silver", 5, IMMEDIATE), + WAND("polymorph", "brass", 5, IMMEDIATE), + WAND("cancellation", "maple", 5, IMMEDIATE), + WAND("teleportation", "pine", 5, IMMEDIATE), + WAND("make invisible", "marble", 9, IMMEDIATE), + WAND("digging", "iron", 5, RAY), + WAND("magic missile", "aluminium", 10, RAY), + WAND("fire", "steel", 5, RAY), + WAND("sleep", "curved", 5, RAY), + WAND("cold", "short", 5, RAY), + WAND("death", "long", 1, RAY), + WAND(NULL, "oak", 0, 0), + WAND(NULL, "ebony", 0, 0), + WAND(NULL, "runed", 0, 0), + +#define RING(name,stone,spec) { name, stone, NULL, 0, 0,\ + RING_SYM, 0, 0, 1, spec, 0, 0 } + + RING("adornment", "engagement", 0), + RING("teleportation", "wooden", 0), + RING("regeneration", "black onyx", 0), + RING("searching", "topaz", 0), + RING("see invisible", "pearl", 0), + RING("stealth", "sapphire", 0), + RING("levitation", "moonstone", 0), + RING("poison resistance", "agate", 0), + RING("aggravate monster", "tiger eye", 0), + RING("hunger", "shining", 0), + RING("fire resistance", "gold", 0), + RING("cold resistance", "copper", 0), + RING("protection from shape changers", "diamond", 0), + RING("conflict", "jade", 0), + RING("gain strength", "ruby", SPEC), + RING("increase damage", "silver", SPEC), + RING("protection", "granite", SPEC), + RING("warning", "wire", 0), + RING("teleport control", "iron", 0), + RING(NULL, "ivory", 0), + RING(NULL, "blackened", 0), + +/* gems ************************************************************/ +#define GEM(name,color,prob,gval) { name, color, NULL, 0, 1,\ + GEM_SYM, prob, 0, 1, 0, 0, gval } + GEM("diamond", "blue", 1, 4000), + GEM("ruby", "red", 1, 3500), + GEM("sapphire", "blue", 1, 3000), + GEM("emerald", "green", 1, 2500), + GEM("turquoise", "green", 1, 2000), + GEM("aquamarine", "blue", 1, 1500), + GEM("tourmaline", "green", 1, 1000), + GEM("topaz", "yellow", 1, 900), + GEM("opal", "yellow", 1, 800), + GEM("garnet", "dark", 1, 700), + GEM("amethyst", "violet", 2, 650), + GEM("agate", "green", 2, 600), + GEM("onyx", "white", 2, 550), + GEM("jasper", "yellowish brown", 2, 500), + GEM("jade", "green", 2, 450), + GEM("worthless piece of blue glass", "blue", 20, 0), + GEM("worthless piece of red glass", "red", 20, 0), + GEM("worthless piece of yellow glass", "yellow", 20, 0), + GEM("worthless piece of green glass", "green", 20, 0), + { NULL, NULL, NULL, 0, 0, ILLOBJ_SYM, 0, 0, 0, 0, 0, 0 } +}; + +char obj_symbols[] = { + ILLOBJ_SYM, AMULET_SYM, FOOD_SYM, WEAPON_SYM, TOOL_SYM, + BALL_SYM, CHAIN_SYM, ROCK_SYM, ARMOR_SYM, POTION_SYM, SCROLL_SYM, + WAND_SYM, RING_SYM, GEM_SYM, 0 }; +int bases[sizeof(obj_symbols)]; diff --git a/hack/def.permonst.h b/hack/def.permonst.h new file mode 100644 index 0000000..591a6c6 --- /dev/null +++ b/hack/def.permonst.h @@ -0,0 +1,27 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* def.permonst.h - version 1.0.2 */ +/* $DragonFly: src/games/hack/def.permonst.h,v 1.2 2005/05/22 03:37:05 y0netan1 Exp $ */ + +struct permonst { + const char *mname; + char mlet; + schar mlevel, mmove, ac, damn, damd; + unsigned pxlth; +}; + +extern struct permonst mons[]; +#define PM_ACID_BLOB &mons[7] +#define PM_ZOMBIE &mons[13] +#define PM_PIERCER &mons[17] +#define PM_KILLER_BEE &mons[26] +#define PM_WRAITH &mons[33] +#define PM_MIMIC &mons[37] +#define PM_VAMPIRE &mons[43] +#define PM_CHAMELEON &mons[47] +#define PM_DEMON &mons[54] +#define PM_MINOTAUR &mons[55] /* last in mons array */ +#define PM_SHK &mons[56] /* very last */ +#define PM_GHOST &pm_ghost +#define PM_EEL &pm_eel +#define PM_WIZARD &pm_wizard +#define CMNUM 55 /* number of common monsters */ diff --git a/hack/def.rm.h b/hack/def.rm.h new file mode 100644 index 0000000..46f3975 --- /dev/null +++ b/hack/def.rm.h @@ -0,0 +1,53 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* def.rm.h - version 1.0.2 */ +/* $DragonFly: src/games/hack/def.rm.h,v 1.2 2004/11/06 12:29:17 eirikn Exp $ */ + +/* Level location types */ +#define HWALL 1 +#define VWALL 2 +#define SDOOR 3 +#define SCORR 4 +#define LDOOR 5 +#define POOL 6 /* not yet fully implemented */ + /* this should in fact be a bit like lit */ +#define DOOR 7 +#define CORR 8 +#define ROOM 9 +#define STAIRS 10 + +/* + * Avoid using the level types in inequalities: + * these types are subject to change. + * Instead, use one of the macros below. + */ +#define IS_WALL(typ) ((typ) <= VWALL) +#define IS_ROCK(typ) ((typ) < POOL) /* absolutely nonaccessible */ +#define ACCESSIBLE(typ) ((typ) >= DOOR) /* good position */ +#define IS_ROOM(typ) ((typ) >= ROOM) /* ROOM or STAIRS */ +#define ZAP_POS(typ) ((typ) > DOOR) + +/* + * A few of the associated symbols are not hardwired. + */ +#ifdef QUEST +#define CORR_SYM ':' +#else +#define CORR_SYM '#' +#endif /* QUEST */ +#define POOL_SYM '}' + +#define ERRCHAR '{' + +/* + * The structure describing a coordinate position. + * Before adding fields, remember that this will significantly affect + * the size of temporary files and save files. + */ +struct rm { + char scrsym; + unsigned typ:5; + unsigned new:1; + unsigned seen:1; + unsigned lit:1; +}; +extern struct rm levl[COLNO][ROWNO]; diff --git a/hack/def.trap.h b/hack/def.trap.h new file mode 100644 index 0000000..2d25ec1 --- /dev/null +++ b/hack/def.trap.h @@ -0,0 +1,27 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* def.trap.h - version 1.0.2 */ +/* $DragonFly: src/games/hack/def.trap.h,v 1.2 2006/08/21 19:45:32 pavalos Exp $ */ + +struct trap { + struct trap *ntrap; + xchar tx, ty; + unsigned ttyp:5; + unsigned tseen:1; + unsigned once:1; +}; + +extern struct trap *ftrap; +#define newtrap() alloc(sizeof(struct trap)) + +/* various kinds of traps */ +#define BEAR_TRAP 0 +#define ARROW_TRAP 1 +#define DART_TRAP 2 +#define TRAPDOOR 3 +#define TELEP_TRAP 4 +#define PIT 5 +#define SLP_GAS_TRAP 6 +#define PIERC 7 +#define MIMIC 8 /* used only in mklev.c */ +#define TRAPNUM 9 /* if not less than 32, change sizeof(ttyp) */ + /* see also mtrapseen (bit map) */ diff --git a/hack/def.wseg.h b/hack/def.wseg.h new file mode 100644 index 0000000..161ead9 --- /dev/null +++ b/hack/def.wseg.h @@ -0,0 +1,14 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* def.wseg.h - version 1.0.2 */ +/* $DragonFly: src/games/hack/def.wseg.h,v 1.2 2004/11/06 12:29:17 eirikn Exp $ */ + +#ifndef NOWORM +/* worm structure */ +struct wseg { + struct wseg *nseg; + xchar wx, wy; + unsigned wdispl:1; +}; + +#define newseg() alloc(sizeof(struct wseg)) +#endif /* NOWORM */ diff --git a/hack/hack.6 b/hack/hack.6 new file mode 100644 index 0000000..239a4ce --- /dev/null +++ b/hack/hack.6 @@ -0,0 +1,160 @@ +.\" $FreeBSD: src/games/hack/hack.6,v 1.2.8.1 2001/07/22 11:01:22 dd Exp $ +.\" $DragonFly: src/games/hack/hack.6,v 1.5 2007/10/23 07:51:09 swildner Exp $ +.Dd March 31, 1985 +.Dt HACK 6 +.Os +.Sh NAME +.Nm hack +.Nd exploring The Dungeons of Doom +.Sh SYNOPSIS +.Nm +.Op Fl d Ar directory +.Op Fl n +.Op Fl u Ar playername +.Nm +.Op Fl d Ar directory +.Op Fl s +.Op Fl X +.Op Ar playername ... +.Sh DESCRIPTION +.Nm +is a display oriented dungeons \*[Am] dragons-like game. +Both display and command structure resemble rogue. +(For a game with the same structure but entirely different display - +a real cave instead of dull rectangles - try Quest.) +.Pp +To get started you really only need to know two commands. +The command +.Ic \&? +will give you a list of the available commands and the command +.Ic / +will identify the things you see on the screen. +.Pp +To win the game (as opposed to merely playing to beat other people's high +scores) you must locate the Amulet of Yendor which is somewhere below +the 20th level of the dungeon and get it out. +Nobody has achieved this yet and if somebody does, he will probably go +down in history as a hero among heroes. +.Pp +When the game ends, either by your death, when you quit, or if you escape +from the caves, +.Nm +will give you (a fragment of) the list of top scorers. +The scoring is based on many aspects of your behavior but a rough estimate +is obtained by taking the amount of gold you've found in the cave plus four +times your (real) experience. +Precious stones may be worth a lot of gold when brought to the exit. +There is a 10% penalty for getting yourself killed. +.Pp +The administration of the game is kept in the directory specified with the +.Fl d +option, or, if no such option is given, in the directory specified by +the environment variable +.Ev HACKDIR , +or, if no such variable exists, in the current directory. +This same directory contains several auxiliary files such as lockfiles and +the list of topscorers and a subdirectory +.Pa save +where games are saved. +The game administrator may however choose to install +.Nm +with a fixed playing ground, usually +.Pa /var/games/hackdir . +.Pp +The +.Fl n +option suppresses printing of the news. +.Pp +The +.Fl u Ar playername +option supplies the answer to the question "Who are you?". +When +.Ar playername +has as suffix one of +.Em -T , +.Em -S , +.Em -K , +.Em -F , +.Em -C , +or +.Em -W , +then this supplies the answer to the question "What kind of character ... ?". +.Pp +The +.Fl s +option will print out the list of your scores. +It may be followed by arguments +.Fl X +where X is one of the letters C, F, K, S, T, W to print the scores of +Cavemen, Fighters, Knights, Speleologists, Tourists or Wizards. +It may also be followed by one or more player names to print the scores of the +players mentioned. +.Sh ENVIRONMENT +.Bl -tag -width 24n -compact +.It Ev USER No or Ev LOGNAME +Your login name. +.It Ev HOME +Your home directory. +.It Ev SHELL +Your shell. +.It Ev TERM +The type of your terminal. +.It Ev HACKPAGER, PAGER +Pager used instead of default pager. +.It Ev MAIL +Mailbox file. +.It Ev MAILREADER +Reader used instead of default (probably +.Pa /usr/bin/mail ) . +.It Ev HACKDIR +Playground. +.It Ev HACKOPTIONS +String predefining several +.Nm +options (see help file). +.El +.Pp +Several other environment variables are used in debugging (wizard) mode, +like +.Ev GENOCIDED , +.Ev INVENT , +.Ev MAGIC +and +.Ev SHOPTYPE . +.Sh FILES +.Bl -tag -width 24n -compact +.It Pa hack +The +.Nm +program. +.It Pa data, rumors +Data files used by +.Nm . +.It Pa help, hh +Help data files. +.It Pa record +The list of topscorers. +.It Pa save +A subdirectory containing the saved games. +.It Pa bones_dd +Descriptions of the ghost and belongings of a deceased adventurer. +.It Pa xlock.dd +Description of a dungeon level. +.It Pa safelock +Lock file for xlock. +.It Pa record_lock +Lock file for record. +.El +.Sh AUTHORS +Jay Fenlason (+ Kenny Woodland, Mike Thome and Jon Payne) wrote the +original +.Nm , +very much like +.Xr rogue 6 +(but full of bugs). +.Pp +Andries Brouwer continuously deformed their sources into the current +version - in fact an entirely different game. +.Sh BUGS +Probably infinite. +Mail complaints to mcvax!aeb . diff --git a/hack/hack.Decl.c b/hack/hack.Decl.c new file mode 100644 index 0000000..15f807e --- /dev/null +++ b/hack/hack.Decl.c @@ -0,0 +1,42 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.Decl.c - version 1.0.3 */ + +#include "hack.h" +char nul[40]; /* contains zeros */ +char plname[PL_NSIZ]; /* player name */ +char lock[PL_NSIZ + 4] = "1lock"; /* long enough for login name .99 */ + +boolean in_mklev, restoring; + +struct rm levl[COLNO][ROWNO]; /* level map */ +#ifndef QUEST +struct mkroom rooms[MAXNROFROOMS + 1]; +coord doors[DOORMAX]; +#endif /* QUEST */ +struct monst *fmon = NULL; +struct trap *ftrap = NULL; +struct gold *fgold = NULL; +struct obj *fobj = NULL, *fcobj = NULL, *invent = NULL, *uwep = NULL, *uarm = NULL, + *uarm2 = NULL, *uarmh = NULL, *uarms = NULL, *uarmg = NULL, *uright = NULL, + *uleft = NULL, *uchain = NULL, *uball = NULL; +struct flag flags; +struct you u; +struct monst youmonst; /* dummy; used as return value for boomhit */ + +xchar dlevel = 1; +xchar xupstair, yupstair, xdnstair, ydnstair; +const char *save_cm, *killer, *nomovemsg; + +long moves = 1; +long wailmsg = 0; + +int multi = 0; +char genocided[60]; +char fut_geno[60]; + +xchar curx, cury; +xchar seelx, seehx, seely, seehy; /* corners of lit room */ + +coord bhitpos; + +char quitchars[] = " \r\n\033"; diff --git a/hack/hack.apply.c b/hack/hack.apply.c new file mode 100644 index 0000000..7944991 --- /dev/null +++ b/hack/hack.apply.c @@ -0,0 +1,467 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.apply.c - version 1.0.3 */ +/* $FreeBSD: src/games/hack/hack.apply.c,v 1.4.2.1 2001/02/18 02:20:07 kris Exp $ */ +/* $DragonFly: src/games/hack/hack.apply.c,v 1.4 2006/08/21 19:45:32 pavalos Exp $ */ + +#include "hack.h" +#include "def.edog.h" +extern char quitchars[]; + +static void use_camera(struct obj *); +static bool in_ice_box(struct obj *); +static bool ck_ice_box(struct obj *); +static int out_ice_box(struct obj *); +static void use_ice_box(struct obj *); +static struct monst *bchit(int, int, int, char); +static void use_whistle(struct obj *); +static void use_magic_whistle(struct obj *); +static bool dig(void); +static int use_pick_axe(struct obj *); + +int +doapply(void) +{ + struct obj *obj; + int res = 1; + + obj = getobj("(", "use or apply"); + if (!obj) + return (0); + + switch (obj->otyp) { + case EXPENSIVE_CAMERA: + use_camera(obj); + break; + case ICE_BOX: + use_ice_box(obj); + break; + case PICK_AXE: + res = use_pick_axe(obj); + break; + + case MAGIC_WHISTLE: + if (pl_character[0] == 'W' || u.ulevel > 9) { + use_magic_whistle(obj); + break; + } + /* fall into next case */ + case WHISTLE: + use_whistle(obj); + break; + + case CAN_OPENER: + if (!carrying(TIN)) { + pline("You have no can to open."); + goto xit; + } + pline("You cannot open a tin without eating its contents."); + pline("In order to eat, use the 'e' command."); + if (obj != uwep) + pline("Opening the tin will be much easier if you wield the can-opener."); + goto xit; + + default: + pline("Sorry, I don't know how to use that."); +xit: + nomul(0); + return (0); + } + nomul(0); + return (res); +} + +static void +use_camera(struct obj *obj __attribute__((unused))) +{ + struct monst *mtmp; + + if (!getdir(1)) { /* ask: in what direction? */ + flags.move = multi = 0; + return; + } + if (u.uswallow) { + pline("You take a picture of %s's stomach.", monnam(u.ustuck)); + return; + } + if (u.dz) { + pline("You take a picture of the %s.", + (u.dz > 0) ? "floor" : "ceiling"); + return; + } + if ((mtmp = bchit(u.dx, u.dy, COLNO, '!')) != NULL) { + if (mtmp->msleep) { + mtmp->msleep = 0; + pline("The flash awakens %s.", monnam(mtmp)); /* a3 */ + } else if (mtmp->data->mlet != 'y') + if (mtmp->mcansee || mtmp->mblinded) { + int tmp = dist(mtmp->mx, mtmp->my); + int tmp2; + if (cansee(mtmp->mx, mtmp->my)) + pline("%s is blinded by the flash!", Monnam(mtmp)); + setmangry(mtmp); + if (tmp < 9 && !mtmp->isshk && rn2(4)) { + mtmp->mflee = 1; + if (rn2(4)) + mtmp->mfleetim = rnd(100); + } + if (tmp < 3) + mtmp->mcansee = mtmp->mblinded = 0; + else { + tmp2 = mtmp->mblinded; + tmp2 += rnd(1 + 50 / tmp); + if (tmp2 > 127) + tmp2 = 127; + mtmp->mblinded = tmp2; + mtmp->mcansee = 0; + } + } + } +} + +static +struct obj *current_ice_box; /* a local variable of use_ice_box, to be + used by its local procedures in/ck_ice_box */ +static bool +in_ice_box(struct obj *obj) +{ + if (obj == current_ice_box || + (Punished && (obj == uball || obj == uchain))) { + pline("You must be kidding."); + return (0); + } + if (obj->owornmask & (W_ARMOR | W_RING)) { + pline("You cannot refrigerate something you are wearing."); + return (0); + } + if (obj->owt + current_ice_box->owt > 70) { + pline("It won't fit."); + return (1); /* be careful! */ + } + if (obj == uwep) { + if (uwep->cursed) { + pline("Your weapon is welded to your hand!"); + return (0); + } + setuwep(NULL); + } + current_ice_box->owt += obj->owt; + freeinv(obj); + obj->o_cnt_id = current_ice_box->o_id; + obj->nobj = fcobj; + fcobj = obj; + obj->age = moves - obj->age; /* actual age */ + return (1); +} + +static bool +ck_ice_box(struct obj *obj) +{ + return (obj->o_cnt_id == current_ice_box->o_id); +} + +static int +out_ice_box(struct obj *obj) +{ + struct obj *otmp; + + if (obj == fcobj) + fcobj = fcobj->nobj; + else { + for (otmp = fcobj; otmp->nobj != obj; otmp = otmp->nobj) + if (!otmp->nobj) + panic("out_ice_box"); + otmp->nobj = obj->nobj; + } + current_ice_box->owt -= obj->owt; + obj->age = moves - obj->age; /* simulated point of time */ + addinv(obj); + return (0); +} + +static void +use_ice_box(struct obj *obj) +{ + int cnt = 0; + struct obj *otmp; + + current_ice_box = obj; /* for use by in/out_ice_box */ + for (otmp = fcobj; otmp; otmp = otmp->nobj) + if (otmp->o_cnt_id == obj->o_id) + cnt++; + if (!cnt) + pline("Your ice-box is empty."); + else { + pline("Do you want to take something out of the ice-box? [yn] "); + if (readchar() == 'y') + if (askchain(fcobj, NULL, 0, out_ice_box, ck_ice_box, 0)) + return; + pline("That was all. Do you wish to put something in? [yn] "); + if (readchar() != 'y') + return; + } + /* call getobj: 0: allow cnt; #: allow all types; %: expect food */ + otmp = getobj("0#%", "put in"); + if (!otmp || !in_ice_box(otmp)) + flags.move = multi = 0; +} + +static +struct monst * +bchit(int ddx, int ddy, int range, char sym) +{ + struct monst *mtmp = NULL; + int bchx = u.ux, bchy = u.uy; + + if (sym) + Tmp_at(-1, sym); /* open call */ + while (range--) { + bchx += ddx; + bchy += ddy; + if ((mtmp = m_at(bchx, bchy))) + break; + if (!ZAP_POS(levl[bchx][bchy].typ)) { + bchx -= ddx; + bchy -= ddy; + break; + } + if (sym) + Tmp_at(bchx, bchy); + } + if (sym) + Tmp_at(-1, -1); + return (mtmp); +} + +static void +use_whistle(struct obj *obj __attribute__((unused))) +{ + struct monst *mtmp = fmon; + + pline("You produce a high whistling sound."); + while (mtmp) { + if (dist(mtmp->mx, mtmp->my) < u.ulevel * 20) { + if (mtmp->msleep) + mtmp->msleep = 0; + if (mtmp->mtame) + EDOG(mtmp)->whistletime = moves; + } + mtmp = mtmp->nmon; + } +} + +static void +use_magic_whistle(struct obj *obj __attribute__((unused))) +{ + struct monst *mtmp = fmon; + + pline("You produce a strange whistling sound."); + while (mtmp) { + if (mtmp->mtame) + mnexto(mtmp); + mtmp = mtmp->nmon; + } +} + +static int dig_effort; /* effort expended on current pos */ +static uchar dig_level; +static coord dig_pos; +static boolean dig_down; + +static +bool +dig(void) +{ + struct rm *lev; + int dpx = dig_pos.x, dpy = dig_pos.y; + + /* perhaps a nymph stole his pick-axe while he was busy digging */ + /* or perhaps he teleported away */ + if (u.uswallow || !uwep || uwep->otyp != PICK_AXE || + dig_level != dlevel || + ((dig_down && (dpx != u.ux || dpy != u.uy)) || + (!dig_down && dist(dpx, dpy) > 2))) + return (0); + + dig_effort += 10 + abon() + uwep->spe + rn2(5); + if (dig_down) { + if (!xdnstair) { + pline("The floor here seems too hard to dig in."); + return (0); + } + if (dig_effort > 250) { + dighole(); + return (0); /* done with digging */ + } + if (dig_effort > 50) { + struct trap *ttmp = t_at(dpx, dpy); + + if (!ttmp) { + ttmp = maketrap(dpx, dpy, PIT); + ttmp->tseen = 1; + pline("You have dug a pit."); + u.utrap = rn1(4, 2); + u.utraptype = TT_PIT; + return (0); + } + } + } else if (dig_effort > 100) { + const char *digtxt; + struct obj *obj; + + lev = &levl[dpx][dpy]; + if ((obj = sobj_at(ENORMOUS_ROCK, dpx, dpy)) != NULL) { + fracture_rock(obj); + digtxt = "The rock falls apart."; + } else if (!lev->typ || lev->typ == SCORR) { + lev->typ = CORR; + digtxt = "You succeeded in cutting away some rock."; + } else if (lev->typ == HWALL || lev->typ == VWALL + || lev->typ == SDOOR) { + lev->typ = xdnstair ? DOOR : ROOM; + digtxt = "You just made an opening in the wall."; + } else + digtxt = "Now what exactly was it that you were digging in?"; + mnewsym(dpx, dpy); + prl(dpx, dpy); + pline("%s", digtxt); /* after mnewsym & prl */ + return (0); + } else { + if (IS_WALL(levl[dpx][dpy].typ)) { + int rno = inroom(dpx, dpy); + + if (rno >= 0 && rooms[rno].rtype >= 8) { + pline("This wall seems too hard to dig into."); + return (0); + } + } + pline("You hit the rock with all your might."); + } + return (1); +} + +/* When will hole be finished? Very rough indication used by shopkeeper. */ +int +holetime(void) +{ + return ((occupation == dig) ? (250 - dig_effort) / 20 : -1); +} + +void +dighole(void) +{ + struct trap *ttmp = t_at(u.ux, u.uy); + + if (!xdnstair) { + pline("The floor here seems too hard to dig in."); + } else { + if (ttmp) + ttmp->ttyp = TRAPDOOR; + else + ttmp = maketrap(u.ux, u.uy, TRAPDOOR); + ttmp->tseen = 1; + pline("You've made a hole in the floor."); + if (!u.ustuck) { + if (inshop()) + shopdig(1); + pline("You fall through ..."); + if (u.utraptype == TT_PIT) { + u.utrap = 0; + u.utraptype = 0; + } + goto_level(dlevel + 1, FALSE); + } + } +} + +static int +use_pick_axe(struct obj *obj) +{ + char dirsyms[12]; + char *dsp = dirsyms, *sdp = sdir; + struct monst *mtmp; + struct rm *lev; + int rx, ry, res = 0; + + if (obj != uwep) { + if (uwep && uwep->cursed) { + /* Andreas Bormann - ihnp4!decvax!mcvax!unido!ab */ + pline("Since your weapon is welded to your hand,"); + pline("you cannot use that pick-axe."); + return (0); + } + pline("You now wield %s.", doname(obj)); + setuwep(obj); + res = 1; + } + while (*sdp) { + movecmd(*sdp); /* sets u.dx and u.dy and u.dz */ + rx = u.ux + u.dx; + ry = u.uy + u.dy; + if (u.dz > 0 || (u.dz == 0 && isok(rx, ry) && + (IS_ROCK(levl[rx][ry].typ) + || sobj_at(ENORMOUS_ROCK, rx, ry)))) + *dsp++ = *sdp; + sdp++; + } + *dsp = 0; + pline("In what direction do you want to dig? [%s] ", dirsyms); + if (!getdir(0)) /* no txt */ + return (res); + if (u.uswallow && attack(u.ustuck)) /* return(1) */ + ; + else if (u.dz < 0) + pline("You cannot reach the ceiling."); + else if (u.dz == 0) { + if (Confusion) + confdir(); + rx = u.ux + u.dx; + ry = u.uy + u.dy; + if ((mtmp = m_at(rx, ry)) && attack(mtmp)) + return (1); + if (!isok(rx, ry)) { + pline("Clash!"); + return (1); + } + lev = &levl[rx][ry]; + if (lev->typ == DOOR) + pline("Your %s against the door.", + aobjnam(obj, "clang")); + else if (!IS_ROCK(lev->typ) + && !sobj_at(ENORMOUS_ROCK, rx, ry)) { + /* ACCESSIBLE or POOL */ + pline("You swing your %s through thin air.", + aobjnam(obj, NULL)); + } else { + if (dig_pos.x != rx || dig_pos.y != ry + || dig_level != dlevel || dig_down) { + dig_down = FALSE; + dig_pos.x = rx; + dig_pos.y = ry; + dig_level = dlevel; + dig_effort = 0; + pline("You start digging."); + } else + pline("You continue digging."); + occupation = dig; + occtxt = "digging"; + } + } else if (Levitation) { + pline("You cannot reach the floor."); + } else { + if (dig_pos.x != u.ux || dig_pos.y != u.uy + || dig_level != dlevel || !dig_down) { + dig_down = TRUE; + dig_pos.x = u.ux; + dig_pos.y = u.uy; + dig_level = dlevel; + dig_effort = 0; + pline("You start digging in the floor."); + if (inshop()) + shopdig(0); + } else + pline("You continue digging in the floor."); + occupation = dig; + occtxt = "digging"; + } + return (1); +} diff --git a/hack/hack.bones.c b/hack/hack.bones.c new file mode 100644 index 0000000..97ecc0f --- /dev/null +++ b/hack/hack.bones.c @@ -0,0 +1,108 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.bones.c - version 1.0.3 */ +/* $FreeBSD: src/games/hack/hack.bones.c,v 1.4 1999/11/16 10:26:35 marcel Exp $ */ +/* $DragonFly: src/games/hack/hack.bones.c,v 1.4 2006/08/21 19:45:32 pavalos Exp $ */ + +#include "hack.h" + +char bones[] = "bones_xx"; + +/* save bones and possessions of a deceased adventurer */ +void +savebones(void) +{ + int fd; + struct obj *otmp; + struct trap *ttmp; + struct monst *mtmp; + + if (dlevel <= 0 || dlevel > MAXLEVEL) + return; + if (!rn2(1 + dlevel / 2)) /* not so many ghosts on low levels */ + return; + bones[6] = '0' + (dlevel / 10); + bones[7] = '0' + (dlevel % 10); + if ((fd = open(bones, O_RDONLY)) >= 0) { + close(fd); + return; + } + /* drop everything; the corpse's possessions are usually cursed */ + otmp = invent; + while (otmp) { + otmp->ox = u.ux; + otmp->oy = u.uy; + otmp->age = 0; /* very long ago */ + otmp->owornmask = 0; + if (rn2(5)) + otmp->cursed = 1; + if (!otmp->nobj) { + otmp->nobj = fobj; + fobj = invent; + invent = 0; /* superfluous */ + break; + } + otmp = otmp->nobj; + } + if (!(mtmp = makemon(PM_GHOST, u.ux, u.uy))) + return; + mtmp->mx = u.ux; + mtmp->my = u.uy; + mtmp->msleep = 1; + strcpy((char *)mtmp->mextra, plname); + mkgold(somegold() + d(dlevel, 30), u.ux, u.uy); + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { + mtmp->m_id = 0; + if (mtmp->mtame) { + mtmp->mtame = 0; + mtmp->mpeaceful = 0; + } + mtmp->mlstmv = 0; + if (mtmp->mdispl) + unpmon(mtmp); + } + for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap) + ttmp->tseen = 0; + for (otmp = fobj; otmp; otmp = otmp->nobj) { + otmp->o_id = 0; + /* otmp->o_cnt_id = 0; - superfluous */ + otmp->onamelth = 0; + otmp->known = 0; + otmp->invlet = 0; + if (otmp->olet == AMULET_SYM && !otmp->spe) { + otmp->spe = -1; /* no longer the actual amulet */ + otmp->cursed = 1; /* flag as gotten from a ghost */ + } + } + if ((fd = creat(bones, FMASK)) < 0) + return; + savelev(fd, dlevel); + close(fd); +} + +int +getbones(void) +{ + int fd, x, y, ok; + + if (rn2(3)) /* only once in three times do we find bones */ + return (0); + bones[6] = '0' + dlevel / 10; + bones[7] = '0' + dlevel % 10; + if ((fd = open(bones, O_RDONLY)) < 0) + return (0); + if ((ok = uptodate(fd)) != 0) { + getlev(fd, 0, dlevel); + for (x = 0; x < COLNO; x++) + for (y = 0; y < ROWNO; y++) + levl[x][y].seen = levl[x][y].new = 0; + } + close(fd); +#ifdef WIZARD + if (!wizard) /* duvel!frans: don't remove bones while debugging */ +#endif /* WiZARD */ + if (unlink(bones) < 0) { + pline("Cannot unlink %s .", bones); + return (0); + } + return (ok); +} diff --git a/hack/hack.c b/hack/hack.c new file mode 100644 index 0000000..56aa11f --- /dev/null +++ b/hack/hack.c @@ -0,0 +1,904 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.c - version 1.0.3 */ +/* $FreeBSD: src/games/hack/hack.c,v 1.4 1999/11/16 10:26:35 marcel Exp $ */ + +#include "hack.h" + +static void movobj(struct obj *, int, int); +#ifdef QUEST +static bool rroom(int, int); +#endif +static int inv_cnt(void); + +/* called on movement: + * 1. when throwing ball+chain far away + * 2. when teleporting + * 3. when walking out of a lit room + */ +void +unsee(void) +{ + int x, y; + struct rm *lev; + +#ifndef QUEST + if (seehx) + seehx = 0; + else +#endif /* QUEST */ + for (x = u.ux - 1; x < u.ux + 2; x++) + for (y = u.uy - 1; y < u.uy + 2; y++) { + if (!isok(x, y)) + continue; + lev = &levl[x][y]; + if (!lev->lit && lev->scrsym == '.') { + lev->scrsym = ' '; + lev->new = 1; + on_scr(x, y); + } + } +} + +/* called: + * in hack.eat.c: seeoff(0) - blind after eating rotten food + * in hack.mon.c: seeoff(0) - blinded by a yellow light + * in hack.mon.c: seeoff(1) - swallowed + * in hack.do.c: seeoff(0) - blind after drinking potion + * in hack.do.c: seeoff(1) - go up or down the stairs + * in hack.trap.c:seeoff(1) - fall through trapdoor + * mode: + * 1 to redo @, 0 to leave them *//* 1 means + * misc movement, 0 means blindness + */ +void +seeoff(bool mode) +{ + int x, y; + struct rm *lev; + + if (u.udispl && mode) { + u.udispl = 0; + levl[u.udisx][u.udisy].scrsym = news0(u.udisx, u.udisy); + } +#ifndef QUEST + if (seehx) + seehx = 0; + else +#endif /* QUEST */ + if (!mode) { + for (x = u.ux - 1; x < u.ux + 2; x++) + for (y = u.uy - 1; y < u.uy + 2; y++) { + if (!isok(x, y)) + continue; + lev = &levl[x][y]; + if (!lev->lit && lev->scrsym == '.') + lev->seen = 0; + } + } +} + +void +domove(void) +{ + xchar oldx, oldy; + struct monst *mtmp = NULL; + struct rm *tmpr, *ust; + struct trap *trap = NULL; + struct obj *otmp; + + u_wipe_engr(rnd(5)); + + if (inv_weight() > 0) { + pline("You collapse under your load."); + nomul(0); + return; + } + if (u.uswallow) { + u.dx = u.dy = 0; + u.ux = u.ustuck->mx; + u.uy = u.ustuck->my; + } else { + if (Confusion) { + do { + confdir(); + } while (!isok(u.ux + u.dx, u.uy + u.dy) || + IS_ROCK(levl[u.ux + u.dx][u.uy + u.dy].typ)); + } + if (!isok(u.ux + u.dx, u.uy + u.dy)) { + nomul(0); + return; + } + } + + ust = &levl[u.ux][u.uy]; + oldx = u.ux; + oldy = u.uy; + if (!u.uswallow && + (trap = t_at(u.ux + u.dx, u.uy + u.dy)) && trap->tseen) + nomul(0); + if (u.ustuck && !u.uswallow && (u.ux + u.dx != u.ustuck->mx || + u.uy + u.dy != u.ustuck->my)) { + if (dist(u.ustuck->mx, u.ustuck->my) > 2) { + /* perhaps it fled (or was teleported or ... ) */ + u.ustuck = 0; + } else { + if (Blind) + pline("You cannot escape from it!"); + else + pline("You cannot escape from %s!", + monnam(u.ustuck)); + nomul(0); + return; + } + } + if (u.uswallow || (mtmp = m_at(u.ux + u.dx, u.uy + u.dy))) { + /* attack monster */ + + nomul(0); + gethungry(); + if (multi < 0) /* we just fainted */ + return; + + /* try to attack; note that it might evade */ + if (attack(u.uswallow ? u.ustuck : mtmp)) + return; + } + /* not attacking an animal, so we try to move */ + if (u.utrap) { + if (u.utraptype == TT_PIT) { + pline("You are still in a pit."); + u.utrap--; + } else { + pline("You are caught in a beartrap."); + if ((u.dx && u.dy) || !rn2(5)) + u.utrap--; + } + return; + } + tmpr = &levl[u.ux + u.dx][u.uy + u.dy]; + if (IS_ROCK(tmpr->typ) || + (u.dx && u.dy && (tmpr->typ == DOOR || ust->typ == DOOR))) { + flags.move = 0; + nomul(0); + return; + } + while ((otmp = sobj_at(ENORMOUS_ROCK, u.ux + u.dx, u.uy + u.dy)) != NULL) { + struct trap *ttmp; + xchar rx = u.ux + 2 * u.dx, ry = u.uy + 2 * u.dy; + nomul(0); + if (isok(rx, ry) && !IS_ROCK(levl[rx][ry].typ) && + (levl[rx][ry].typ != DOOR || !(u.dx && u.dy)) && + !sobj_at(ENORMOUS_ROCK, rx, ry)) { + if (m_at(rx, ry)) { + pline("You hear a monster behind the rock."); + pline("Perhaps that's why you cannot move it."); + goto cannot_push; + } + if ((ttmp = t_at(rx, ry)) != NULL) + switch (ttmp->ttyp) { + case PIT: + pline("You push the rock into a pit!"); + deltrap(ttmp); + delobj(otmp); + pline("It completely fills the pit!"); + continue; + case TELEP_TRAP: + pline("You push the rock and suddenly it disappears!"); + delobj(otmp); + continue; + } + if (levl[rx][ry].typ == POOL) { + levl[rx][ry].typ = ROOM; + mnewsym(rx, ry); + prl(rx, ry); + pline("You push the rock into the water."); + pline("Now you can cross the water!"); + delobj(otmp); + continue; + } + otmp->ox = rx; + otmp->oy = ry; + if (cansee(rx, ry)) + atl(rx, ry, otmp->olet); + if (Invisible) + newsym(u.ux + u.dx, u.uy + u.dy); + + { + static long lastmovetime; + /* note: this var contains garbage initially and + * after a restore */ + if (moves > lastmovetime + 2 || moves < lastmovetime) + pline("With great effort you move the enormous rock."); + lastmovetime = moves; + } + } else { + pline("You try to move the enormous rock, but in vain."); +cannot_push: + if ((!invent || inv_weight() + 90 <= 0) && + (!u.dx || !u.dy || + (IS_ROCK(levl[u.ux][u.uy + u.dy].typ) + && IS_ROCK(levl[u.ux + u.dx][u.uy].typ)))) { + pline("However, you can squeeze yourself into a small opening."); + break; + } else + return; + } + } + if (u.dx && u.dy && IS_ROCK(levl[u.ux][u.uy + u.dy].typ) && + IS_ROCK(levl[u.ux + u.dx][u.uy].typ) && + invent && inv_weight() + 40 > 0) { + pline("You are carrying too much to get through."); + nomul(0); + return; + } + if (Punished && + DIST(u.ux + u.dx, u.uy + u.dy, uchain->ox, uchain->oy) > 2) { + if (carried(uball)) { + movobj(uchain, u.ux, u.uy); + goto nodrag; + } + + if (DIST(u.ux + u.dx, u.uy + u.dy, uball->ox, uball->oy) < 3) { + /* leave ball, move chain under/over ball */ + movobj(uchain, uball->ox, uball->oy); + goto nodrag; + } + + if (inv_weight() + (int)uball->owt / 2 > 0) { + pline("You cannot %sdrag the heavy iron ball.", + invent ? "carry all that and also " : ""); + nomul(0); + return; + } + + movobj(uball, uchain->ox, uchain->oy); + unpobj(uball); /* BAH %% */ + uchain->ox = u.ux; + uchain->oy = u.uy; + nomul(-2); + nomovemsg = ""; +nodrag: ; + } + u.ux += u.dx; + u.uy += u.dy; + if (flags.run) { + if (tmpr->typ == DOOR || + (xupstair == u.ux && yupstair == u.uy) || + (xdnstair == u.ux && ydnstair == u.uy)) + nomul(0); + } + + if (tmpr->typ == POOL && !Levitation) + drown(); /* not necessarily fatal */ + + if (!Blind) { +#ifdef QUEST + setsee(); +#else + if (ust->lit) { + if (tmpr->lit) { + if (tmpr->typ == DOOR) + prl1(u.ux + u.dx, u.uy + u.dy); + else if (ust->typ == DOOR) + nose1(oldx - u.dx, oldy - u.dy); + } else { + unsee(); + prl1(u.ux + u.dx, u.uy + u.dy); + } + } else { + if (tmpr->lit) + setsee(); + else { + prl1(u.ux + u.dx, u.uy + u.dy); + if (tmpr->typ == DOOR) { + if (u.dy) { + prl(u.ux - 1, u.uy); + prl(u.ux + 1, u.uy); + } else { + prl(u.ux, u.uy - 1); + prl(u.ux, u.uy + 1); + } + } + } + nose1(oldx - u.dx, oldy - u.dy); + } +#endif /* QUEST */ + } else + pru(); + if (!flags.nopick) + pickup(1); + if (trap) + dotrap(trap); /* fall into pit, arrow trap, etc. */ + inshop(); + if (!Blind) + read_engr_at(u.ux, u.uy); +} + +static void +movobj(struct obj *obj, int ox, int oy) +{ + /* Some dirty programming to get display right */ + freeobj(obj); + unpobj(obj); + obj->nobj = fobj; + fobj = obj; + obj->ox = ox; + obj->oy = oy; +} + +int +dopickup(void) +{ + if (!g_at(u.ux, u.uy) && !o_at(u.ux, u.uy)) { + pline("There is nothing here to pick up."); + return (0); + } + if (Levitation) { + pline("You cannot reach the floor."); + return (1); + } + pickup(0); + return (1); +} + +void +pickup(int all) +{ + struct gold *gold; + struct obj *obj, *obj2; + int wt; + + if (Levitation) + return; + while ((gold = g_at(u.ux, u.uy))) { + pline("%ld gold piece%s.", gold->amount, plur(gold->amount)); + u.ugold += gold->amount; + flags.botl = 1; + freegold(gold); + if (flags.run) + nomul(0); + if (Invisible) + newsym(u.ux, u.uy); + } + + /* check for more than one object */ + if (!all) { + int ct = 0; + + for (obj = fobj; obj; obj = obj->nobj) + if (obj->ox == u.ux && obj->oy == u.uy) + if (!Punished || obj != uchain) + ct++; + if (ct < 2) + all++; + else + pline("There are several objects here."); + } + + for (obj = fobj; obj; obj = obj2) { + obj2 = obj->nobj; /* perhaps obj will be picked up */ + if (obj->ox == u.ux && obj->oy == u.uy) { + if (flags.run) + nomul(0); + + /* do not pick up uchain */ + if (Punished && obj == uchain) + continue; + + if (!all) { + char c; + + pline("Pick up %s ? [ynaq]", doname(obj)); + while (!strchr("ynaq ", (c = readchar()))) + bell(); + if (c == 'q') + return; + if (c == 'n') + continue; + if (c == 'a') + all = 1; + } + + if (obj->otyp == DEAD_COCKATRICE && !uarmg) { + pline("Touching the dead cockatrice is a fatal mistake."); + pline("You turn to stone."); + killer = "cockatrice cadaver"; + done("died"); + } + + if (obj->otyp == SCR_SCARE_MONSTER) { + if (!obj->spe) + obj->spe = 1; + else { + /* Note: perhaps the 1st pickup failed: you cannot + * carry anymore, and so we never dropped it - + * let's assume that treading on it twice also + * destroys the scroll */ + pline("The scroll turns to dust as you pick it up."); + delobj(obj); + continue; + } + } + + wt = inv_weight() + obj->owt; + if (wt > 0) { + if (obj->quan > 1) { + /* see how many we can lift */ + int savequan = obj->quan; + int iw = inv_weight(); + int qq; + for (qq = 1; qq < savequan; qq++) { + obj->quan = qq; + if (iw + weight(obj) > 0) + break; + } + obj->quan = savequan; + qq--; + /* we can carry qq of them */ + if (!qq) + goto too_heavy; + pline("You can only carry %s of the %s lying here.", + (qq == 1) ? "one" : "some", + doname(obj)); + splitobj(obj, qq); + /* note: obj2 is set already, so we'll never + * encounter the other half; if it should be + * otherwise then write + * obj2 = splitobj(obj, qq); + */ + goto lift_some; + } +too_heavy: + pline("There %s %s here, but %s.", + (obj->quan == 1) ? "is" : "are", + doname(obj), + !invent ? "it is too heavy for you to lift" + : "you cannot carry anymore"); + break; + } +lift_some: + if (inv_cnt() >= 52) { + pline("Your knapsack cannot accommodate anymore items."); + break; + } + if (wt > -5) + pline("You have a little trouble lifting"); + freeobj(obj); + if (Invisible) + newsym(u.ux, u.uy); + addtobill(obj); /* sets obj->unpaid if necessary */ + { + int pickquan = obj->quan; + int mergquan; + if (!Blind) /* this is done by prinv(), */ + obj->dknown = 1;/* but addinv() needs it */ + /* already for merging */ + obj = addinv(obj); /* might merge it with other objects */ + mergquan = obj->quan; + obj->quan = pickquan; /* to fool prinv() */ + prinv(obj); + obj->quan = mergquan; + } + } + } +} + +/* stop running if we see something interesting */ +/* turn around a corner if that is the only way we can proceed */ +/* do not turn left or right twice */ +void +lookaround(void) +{ + int x, y, i, x0, y0, m0, i0 = 9; + int corrct = 0, noturn = 0; + struct monst *mtmp; + + /* suppress "used before set" message */ + x0 = y0 = m0 = 0; + if (Blind || flags.run == 0) + return; + if (flags.run == 1 && levl[u.ux][u.uy].typ == ROOM) + return; +#ifdef QUEST + if (u.ux0 == u.ux + u.dx && u.uy0 == u.uy + u.dy) + goto stop; +#endif /* QUEST */ + for (x = u.ux - 1; x <= u.ux + 1; x++) + for (y = u.uy - 1; y <= u.uy + 1; y++) { + if (x == u.ux && y == u.uy) + continue; + if (!levl[x][y].typ) + continue; + if ((mtmp = m_at(x, y)) && !mtmp->mimic && + (!mtmp->minvis || See_invisible)) { + if (!mtmp->mtame || + (x == u.ux + u.dx && y == u.uy + u.dy)) + goto stop; + } else /* invisible M cannot influence us */ + mtmp = NULL; + if (x == u.ux - u.dx && y == u.uy - u.dy) + continue; + switch (levl[x][y].scrsym) { + case '|': + case '-': + case '.': + case ' ': + break; + case '+': + if (x != u.ux && y != u.uy) + break; + if (flags.run != 1) + goto stop; + /* fall into next case */ + case CORR_SYM: +corr: + if (flags.run == 1 || flags.run == 3) { + i = DIST(x, y, u.ux + u.dx, u.uy + u.dy); + if (i > 2) + break; + if (corrct == 1 && + DIST(x, y, x0, y0) != 1) + noturn = 1; + if (i < i0) { + i0 = i; + x0 = x; + y0 = y; + m0 = mtmp ? 1 : 0; + } + } + corrct++; + break; + case '^': + if (flags.run == 1) /* if you must */ + goto corr; + if (x == u.ux + u.dx && y == u.uy + u.dy) + goto stop; + break; + default: /* e.g. objects or trap or stairs */ + if (flags.run == 1) + goto corr; + if (mtmp) /* d */ + break; +stop: + nomul(0); + return; + } + } +#ifdef QUEST + if (corrct > 0 && (flags.run == 4 || flags.run == 5)) + goto stop; +#endif /* QUEST */ + if (corrct > 1 && flags.run == 2) + goto stop; + if ((flags.run == 1 || flags.run == 3) && !noturn && !m0 && i0 && + (corrct == 1 || (corrct == 2 && i0 == 1))) { + /* make sure that we do not turn too far */ + if (i0 == 2) { + if (u.dx == y0 - u.uy && u.dy == u.ux - x0) + i = 2; /* straight turn right */ + else + i = -2; /* straight turn left */ + } else if (u.dx && u.dy) { + if ((u.dx == u.dy && y0 == u.uy) || + (u.dx != u.dy && y0 != u.uy)) + i = -1; /* half turn left */ + else + i = 1; /* half turn right */ + } else { + if ((x0 - u.ux == y0 - u.uy && !u.dy) || + (x0 - u.ux != y0 - u.uy && u.dy)) + i = 1; /* half turn right */ + else + i = -1; /* half turn left */ + } + i += u.last_str_turn; + if (i <= 2 && i >= -2) { + u.last_str_turn = i; + u.dx = x0 - u.ux, u.dy = y0 - u.uy; + } + } +} + +/* something like lookaround, but we are not running */ +/* react only to monsters that might hit us */ +bool +monster_nearby(void) +{ + int x, y; + struct monst *mtmp; + + if (!Blind) + for (x = u.ux - 1; x <= u.ux + 1; x++) + for (y = u.uy - 1; y <= u.uy + 1; y++) { + if (x == u.ux && y == u.uy) + continue; + if ((mtmp = m_at(x, y)) && !mtmp->mimic && + !mtmp->mtame && + !mtmp->mpeaceful && + !strchr("Ea", mtmp->data->mlet) && + !mtmp->mfroz && !mtmp->msleep && /* aplvax!jcn */ + (!mtmp->minvis || See_invisible)) + return (1); + } + return (0); +} + +#ifdef QUEST +bool +cansee(xchar x, xchar y) +{ + int dx, dy, adx, ady, sdx, sdy, dmax, d; + + if (Blind) + return (0); + if (!isok(x, y)) + return (0); + d = dist(x, y); + if (d < 3) + return (1); + if (d > u.uhorizon * u.uhorizon) + return (0); + if (!levl[x][y].lit) + return (0); + dx = x - u.ux; + adx = abs(dx); + sdx = sgn(dx); + dy = y - u.uy; + ady = abs(dy); + sdy = sgn(dy); + if (dx == 0 || dy == 0 || adx == ady) { + dmax = (dx == 0) ? ady : adx; + for (d = 1; d <= dmax; d++) + if (!rroom(sdx * d, sdy * d)) + return (0); + return (1); + } else if (ady > adx) { + for (d = 1; d <= ady; d++) { + if (!rroom(sdx * ((d * adx) / ady), sdy * d) || + !rroom(sdx * ((d * adx - 1) / ady + 1), sdy * d)) + return (0); + } + return (1); + } else { + for (d = 1; d <= adx; d++) { + if (!rroom(sdx * d, sdy * ((d * ady) / adx)) || + !rroom(sdx * d, sdy * ((d * ady - 1) / adx + 1))) + return (0); + } + return (1); + } +} + +static bool +rroom(int x, int y) +{ + return (IS_ROOM(levl[u.ux + x][u.uy + y].typ)); +} + +#else + +bool +cansee(xchar x, xchar y) +{ + if (Blind || u.uswallow) + return (0); + if (dist(x, y) < 3) + return (1); + if (levl[x][y].lit && seelx <= x && x <= seehx && seely <= y && + y <= seehy) + return (1); + return (0); +} +#endif /* QUEST */ + +int +sgn(int a) +{ + return ((a > 0) ? 1 : (a == 0) ? 0 : -1); +} + +#ifdef QUEST +void +setsee(void) +{ + int x, y; + + if (Blind) { + pru(); + return; + } + for (y = u.uy - u.uhorizon; y <= u.uy + u.uhorizon; y++) + for (x = u.ux - u.uhorizon; x <= u.ux + u.uhorizon; x++) { + if (cansee(x, y)) + prl(x, y); + } +} + +#else + +void +setsee(void) +{ + int x, y; + + if (Blind) { + pru(); + return; + } + if (!levl[u.ux][u.uy].lit) { + seelx = u.ux - 1; + seehx = u.ux + 1; + seely = u.uy - 1; + seehy = u.uy + 1; + } else { + for (seelx = u.ux; levl[seelx - 1][u.uy].lit; seelx--) ; + for (seehx = u.ux; levl[seehx + 1][u.uy].lit; seehx++) ; + for (seely = u.uy; levl[u.ux][seely - 1].lit; seely--) ; + for (seehy = u.uy; levl[u.ux][seehy + 1].lit; seehy++) ; + } + for (y = seely; y <= seehy; y++) + for (x = seelx; x <= seehx; x++) + prl(x, y); + + if (!levl[u.ux][u.uy].lit) /* seems necessary elsewhere */ + seehx = 0; + else { + if (seely == u.uy) + for (x = u.ux - 1; x <= u.ux + 1; x++) + prl(x, seely - 1); + if (seehy == u.uy) + for (x = u.ux - 1; x <= u.ux + 1; x++) + prl(x, seehy + 1); + if (seelx == u.ux) + for (y = u.uy - 1; y <= u.uy + 1; y++) + prl(seelx - 1, y); + if (seehx == u.ux) + for (y = u.uy - 1; y <= u.uy + 1; y++) + prl(seehx + 1, y); + } +} +#endif /* QUEST */ + +void +nomul(int nval) +{ + if (multi < 0) + return; + multi = nval; + flags.mv = flags.run = 0; +} + +int +abon(void) +{ + if (u.ustr == 3) + return (-3); + else if (u.ustr < 6) + return (-2); + else if (u.ustr < 8) + return (-1); + else if (u.ustr < 17) + return (0); + else if (u.ustr < 69) /* up to 18/50 */ + return (1); + else if (u.ustr < 118) + return (2); + else + return (3); +} + +int +dbon(void) +{ + if (u.ustr < 6) + return (-1); + else if (u.ustr < 16) + return (0); + else if (u.ustr < 18) + return (1); + else if (u.ustr == 18) /* up to 18 */ + return (2); + else if (u.ustr < 94) /* up to 18/75 */ + return (3); + else if (u.ustr < 109) /* up to 18/90 */ + return (4); + else if (u.ustr < 118) /* up to 18/99 */ + return (5); + else + return (6); +} + +/* may kill you; cause may be poison or monster like 'A' */ +void +losestr(int num) +{ + u.ustr -= num; + while (u.ustr < 3) { + u.ustr++; + u.uhp -= 6; + u.uhpmax -= 6; + } + flags.botl = 1; +} + +void +losehp(int n, const char *knam) +{ + u.uhp -= n; + if (u.uhp > u.uhpmax) + u.uhpmax = u.uhp; /* perhaps n was negative */ + flags.botl = 1; + if (u.uhp < 1) { + killer = knam; /* the thing that killed you */ + done("died"); + } +} + +void +losehp_m(int n, struct monst *mtmp) +{ + u.uhp -= n; + flags.botl = 1; + if (u.uhp < 1) + done_in_by(mtmp); +} + +void +losexp(void) /* hit by V or W */ +{ + int num; + + if (u.ulevel > 1) + pline("Goodbye level %u.", u.ulevel--); + else + u.uhp = -1; + num = rnd(10); + u.uhp -= num; + u.uhpmax -= num; + u.uexp = newuexp(); + flags.botl = 1; +} + +int +inv_weight(void) +{ + struct obj *otmp = invent; + int wt = (u.ugold + 500) / 1000; + int carrcap; + + if (Levitation) /* pugh@cornell */ + carrcap = MAX_CARR_CAP; + else { + carrcap = 5 * (((u.ustr > 18) ? 20 : u.ustr) + u.ulevel); + if (carrcap > MAX_CARR_CAP) + carrcap = MAX_CARR_CAP; + if (Wounded_legs & LEFT_SIDE) + carrcap -= 10; + if (Wounded_legs & RIGHT_SIDE) + carrcap -= 10; + } + while (otmp) { + wt += otmp->owt; + otmp = otmp->nobj; + } + return (wt - carrcap); +} + +static int +inv_cnt(void) +{ + struct obj *otmp = invent; + int ct = 0; + + while (otmp) { + ct++; + otmp = otmp->nobj; + } + return (ct); +} + +long +newuexp(void) +{ + return (10 * (1L << (u.ulevel - 1))); +} diff --git a/hack/hack.cmd.c b/hack/hack.cmd.c new file mode 100644 index 0000000..d2aaed3 --- /dev/null +++ b/hack/hack.cmd.c @@ -0,0 +1,332 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.cmd.c - version 1.0.3 */ +/* $FreeBSD: src/games/hack/hack.cmd.c,v 1.4 1999/11/16 10:26:35 marcel Exp $ */ +/* $DragonFly: src/games/hack/hack.cmd.c,v 1.4 2006/08/21 19:45:32 pavalos Exp $ */ + +#include "hack.h" +#include "def.func_tab.h" + +static int doextcmd(void); +static char lowc(char); +static char unctrl(char); +#ifdef QUEST +static bool isroom(int, int); +#endif +static int done2(void); + +struct func_tab cmdlist[]={ + { '\020', doredotopl }, + { '\022', doredraw }, + { '\024', dotele }, +#ifdef SUSPEND + { '\032', dosuspend }, +#endif /* SUSPEND */ + { 'a', doapply }, + /*'A' : UNUSED */ + /*'b', 'B' : go sw */ + { 'c', ddocall }, + { 'C', do_mname }, + { 'd', dodrop }, + { 'D', doddrop }, + { 'e', doeat }, + { 'E', doengrave }, + /*'f', 'F' : multiple go (might become 'fight') */ + /*'g', 'G' : UNUSED */ + /*'h', 'H' : go west */ + { 'I', dotypeinv }, /* Robert Viduya */ + { 'i', ddoinv }, + /*'j', 'J', 'k', 'K', 'l', 'L', 'm', 'M', 'n', 'N' : move commands */ + /*'o', doopen, */ + { 'O', doset }, + { 'p', dopay }, + { 'P', dowearring }, + { 'q', dodrink }, + { 'Q', done2 }, + { 'r', doread }, + { 'R', doremring }, + { 's', dosearch }, + { 'S', dosave }, + { 't', dothrow }, + { 'T', doremarm }, + /*'u', 'U' : go ne */ + { 'v', doversion }, + /*'V' : UNUSED */ + { 'w', dowield }, + { 'W', doweararm }, + /*'x', 'X' : UNUSED */ + /*'y', 'Y' : go nw */ + { 'z', dozap }, + /*'Z' : UNUSED */ + { '<', doup }, + { '>', dodown }, + { '/', dowhatis }, + { '?', dohelp }, +#ifdef SHELL + { '!', dosh }, +#endif /* SHELL */ + { '.', donull }, + { ' ', donull }, + { ',', dopickup }, + { ':', dolook }, + { '^', doidtrap }, + { '\\', dodiscovered }, /* Robert Viduya */ + { WEAPON_SYM, doprwep }, + { ARMOR_SYM, doprarm }, + { RING_SYM, doprring }, + { '$', doprgold }, + { '#', doextcmd }, + { 0, 0 } +}; + +struct ext_func_tab extcmdlist[] = { + { "dip", dodip }, + { "pray", dopray }, + { NULL, donull } +}; + +extern char quitchars[]; + +void +rhack(const char *cmd) +{ + struct func_tab *tlist = cmdlist; + boolean firsttime = FALSE; + int res; + + if (!cmd) { + firsttime = TRUE; + flags.nopick = 0; + cmd = parse(); + } + if (!*cmd || (*cmd & 0377) == 0377 || + (flags.no_rest_on_space && *cmd == ' ')) { + bell(); + flags.move = 0; + return; /* probably we just had an interrupt */ + } + if (movecmd(*cmd)) { +walk: + if (multi) + flags.mv = 1; + domove(); + return; + } + if (movecmd(lowc(*cmd))) { + flags.run = 1; +rush: + if (firsttime) { + if (!multi) + multi = COLNO; + u.last_str_turn = 0; + } + flags.mv = 1; +#ifdef QUEST + if (flags.run >= 4) + finddir(); + if (firsttime) { + u.ux0 = u.ux + u.dx; + u.uy0 = u.uy + u.dy; + } +#endif /* QUEST */ + domove(); + return; + } + if ((*cmd == 'f' && movecmd(cmd[1])) || movecmd(unctrl(*cmd))) { + flags.run = 2; + goto rush; + } + if (*cmd == 'F' && movecmd(lowc(cmd[1]))) { + flags.run = 3; + goto rush; + } + if (*cmd == 'm' && movecmd(cmd[1])) { + flags.run = 0; + flags.nopick = 1; + goto walk; + } + if (*cmd == 'M' && movecmd(lowc(cmd[1]))) { + flags.run = 1; + flags.nopick = 1; + goto rush; + } +#ifdef QUEST + if (*cmd == cmd[1] && (*cmd == 'f' || *cmd == 'F')) { + flags.run = 4; + if (*cmd == 'F') + flags.run += 2; + if (cmd[2] == '-') + flags.run += 1; + goto rush; + } +#endif /* QUEST */ + while (tlist->f_char) { + if (*cmd == tlist->f_char) { + res = (*(tlist->f_funct))(); + if (!res) { + flags.move = 0; + multi = 0; + } + return; + } + tlist++; + } + { + char expcmd[10]; + char *cp = expcmd; + while (*cmd && cp - expcmd < (int)sizeof(expcmd) - 2) { + if (*cmd >= 040 && *cmd < 0177) + *cp++ = *cmd++; + else { + *cp++ = '^'; + *cp++ = *cmd++ ^ 0100; + } + } + *cp++ = 0; + pline("Unknown command '%s'.", expcmd); + } + multi = flags.move = 0; +} + +static int +doextcmd(void) /* here after # - now read a full-word command */ +{ + char buf[BUFSZ]; + struct ext_func_tab *efp = extcmdlist; + + pline("# "); + getlin(buf); + clrlin(); + if (buf[0] == '\033') + return (0); + while (efp->ef_txt) { + if (!strcmp(efp->ef_txt, buf)) + return ((*(efp->ef_funct))()); + efp++; + } + pline("%s: unknown command.", buf); + return (0); +} + +static char +lowc(char sym) +{ + return ((sym >= 'A' && sym <= 'Z') ? sym + 'a' - 'A' : sym); +} + +static char +unctrl(char sym) +{ + return ((sym >= ('A' & 037) && sym <= ('Z' & 037)) ? sym + 0140 : sym); +} + +/* 'rogue'-like direction commands */ +char sdir[] = "hykulnjb><"; +schar xdir[10] = { -1, -1, 0, 1, 1, 1, 0, -1, 0, 0 }; +schar ydir[10] = { 0, -1, -1, -1, 0, 1, 1, 1, 0, 0 }; +schar zdir[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, -1 }; + +bool +movecmd(char sym) /* also sets u.dz, but returns false for <> */ +{ + char *dp; + + u.dz = 0; + if (!(dp = strchr(sdir, sym))) + return (0); + u.dx = xdir[dp - sdir]; + u.dy = ydir[dp - sdir]; + u.dz = zdir[dp - sdir]; + return (!u.dz); +} + +bool +getdir(bool s) +{ + char dirsym; + + if (s) + pline("In what direction?"); + dirsym = readchar(); + if (!movecmd(dirsym) && !u.dz) { + if (!strchr(quitchars, dirsym)) + pline("What a strange direction!"); + return (0); + } + if (Confusion && !u.dz) + confdir(); + return (1); +} + +void +confdir(void) +{ + int x = rn2(8); + + u.dx = xdir[x]; + u.dy = ydir[x]; +} + +#ifdef QUEST +void +finddir(void) +{ + int i, ui = u.di; + + for (i = 0; i <= 8; i++) { + if (flags.run & 1) + ui++; + else + ui += 7; + ui %= 8; + if (i == 8) { + pline("Not near a wall."); + flags.move = multi = 0; + return; + } + if (!isroom(u.ux + xdir[ui], u.uy + ydir[ui])) + break; + } + for (i = 0; i <= 8; i++) { + if (flags.run & 1) + ui += 7; + else + ui++; + ui %= 8; + if (i == 8) { + pline("Not near a room."); + flags.move = multi = 0; + return; + } + if (isroom(u.ux + xdir[ui], u.uy + ydir[ui])) + break; + } + u.di = ui; + u.dx = xdir[ui]; + u.dy = ydir[ui]; +} + +static bool +isroom(int x, int y) +{ /* what about POOL? */ + return (isok(x, y) && (levl[x][y].typ == ROOM || + (levl[x][y].typ >= LDOOR && flags.run >= 6))); +} +#endif /* QUEST */ + +bool +isok(int x, int y) +{ + /* x corresponds to curx, so x==1 is the first column. Ach. %% */ + return (x >= 1 && x <= COLNO - 1 && y >= 0 && y <= ROWNO - 1); +} + +/* + * done2 is a function that fits into cmdlist[] (int func(void)) + * and calls done1 which discards its argument. + */ +static int +done2(void) +{ + done1(0); + return (0); +} diff --git a/hack/hack.do.c b/hack/hack.do.c new file mode 100644 index 0000000..8d6277d --- /dev/null +++ b/hack/hack.do.c @@ -0,0 +1,512 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.do.c - version 1.0.3 */ +/* $FreeBSD: src/games/hack/hack.do.c,v 1.4 1999/11/16 10:26:36 marcel Exp $ */ +/* $DragonFly: src/games/hack/hack.do.c,v 1.5 2006/08/21 19:45:32 pavalos Exp $ */ + +/* Contains code for 'd', 'D' (drop), '>', '<' (up, down) and 't' (throw) */ + +#include "hack.h" + +extern struct monst youmonst; + +static int drop(struct obj *); +static void dropy(struct obj *); + +int +dodrop(void) +{ + return (drop(getobj("0$#", "drop"))); +} + +static int +drop(struct obj *obj) +{ + if (!obj) + return (0); + if (obj->olet == '$') { /* pseudo object */ + long amount = OGOLD(obj); + + if (amount == 0) + pline("You didn't drop any gold pieces."); + else { + mkgold(amount, u.ux, u.uy); + pline("You dropped %ld gold piece%s.", + amount, plur(amount)); + if (Invisible) + newsym(u.ux, u.uy); + } + free(obj); + return (1); + } + if (obj->owornmask & (W_ARMOR | W_RING)) { + pline("You cannot drop something you are wearing."); + return (0); + } + if (obj == uwep) { + if (uwep->cursed) { + pline("Your weapon is welded to your hand!"); + return (0); + } + setuwep(NULL); + } + pline("You dropped %s.", doname(obj)); + dropx(obj); + return (1); +} + +/* Called in several places - should not produce texts */ +void +dropx(struct obj *obj) +{ + freeinv(obj); + dropy(obj); +} + +static void +dropy(struct obj *obj) +{ + if (obj->otyp == CRYSKNIFE) + obj->otyp = WORM_TOOTH; + obj->ox = u.ux; + obj->oy = u.uy; + obj->nobj = fobj; + fobj = obj; + if (Invisible) + newsym(u.ux, u.uy); + subfrombill(obj); + stackobj(obj); +} + +/* drop several things */ +int +doddrop(void) +{ + return (ggetobj("drop", drop, 0)); +} + +int +dodown(void) +{ + if (u.ux != xdnstair || u.uy != ydnstair) { + pline("You can't go down here."); + return (0); + } + if (u.ustuck) { + pline("You are being held, and cannot go down."); + return (1); + } + if (Levitation) { + pline("You're floating high above the stairs."); + return (0); + } + + goto_level(dlevel + 1, TRUE); + return (1); +} + +int +doup(void) +{ + if (u.ux != xupstair || u.uy != yupstair) { + pline("You can't go up here."); + return (0); + } + if (u.ustuck) { + pline("You are being held, and cannot go up."); + return (1); + } + if (!Levitation && inv_weight() + 5 > 0) { + pline("Your load is too heavy to climb the stairs."); + return (1); + } + + goto_level(dlevel - 1, TRUE); + return (1); +} + +void +goto_level(int newlevel, boolean at_stairs) +{ + int fd; + boolean up = (newlevel < dlevel); + + if (newlevel <= 0) /* in fact < 0 is impossible */ + done("escaped"); + if (newlevel > MAXLEVEL) /* strange ... */ + newlevel = MAXLEVEL; + if (newlevel == dlevel) /* this can happen */ + return; + + glo(dlevel); + fd = creat(lock, FMASK); + if (fd < 0) { + /* + * This is not quite impossible: e.g., we may have + * exceeded our quota. If that is the case then we + * cannot leave this level, and cannot save either. + * Another possibility is that the directory was not + * writable. + */ + pline("A mysterious force prevents you from going %s.", + up ? "up" : "down"); + return; + } + + if (Punished) + unplacebc(); + u.utrap = 0; /* needed in level_tele */ + u.ustuck = 0; /* idem */ + keepdogs(); + seeoff(1); + if (u.uswallow) /* idem */ + u.uswldtim = u.uswallow = 0; + flags.nscrinh = 1; + u.ux = FAR; /* hack */ + inshop(); /* probably was a trapdoor */ + + savelev(fd, dlevel); + close(fd); + + dlevel = newlevel; + if (maxdlevel < dlevel) + maxdlevel = dlevel; + glo(dlevel); + + if (!level_exists[dlevel]) + mklev(); + else { + if ((fd = open(lock, O_RDONLY)) < 0) { + pline("Cannot open %s .", lock); + pline("Probably someone removed it."); + done("tricked"); + } + getlev(fd, hackpid, dlevel); + close(fd); + } + + if (at_stairs) { + if (up) { + u.ux = xdnstair; + u.uy = ydnstair; + if (!u.ux) { /* entering a maze from below? */ + u.ux = xupstair; /* this will confuse the player! */ + u.uy = yupstair; + } + if (Punished && !Levitation) { + pline("With great effort you climb the stairs."); + placebc(1); + } + } else { + u.ux = xupstair; + u.uy = yupstair; + if (inv_weight() + 5 > 0 || Punished) { + pline("You fall down the stairs."); /* %% */ + losehp(rnd(3), "fall"); + if (Punished) { + if (uwep != uball && rn2(3)) { + pline("... and are hit by the iron ball."); + losehp(rnd(20), "iron ball"); + } + placebc(1); + } + selftouch("Falling, you"); + } + } + { + struct monst *mtmp = m_at(u.ux, u.uy); + if (mtmp) + mnexto(mtmp); + } + } else { /* trapdoor or level_tele */ + do { + u.ux = rnd(COLNO - 1); + u.uy = rn2(ROWNO); + } while (levl[u.ux][u.uy].typ != ROOM || + m_at(u.ux, u.uy)); + if (Punished) { + if (uwep != uball && !up /* %% */ && rn2(5)) { + pline("The iron ball falls on your head."); + losehp(rnd(25), "iron ball"); + } + placebc(1); + } + selftouch("Falling, you"); + } + inshop(); + initrack(); + + losedogs(); + { + struct monst *mtmp; + if ((mtmp = m_at(u.ux, u.uy))) /* riv05!a3 */ + mnexto(mtmp); + } + flags.nscrinh = 0; + setsee(); + seeobjs(); /* make old cadavers disappear - riv05!a3 */ + docrt(); + pickup(1); + read_engr_at(u.ux, u.uy); +} + +int +donull(void) +{ + return (1); /* Do nothing, but let other things happen */ +} + +int +dopray(void) +{ + nomovemsg = "You finished your prayer."; + nomul(-3); + return (1); +} + +int +dothrow(void) +{ + struct obj *obj; + struct monst *mon; + int tmp; + + obj = getobj("#)", "throw"); /* it is also possible to throw food */ + /* (or jewels, or iron balls ... ) */ + if (!obj || !getdir(1)) /* ask "in what direction?" */ + return (0); + if (obj->owornmask & (W_ARMOR | W_RING)) { + pline("You can't throw something you are wearing."); + return (0); + } + + u_wipe_engr(2); + + if (obj == uwep) { + if (obj->cursed) { + pline("Your weapon is welded to your hand."); + return (1); + } + if (obj->quan > 1) + setuwep(splitobj(obj, 1)); + else + setuwep(NULL); + } else if (obj->quan > 1) + splitobj(obj, 1); + freeinv(obj); + if (u.uswallow) { + mon = u.ustuck; + bhitpos.x = mon->mx; + bhitpos.y = mon->my; + } else if (u.dz) { + if (u.dz < 0) { + pline("%s hits the ceiling, then falls back on top of your head.", + Doname(obj)); /* note: obj->quan == 1 */ + if (obj->olet == POTION_SYM) + potionhit(&youmonst, obj); + else { + if (uarmh) + pline("Fortunately, you are wearing a helmet!"); + losehp(uarmh ? 1 : rnd((int)(obj->owt)), + "falling object"); + dropy(obj); + } + } else { + pline("%s hits the floor.", Doname(obj)); + if (obj->otyp == EXPENSIVE_CAMERA) { + pline("It is shattered in a thousand pieces!"); + obfree(obj, NULL); + } else if (obj->otyp == EGG) { + pline("\"Splash!\""); + obfree(obj, NULL); + } else if (obj->olet == POTION_SYM) { + pline("The flask breaks, and you smell a peculiar odor ..."); + potionbreathe(obj); + obfree(obj, NULL); + } else + dropy(obj); + } + return (1); + } else if (obj->otyp == BOOMERANG) { + mon = boomhit(u.dx, u.dy); + if (mon == &youmonst) { /* the thing was caught */ + addinv(obj); + return (1); + } + } else { + if (obj->otyp == PICK_AXE && shkcatch(obj)) + return (1); + + mon = bhit(u.dx, u.dy, (obj->otyp == ICE_BOX) ? 1 : + (!Punished || obj != uball) ? 8 : !u.ustuck ? 5 : 1, + obj->olet, (void (*)(struct monst *, struct obj *)) 0, + (bool (*)(struct obj *, struct obj *)) 0, obj); + } + if (mon) { + /* awake monster if sleeping */ + wakeup(mon); + + if (obj->olet == WEAPON_SYM) { + tmp = -1 + u.ulevel + mon->data->ac + abon(); + if (obj->otyp < ROCK) { + if (!uwep || + uwep->otyp != obj->otyp + (BOW - ARROW)) + tmp -= 4; + else { + tmp += uwep->spe; + } + } else if (obj->otyp == BOOMERANG) + tmp += 4; + tmp += obj->spe; + if (u.uswallow || tmp >= rnd(20)) { + if (hmon(mon, obj, 1) == TRUE) { + /* mon still alive */ +#ifndef NOWORM + cutworm(mon, bhitpos.x, bhitpos.y, obj->otyp); +#endif /* NOWORM */ + } else + mon = 0; + /* weapons thrown disappear sometimes */ + if (obj->otyp < BOOMERANG && rn2(3)) { + /* check bill; free */ + obfree(obj, NULL); + return (1); + } + } else + miss(objects[obj->otyp].oc_name, mon); + } else if (obj->otyp == HEAVY_IRON_BALL) { + tmp = -1 + u.ulevel + mon->data->ac + abon(); + if (!Punished || obj != uball) + tmp += 2; + if (u.utrap) + tmp -= 2; + if (u.uswallow || tmp >= rnd(20)) { + if (hmon(mon, obj, 1) == FALSE) + mon = 0; /* he died */ + } else + miss("iron ball", mon); + } else if (obj->olet == POTION_SYM && u.ulevel > rn2(15)) { + potionhit(mon, obj); + return (1); + } else { + if (cansee(bhitpos.x, bhitpos.y)) + pline("You miss %s.", monnam(mon)); + else + pline("You miss it."); + if (obj->olet == FOOD_SYM && mon->data->mlet == 'd') + if (tamedog(mon, obj)) + return (1); + if (obj->olet == GEM_SYM && mon->data->mlet == 'u' && + !mon->mtame) { + if (obj->dknown && objects[obj->otyp].oc_name_known) { + if (objects[obj->otyp].g_val > 0) { + u.uluck += 5; + goto valuable; + } else + pline("%s is not interested in your junk.", + Monnam(mon)); + } else { /* value unknown to @ */ + u.uluck++; +valuable: + if (u.uluck > LUCKMAX) /* dan@ut-ngp */ + u.uluck = LUCKMAX; + pline("%s graciously accepts your gift.", + Monnam(mon)); + mpickobj(mon, obj); + rloc(mon); + return (1); + } + } + } + } + /* the code following might become part of dropy() */ + if (obj->otyp == CRYSKNIFE) + obj->otyp = WORM_TOOTH; + obj->ox = bhitpos.x; + obj->oy = bhitpos.y; + obj->nobj = fobj; + fobj = obj; + /* prevent him from throwing articles to the exit and escaping */ + /* subfrombill(obj); */ + stackobj(obj); + if (Punished && obj == uball && + (bhitpos.x != u.ux || bhitpos.y != u.uy)) { + freeobj(uchain); + unpobj(uchain); + if (u.utrap) { + if (u.utraptype == TT_PIT) + pline("The ball pulls you out of the pit!"); + else { + long side = rn2(3) ? LEFT_SIDE : RIGHT_SIDE; + pline("The ball pulls you out of the bear trap."); + pline("Your %s leg is severely damaged.", + (side == LEFT_SIDE) ? "left" : "right"); + set_wounded_legs(side, 500 + rn2(1000)); + losehp(2, "thrown ball"); + } + u.utrap = 0; + } + unsee(); + uchain->nobj = fobj; + fobj = uchain; + u.ux = uchain->ox = bhitpos.x - u.dx; + u.uy = uchain->oy = bhitpos.y - u.dy; + setsee(); + inshop(); + } + if (cansee(bhitpos.x, bhitpos.y)) + prl(bhitpos.x, bhitpos.y); + return (1); +} + +/* split obj so that it gets size num */ +/* remainder is put in the object structure delivered by this call */ +struct obj * +splitobj(struct obj *obj, int num) +{ + struct obj *otmp; + + otmp = newobj(0); + *otmp = *obj; /* copies whole structure */ + otmp->o_id = flags.ident++; + otmp->onamelth = 0; + obj->quan = num; + obj->owt = weight(obj); + otmp->quan -= num; + otmp->owt = weight(otmp); /* -= obj->owt ? */ + obj->nobj = otmp; + if (obj->unpaid) + splitbill(obj, otmp); + return (otmp); +} + +void +more_experienced(int exp, int rexp) +{ + u.uexp += exp; + u.urexp += 4 * exp + rexp; + if (exp) + flags.botl = 1; + if (u.urexp >= ((pl_character[0] == 'W') ? 1000 : 2000)) + flags.beginner = 0; +} + +void +set_wounded_legs(long side, int timex) +{ + if (!Wounded_legs || (Wounded_legs & TIMEOUT)) + Wounded_legs |= side + timex; + else + Wounded_legs |= side; +} + +void +heal_legs(void) +{ + if (Wounded_legs) { + if ((Wounded_legs & BOTH_SIDES) == BOTH_SIDES) + pline("Your legs feel somewhat better."); + else + pline("Your leg feels somewhat better."); + Wounded_legs = 0; + } +} diff --git a/hack/hack.do_name.c b/hack/hack.do_name.c new file mode 100644 index 0000000..38ef837 --- /dev/null +++ b/hack/hack.do_name.c @@ -0,0 +1,316 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.do_name.c - version 1.0.3 */ +/* $FreeBSD: src/games/hack/hack.do_name.c,v 1.5 1999/11/16 10:26:36 marcel Exp $ */ +/* $DragonFly: src/games/hack/hack.do_name.c,v 1.4 2006/08/21 19:45:32 pavalos Exp $ */ + +#include "hack.h" + +static void do_oname(struct obj *); +static char *xmonnam(struct monst *, int); +static char *lmonnam(struct monst *); +static char *visctrl(char); + +coord +getpos(int force, const char *goal) +{ + int cx, cy, i, c; + coord cc; + + pline("(For instructions type a ?)"); + cx = u.ux; + cy = u.uy; + curs(cx, cy + 2); + while ((c = readchar()) != '.') { + for (i = 0; i < 8; i++) + if (sdir[i] == c) { + if (1 <= cx + xdir[i] && cx + xdir[i] <= COLNO) + cx += xdir[i]; + if (0 <= cy + ydir[i] && cy + ydir[i] <= ROWNO - 1) + cy += ydir[i]; + goto nxtc; + } + if (c == '?') { + pline("Use [hjkl] to move the cursor to %s.", goal); + pline("Type a . when you are at the right place."); + } else { + pline("Unknown direction: '%s' (%s).", + visctrl(c), + force ? "use hjkl or ." : "aborted"); + if (force) + goto nxtc; + cc.x = -1; + cc.y = 0; + return (cc); + } +nxtc: + curs(cx, cy + 2); + } + cc.x = cx; + cc.y = cy; + return (cc); +} + +int +do_mname(void) +{ + char buf[BUFSZ]; + coord cc; + int cx, cy, lth, i; + struct monst *mtmp, *mtmp2; + + cc = getpos(0, "the monster you want to name"); + cx = cc.x; + cy = cc.y; + if (cx < 0) + return (0); + mtmp = m_at(cx, cy); + if (!mtmp) { + if (cx == u.ux && cy == u.uy) + pline("This ugly monster is called %s and cannot be renamed.", + plname); + else + pline("There is no monster there."); + return (1); + } + if (mtmp->mimic) { + pline("I see no monster there."); + return (1); + } + if (!cansee(cx, cy)) { + pline("I cannot see a monster there."); + return (1); + } + pline("What do you want to call %s? ", lmonnam(mtmp)); + getlin(buf); + clrlin(); + if (!*buf || *buf == '\033') + return (1); + lth = strlen(buf) + 1; + if (lth > 63) { + buf[62] = 0; + lth = 63; + } + mtmp2 = newmonst(mtmp->mxlth + lth); + *mtmp2 = *mtmp; + for (i = 0; (unsigned)i < mtmp->mxlth; i++) + ((char *)mtmp2->mextra)[i] = ((char *)mtmp->mextra)[i]; + mtmp2->mnamelth = lth; + strcpy(NAME(mtmp2), buf); + replmon(mtmp, mtmp2); + return (1); +} + +/* + * This routine changes the address of obj . Be careful not to call it + * when there might be pointers around in unknown places. For now: only + * when obj is in the inventory. + */ +static void +do_oname(struct obj *obj) +{ + struct obj *otmp, *otmp2; + int lth; + char buf[BUFSZ]; + + pline("What do you want to name %s? ", doname(obj)); + getlin(buf); + clrlin(); + if (!*buf || *buf == '\033') + return; + lth = strlen(buf) + 1; + if (lth > 63) { + buf[62] = 0; + lth = 63; + } + otmp2 = newobj(lth); + *otmp2 = *obj; + otmp2->onamelth = lth; + strcpy(ONAME(otmp2), buf); + + setworn(NULL, obj->owornmask); + setworn(otmp2, otmp2->owornmask); + + /* + * do freeinv(obj); etc. by hand in order to preserve the position of + * this object in the inventory + */ + if (obj == invent) + invent = otmp2; + else + for (otmp = invent;; otmp = otmp->nobj) { + if (!otmp) + panic("Do_oname: cannot find obj."); + if (otmp->nobj == obj) { + otmp->nobj = otmp2; + break; + } + } + /*obfree(obj, otmp2);*/ /* now unnecessary: no pointers on bill */ + free(obj); /* let us hope nobody else saved a pointer */ +} + +int +ddocall(void) +{ + struct obj *obj; + + pline("Do you want to name an individual object? [ny] "); + switch (readchar()) { + case '\033': + break; + case 'y': + obj = getobj("#", "name"); + if (obj) + do_oname(obj); + break; + default: + obj = getobj("?!=/", "call"); + if (obj) + docall(obj); + } + return (0); +} + +void +docall(struct obj *obj) +{ + char buf[BUFSZ]; + struct obj otemp; + char **str1; + char *str; + + otemp = *obj; + otemp.quan = 1; + otemp.onamelth = 0; + str = xname(&otemp); + pline("Call %s %s: ", strchr(vowels, *str) ? "an" : "a", str); + getlin(buf); + clrlin(); + if (!*buf || *buf == '\033') + return; + str = newstring(strlen(buf) + 1); + strcpy(str, buf); + str1 = &(objects[obj->otyp].oc_uname); + if (*str1) + free(*str1); + *str1 = str; +} + +/* these names should have length < PL_NSIZ */ +const char *ghostnames[] = { + "adri", "andries", "andreas", "bert", "david", "dirk", "emile", + "frans", "fred", "greg", "hether", "jay", "john", "jon", "kay", + "kenny", "maud", "michiel", "mike", "peter", "robert", "ron", + "tom", "wilmar" +}; + +static char * +xmonnam(struct monst *mtmp, int vb) +{ + static char buf[BUFSZ]; /* %% */ + + if (mtmp->mnamelth && !vb) { + strcpy(buf, NAME(mtmp)); + return (buf); + } + switch (mtmp->data->mlet) { + case ' ': + { + const char *gn = (const char *)mtmp->mextra; + if (!*gn) { /* might also look in scorefile */ + gn = ghostnames[rn2(SIZE(ghostnames))]; + if (!rn2(2)) + strcpy((char *)mtmp->mextra, + !rn2(5) ? plname : gn); + } + sprintf(buf, "%s's ghost", gn); + } + break; + case '@': + if (mtmp->isshk) { + strcpy(buf, shkname(mtmp)); + break; + } + /* fall into next case */ + default: + sprintf(buf, "the %s%s", + mtmp->minvis ? "invisible " : "", + mtmp->data->mname); + } + if (vb && mtmp->mnamelth) { + strcat(buf, " called "); + strcat(buf, NAME(mtmp)); + } + return (buf); +} + +static char * +lmonnam(struct monst *mtmp) +{ + return (xmonnam(mtmp, 1)); +} + +char * +monnam(struct monst *mtmp) +{ + return (xmonnam(mtmp, 0)); +} + +char * +Monnam(struct monst *mtmp) +{ + char *bp = monnam(mtmp); + + if ('a' <= *bp && *bp <= 'z') + *bp += ('A' - 'a'); + return (bp); +} + +char * +amonnam(struct monst *mtmp, const char *adj) +{ + char *bp = monnam(mtmp); + static char buf[BUFSZ]; /* %% */ + + if (!strncmp(bp, "the ", 4)) + bp += 4; + sprintf(buf, "the %s %s", adj, bp); + return (buf); +} + +char * +Amonnam(struct monst *mtmp, const char *adj) +{ + char *bp = amonnam(mtmp, adj); + + *bp = 'T'; + return (bp); +} + +char * +Xmonnam(struct monst *mtmp) +{ + char *bp = Monnam(mtmp); + + if (!strncmp(bp, "The ", 4)) { + bp += 2; + *bp = 'A'; + } + return (bp); +} + +static char * +visctrl(char c) +{ + static char ccc[3]; + + if (c < 040) { + ccc[0] = '^'; + ccc[1] = c + 0100; + ccc[2] = 0; + } else { + ccc[0] = c; + ccc[1] = 0; + } + return (ccc); +} diff --git a/hack/hack.do_wear.c b/hack/hack.do_wear.c new file mode 100644 index 0000000..c7f7802 --- /dev/null +++ b/hack/hack.do_wear.c @@ -0,0 +1,395 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.do_wear.c - version 1.0.3 */ +/* $FreeBSD: src/games/hack/hack.do_wear.c,v 1.3 1999/11/16 02:57:03 billf Exp $ */ +/* $DragonFly: src/games/hack/hack.do_wear.c,v 1.6 2008/04/20 13:44:24 swildner Exp $ */ + +#include "hack.h" +extern char quitchars[]; + +static void off_msg(struct obj *); +static int dorr(struct obj *); +static bool cursed(struct obj *); + +static void +off_msg(struct obj *otmp) +{ + pline("You were wearing %s.", doname(otmp)); +} + +int +doremarm(void) +{ + struct obj *otmp; + + if (!uarm && !uarmh && !uarms && !uarmg) { + pline("Not wearing any armor."); + return (0); + } + otmp = (!uarmh && !uarms && !uarmg) ? uarm : + (!uarms && !uarm && !uarmg) ? uarmh : + (!uarmh && !uarm && !uarmg) ? uarms : + (!uarmh && !uarm && !uarms) ? uarmg : + getobj("[", "take off"); + if (!otmp) + return (0); + if (!(otmp->owornmask & (W_ARMOR - W_ARM2))) { + pline("You can't take that off."); + return (0); + } + if (otmp == uarmg && uwep && uwep->cursed) { /* myers@uwmacc */ + pline("You seem not able to take off the gloves while holding your weapon."); + return (0); + } + armoroff(otmp); + return (1); +} + +int +doremring(void) +{ + if (!uleft && !uright) { + pline("Not wearing any ring."); + return (0); + } + if (!uleft) + return (dorr(uright)); + if (!uright) + return (dorr(uleft)); + if (uleft && uright) + for (;;) { + char answer; + + pline("What ring, Right or Left? [ rl?]"); + if (strchr(quitchars, (answer = readchar()))) + return (0); + switch (answer) { + case 'l': + case 'L': + return (dorr(uleft)); + case 'r': + case 'R': + return (dorr(uright)); + case '?': + doprring(); + /* might look at morc here %% */ + } + } + /* NOTREACHED */ + return (0); +} + +static int +dorr(struct obj *otmp) +{ + if (cursed(otmp)) + return (0); + ringoff(otmp); + off_msg(otmp); + return (1); +} + +static bool +cursed(struct obj *otmp) +{ + if (otmp->cursed) { + pline("You can't. It appears to be cursed."); + return (1); + } + return (0); +} + +bool +armoroff(struct obj *otmp) +{ + int delay = -objects[otmp->otyp].oc_delay; + + if (cursed(otmp)) + return (0); + setworn(NULL, otmp->owornmask & W_ARMOR); + if (delay) { + nomul(delay); + switch (otmp->otyp) { + case HELMET: + nomovemsg = "You finished taking off your helmet."; + break; + case PAIR_OF_GLOVES: + nomovemsg = "You finished taking off your gloves"; + break; + default: + nomovemsg = "You finished taking off your suit."; + } + } else + off_msg(otmp); + return (1); +} + +int +doweararm(void) +{ + struct obj *otmp; + int delay; + int err = 0; + long mask = 0; + + otmp = getobj("[", "wear"); + if (!otmp) + return (0); + if (otmp->owornmask & W_ARMOR) { + pline("You are already wearing that!"); + return (0); + } + if (otmp->otyp == HELMET) { + if (uarmh) { + pline("You are already wearing a helmet."); + err++; + } else + mask = W_ARMH; + } else if (otmp->otyp == SHIELD) { + if (uarms) + pline("You are already wearing a shield."), err++; + if (uwep && uwep->otyp == TWO_HANDED_SWORD) { + pline("You cannot wear a shield and wield a two-handed sword."); + err++; + } + if (!err) + mask = W_ARMS; + } else if (otmp->otyp == PAIR_OF_GLOVES) { + if (uarmg) { + pline("You are already wearing gloves."); + err++; + } else if (uwep && uwep->cursed) { + pline("You cannot wear gloves over your weapon."); + err++; + } else + mask = W_ARMG; + } else { + if (uarm) { + if (otmp->otyp != ELVEN_CLOAK || uarm2) { + pline("You are already wearing some armor."); + err++; + } + } + if (!err) + mask = W_ARM; + } + if (otmp == uwep && uwep->cursed) { + if (!err++) + pline("%s is welded to your hand.", Doname(uwep)); + } + if (err) + return (0); + setworn(otmp, mask); + if (otmp == uwep) + setuwep(NULL); + delay = -objects[otmp->otyp].oc_delay; + if (delay) { + nomul(delay); + nomovemsg = "You finished your dressing manoeuvre."; + } + otmp->known = 1; + return (1); +} + +int +dowearring(void) +{ + struct obj *otmp; + long mask = 0; + long oldprop; + + if (uleft && uright) { + pline("There are no more ring-fingers to fill."); + return (0); + } + otmp = getobj("=", "wear"); + if (!otmp) + return (0); + if (otmp->owornmask & W_RING) { + pline("You are already wearing that!"); + return (0); + } + if (otmp == uleft || otmp == uright) { + pline("You are already wearing that."); + return (0); + } + if (otmp == uwep && uwep->cursed) { + pline("%s is welded to your hand.", Doname(uwep)); + return (0); + } + if (uleft) + mask = RIGHT_RING; + else if (uright) + mask = LEFT_RING; + else + do { + char answer; + + pline("What ring-finger, Right or Left? "); + if (strchr(quitchars, (answer = readchar()))) + return (0); + switch (answer) { + case 'l': + case 'L': + mask = LEFT_RING; + break; + case 'r': + case 'R': + mask = RIGHT_RING; + break; + } + } while (!mask); + setworn(otmp, mask); + if (otmp == uwep) + setuwep(NULL); + oldprop = u.uprops[PROP(otmp->otyp)].p_flgs; + u.uprops[PROP(otmp->otyp)].p_flgs |= mask; + switch (otmp->otyp) { + case RIN_LEVITATION: + if (!oldprop) + float_up(); + break; + case RIN_PROTECTION_FROM_SHAPE_CHANGERS: + rescham(); + break; + case RIN_GAIN_STRENGTH: + u.ustr += otmp->spe; + u.ustrmax += otmp->spe; + if (u.ustr > 118) + u.ustr = 118; + if (u.ustrmax > 118) + u.ustrmax = 118; + flags.botl = 1; + break; + case RIN_INCREASE_DAMAGE: + u.udaminc += otmp->spe; + break; + } + prinv(otmp); + return (1); +} + +void +ringoff(struct obj *obj) +{ + long mask; + + mask = obj->owornmask & W_RING; + setworn(NULL, obj->owornmask); + if (!(u.uprops[PROP(obj->otyp)].p_flgs & mask)) + impossible("Strange... I didn't know you had that ring."); + u.uprops[PROP(obj->otyp)].p_flgs &= ~mask; + switch (obj->otyp) { + case RIN_FIRE_RESISTANCE: + /* Bad luck if the player is in hell... --jgm */ + if (!Fire_resistance && dlevel >= 30) { + pline("The flames of Hell burn you to a crisp."); + killer = "stupidity in hell"; + done("burned"); + } + break; + case RIN_LEVITATION: + if (!Levitation) /* no longer floating */ + float_down(); + break; + case RIN_GAIN_STRENGTH: + u.ustr -= obj->spe; + u.ustrmax -= obj->spe; + if (u.ustr > 118) + u.ustr = 118; + if (u.ustrmax > 118) + u.ustrmax = 118; + flags.botl = 1; + break; + case RIN_INCREASE_DAMAGE: + u.udaminc -= obj->spe; + break; + } +} + +void +find_ac(void) +{ + int uac = 10; + + if (uarm) + uac -= ARM_BONUS(uarm); + if (uarm2) + uac -= ARM_BONUS(uarm2); + if (uarmh) + uac -= ARM_BONUS(uarmh); + if (uarms) + uac -= ARM_BONUS(uarms); + if (uarmg) + uac -= ARM_BONUS(uarmg); + if (uleft && uleft->otyp == RIN_PROTECTION) + uac -= uleft->spe; + if (uright && uright->otyp == RIN_PROTECTION) + uac -= uright->spe; + if (uac != u.uac) { + u.uac = uac; + flags.botl = 1; + } +} + +void +glibr(void) +{ + struct obj *otmp; + int xfl = 0; + + if (!uarmg) + if (uleft || uright) { + /* Note: at present also cursed rings fall off */ + pline("Your %s off your fingers.", + (uleft && uright) ? "rings slip" : "ring slips"); + xfl++; + if ((otmp = uleft) != NULL) { + ringoff(uleft); + dropx(otmp); + } + if ((otmp = uright) != NULL) { + ringoff(uright); + dropx(otmp); + } + } + if ((otmp = uwep) != NULL) { + /* Note: at present also cursed weapons fall */ + setuwep(NULL); + dropx(otmp); + pline("Your weapon %sslips from your hands.", + xfl ? "also " : ""); + } +} + +struct obj * +some_armor(void) +{ + struct obj *otmph = uarm; + + if (uarmh && (!otmph || !rn2(4))) + otmph = uarmh; + if (uarmg && (!otmph || !rn2(4))) + otmph = uarmg; + if (uarms && (!otmph || !rn2(4))) + otmph = uarms; + return (otmph); +} + +void +corrode_armor(void) +{ + struct obj *otmph = some_armor(); + + if (otmph) { + if (otmph->rustfree || + otmph->otyp == ELVEN_CLOAK || + otmph->otyp == LEATHER_ARMOR || + otmph->otyp == STUDDED_LEATHER_ARMOR) { + pline("Your %s not affected!", + aobjnam(otmph, "are")); + return; + } + pline("Your %s!", aobjnam(otmph, "corrode")); + otmph->spe--; + } +} diff --git a/hack/hack.dog.c b/hack/hack.dog.c new file mode 100644 index 0000000..531a3a4 --- /dev/null +++ b/hack/hack.dog.c @@ -0,0 +1,459 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.dog.c - version 1.0.3 */ +/* $FreeBSD: src/games/hack/hack.dog.c,v 1.3 1999/11/16 02:57:03 billf Exp $ */ + +#include "hack.h" +#include "hack.mfndpos.h" +#include "def.edog.h" + +struct permonst li_dog = + { "little dog", 'd', 2, 18, 6, 1, 6, sizeof(struct edog) }; +struct permonst dog = + { "dog", 'd', 4, 16, 5, 1, 6, sizeof(struct edog) }; +struct permonst la_dog = + { "large dog", 'd', 6, 15, 4, 2, 4, sizeof(struct edog) }; + +static void initedog(struct monst *); +static xchar dogfood(struct obj *); + +void +makedog(void) +{ + struct monst *mtmp = makemon(&li_dog, u.ux, u.uy); + + if (!mtmp) + return; /* dogs were genocided */ + initedog(mtmp); +} + +static void +initedog(struct monst *mtmp) +{ + mtmp->mtame = mtmp->mpeaceful = 1; + EDOG(mtmp)->hungrytime = 1000 + moves; + EDOG(mtmp)->eattime = 0; + EDOG(mtmp)->droptime = 0; + EDOG(mtmp)->dropdist = 10000; + EDOG(mtmp)->apport = 10; + EDOG(mtmp)->whistletime = 0; +} + +/* attach the monsters that went down (or up) together with @ */ +struct monst *mydogs = NULL; +struct monst *fallen_down = NULL; /* monsters that fell through a trapdoor */ +/* they will appear on the next level @ goes to, even if he goes up! */ + +void +losedogs(void) +{ + struct monst *mtmp; + + while ((mtmp = mydogs)) { + mydogs = mtmp->nmon; + mtmp->nmon = fmon; + fmon = mtmp; + mnexto(mtmp); + } + while ((mtmp = fallen_down)) { + fallen_down = mtmp->nmon; + mtmp->nmon = fmon; + fmon = mtmp; + rloc(mtmp); + } +} + +void +keepdogs(void) +{ + struct monst *mtmp; + + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) + if (dist(mtmp->mx, mtmp->my) < 3 && follower(mtmp) + && !mtmp->msleep && !mtmp->mfroz) { + relmon(mtmp); + mtmp->nmon = mydogs; + mydogs = mtmp; + unpmon(mtmp); + keepdogs(); /* we destroyed the link, so use recursion */ + return; /* (admittedly somewhat primitive) */ + } +} + +void +fall_down(struct monst *mtmp) +{ + relmon(mtmp); + mtmp->nmon = fallen_down; + fallen_down = mtmp; + unpmon(mtmp); + mtmp->mtame = 0; +} + +/* return quality of food; the lower the better */ +#define DOGFOOD 0 +#define CADAVER 1 +#define ACCFOOD 2 +#define MANFOOD 3 +#define APPORT 4 +#define POISON 5 +#define UNDEF 6 + +static xchar +dogfood(struct obj *obj) +{ + switch (obj->olet) { + case FOOD_SYM: + return ( + (obj->otyp == TRIPE_RATION) ? DOGFOOD : + (obj->otyp < CARROT) ? ACCFOOD : + (obj->otyp < CORPSE) ? MANFOOD : + (poisonous(obj) || obj->age + 50 <= moves || + obj->otyp == DEAD_COCKATRICE) + ? POISON : CADAVER + ); + default: + if (!obj->cursed) + return (APPORT); + /* fall into next case */ + case BALL_SYM: + case CHAIN_SYM: + case ROCK_SYM: + return (UNDEF); + } +} + +/* return 0 (no move), 1 (move) or 2 (dead) */ +int +dog_move(struct monst *mtmp, int after) +{ + int nx, ny, omx, omy, appr, nearer, j; + int udist, chi = 0, i, whappr; + struct monst *mtmp2; + struct permonst *mdat = mtmp->data; + struct edog *edog = EDOG(mtmp); + struct obj *obj; + struct trap *trap; + xchar cnt, chcnt, nix, niy; + schar dogroom, uroom; + xchar gx, gy, gtyp, otyp; /* current goal */ + coord poss[9]; + int info[9]; +#define GDIST(x, y) ((x - gx) * (x - gx) + (y - gy) * (y - gy)) +#define DDIST(x, y) ((x - omx) * (x - omx) + (y - omy) * (y - omy)) + + if (moves <= edog->eattime) /* dog is still eating */ + return (0); + omx = mtmp->mx; + omy = mtmp->my; + whappr = (moves - EDOG(mtmp)->whistletime < 5); + if (moves > edog->hungrytime + 500 && !mtmp->mconf) { + mtmp->mconf = 1; + mtmp->mhpmax /= 3; + if (mtmp->mhp > mtmp->mhpmax) + mtmp->mhp = mtmp->mhpmax; + if (cansee(omx, omy)) + pline("%s is confused from hunger.", Monnam(mtmp)); + else + pline("You feel worried about %s.", monnam(mtmp)); + } else if (moves > edog->hungrytime + 750 || mtmp->mhp < 1) { + if (cansee(omx, omy)) + pline("%s dies from hunger.", Monnam(mtmp)); + else + pline("You have a sad feeling for a moment, then it passes."); + mondied(mtmp); + return (2); + } + dogroom = inroom(omx, omy); + uroom = inroom(u.ux, u.uy); + udist = dist(omx, omy); + + /* maybe we tamed him while being swallowed --jgm */ + if (!udist) + return (0); + + /* if we are carrying sth then we drop it (perhaps near @) */ + /* Note: if apport == 1 then our behaviour is independent of udist */ + if (mtmp->minvent) { + if (!rn2(udist) || !rn2((int)edog->apport)) + if (rn2(10) < (int)edog->apport) { + relobj(mtmp, (int)mtmp->minvis); + if (edog->apport > 1) + edog->apport--; + edog->dropdist = udist; /* hpscdi!jon */ + edog->droptime = moves; + } + } else if ((obj = o_at(omx, omy))) { + if (!strchr("0_", obj->olet)) { + if ((otyp = dogfood(obj)) <= CADAVER) { + nix = omx; + niy = omy; + goto eatobj; + } + if (obj->owt < 10 * mtmp->data->mlevel) { + if (rn2(20) < (int)edog->apport + 3) { + if (rn2(udist) || + !rn2((int)edog->apport)) { + freeobj(obj); + unpobj(obj); + /* if (levl[omx][omy].scrsym == obj->olet) + * newsym(omx, omy); */ + mpickobj(mtmp, obj); + } + } + } + } + } + + /* first we look for food */ + gtyp = UNDEF; /* no goal as yet */ + gx = gy = 0; /* suppress 'used before set' message */ + for (obj = fobj; obj; obj = obj->nobj) { + otyp = dogfood(obj); + if (otyp > gtyp || otyp == UNDEF) + continue; + if (inroom(obj->ox, obj->oy) != dogroom) + continue; + if (otyp < MANFOOD && + (dogroom >= 0 || DDIST(obj->ox, obj->oy) < 10)) { + if (otyp < gtyp || (otyp == gtyp && + DDIST(obj->ox, obj->oy) < DDIST(gx, gy))) { + gx = obj->ox; + gy = obj->oy; + gtyp = otyp; + } + } else if (gtyp == UNDEF && dogroom >= 0 && + uroom == dogroom && + !mtmp->minvent && (int)edog->apport > rn2(8)) { + gx = obj->ox; + gy = obj->oy; + gtyp = APPORT; + } + } + if (gtyp == UNDEF || + (gtyp != DOGFOOD && gtyp != APPORT && moves < edog->hungrytime)) { + if (dogroom < 0 || dogroom == uroom) { + gx = u.ux; + gy = u.uy; +#ifndef QUEST + } else { + int tmp = rooms[dogroom].fdoor; + cnt = rooms[dogroom].doorct; + + gx = gy = FAR; /* random, far away */ + while (cnt--) { + if (dist(gx, gy) > + dist(doors[tmp].x, doors[tmp].y)) { + gx = doors[tmp].x; + gy = doors[tmp].y; + } + tmp++; + } + /* here gx == FAR e.g. when dog is in a vault */ + if (gx == FAR || (gx == omx && gy == omy)) { + gx = u.ux; + gy = u.uy; + } +#endif /* QUEST */ + } + appr = (udist >= 9) ? 1 : (mtmp->mflee) ? -1 : 0; + if (after && udist <= 4 && gx == u.ux && gy == u.uy) + return (0); + if (udist > 1) { + if (!IS_ROOM(levl[u.ux][u.uy].typ) || !rn2(4) || + whappr || + (mtmp->minvent && rn2((int)edog->apport))) + appr = 1; + } + /* if you have dog food he'll follow you more closely */ + if (appr == 0) { + obj = invent; + while (obj) { + if (obj->otyp == TRIPE_RATION) { + appr = 1; + break; + } + obj = obj->nobj; + } + } + } else /* gtyp != UNDEF */ + appr = 1; + if (mtmp->mconf) + appr = 0; + + if (gx == u.ux && gy == u.uy && (dogroom != uroom || dogroom < 0)) { + coord *cp; + cp = gettrack(omx, omy); + if (cp) { + gx = cp->x; + gy = cp->y; + } + } + + nix = omx; + niy = omy; + cnt = mfndpos(mtmp, poss, info, ALLOW_M | ALLOW_TRAPS); + chcnt = 0; + chi = -1; + for (i = 0; i < cnt; i++) { + nx = poss[i].x; + ny = poss[i].y; + if (info[i] & ALLOW_M) { + mtmp2 = m_at(nx, ny); + if (mtmp2->data->mlevel >= mdat->mlevel + 2 || + mtmp2->data->mlet == 'c') + continue; + if (after) /* hit only once each move */ + return (0); + + if (hitmm(mtmp, mtmp2) == 1 && rn2(4) && + mtmp2->mlstmv != moves && + hitmm(mtmp2, mtmp) == 2) + return (2); + return (0); + } + + /* dog avoids traps */ + /* but perhaps we have to pass a trap in order to follow @ */ + if ((info[i] & ALLOW_TRAPS) && (trap = t_at(nx, ny))) { + if (!trap->tseen && rn2(40)) + continue; + if (rn2(10)) + continue; + } + + /* dog eschewes cursed objects */ + /* but likes dog food */ + obj = fobj; + while (obj) { + if (obj->ox != nx || obj->oy != ny) + goto nextobj; + if (obj->cursed) + goto nxti; + if (obj->olet == FOOD_SYM && + (otyp = dogfood(obj)) < MANFOOD && + (otyp < ACCFOOD || edog->hungrytime <= moves)) { + /* + * Note: our dog likes the food so much that + * he might eat it even when it conceals a + * cursed object + */ + nix = nx; + niy = ny; + chi = i; +eatobj: + edog->eattime = + moves + obj->quan * + objects[obj->otyp].oc_delay; + if (edog->hungrytime < moves) + edog->hungrytime = moves; + edog->hungrytime += + 5 * obj->quan * + objects[obj->otyp].nutrition; + mtmp->mconf = 0; + if (cansee(nix, niy)) + pline("%s ate %s.", Monnam( + mtmp), doname(obj)); + /* perhaps this was a reward */ + if (otyp != CADAVER) + edog->apport += 200 / + (edog->dropdist + + moves - edog->droptime); + delobj(obj); + goto newdogpos; + } +nextobj: + obj = obj->nobj; + } + + for (j = 0; j < MTSZ && j < cnt - 1; j++) + if (nx == mtmp->mtrack[j].x && ny == mtmp->mtrack[j].y) + if (rn2(4 * (cnt - j))) + goto nxti; + +/* Some stupid C compilers cannot compute the whole expression at once. */ + nearer = GDIST(nx, ny); + nearer -= GDIST(nix, niy); + nearer *= appr; + if ((nearer == 0 && !rn2(++chcnt)) || nearer < 0 || + (nearer > 0 && !whappr && + ((omx == nix && omy == niy && !rn2(3)) + || !rn2(12)) + )) { + nix = nx; + niy = ny; + if (nearer < 0) + chcnt = 0; + chi = i; + } +nxti:; + } +newdogpos: + if (nix != omx || niy != omy) { + if (info[chi] & ALLOW_U) { + hitu(mtmp, d(mdat->damn, mdat->damd) + 1); + return (0); + } + mtmp->mx = nix; + mtmp->my = niy; + for (j = MTSZ - 1; j > 0; j--) + mtmp->mtrack[j] = mtmp->mtrack[j - 1]; + mtmp->mtrack[0].x = omx; + mtmp->mtrack[0].y = omy; + } + if (mintrap(mtmp) == 2) /* he died */ + return (2); + pmon(mtmp); + return (1); +} + +/* return roomnumber or -1 */ +int +inroom(xchar x, xchar y) +{ +#ifndef QUEST + struct mkroom *croom = &rooms[0]; + + while (croom->hx >= 0) { + if (croom->hx >= x - 1 && croom->lx <= x + 1 && + croom->hy >= y - 1 && croom->ly <= y + 1) + return (croom - rooms); + croom++; + } +#endif /* QUEST */ + return (-1); /* not in room or on door */ +} + +bool +tamedog(struct monst *mtmp, struct obj *obj) +{ + struct monst *mtmp2; + + if (flags.moonphase == FULL_MOON && night() && rn2(6)) + return (0); + + /* If we cannot tame him, at least he's no longer afraid. */ + mtmp->mflee = 0; + mtmp->mfleetim = 0; + if (mtmp->mtame || mtmp->mfroz || +#ifndef NOWORM + mtmp->wormno || +#endif /* NOWORM */ + mtmp->isshk || mtmp->isgd || strchr(" &@12", mtmp->data->mlet)) + return (0); /* no tame long worms? */ + if (obj) { + if (dogfood(obj) >= MANFOOD) + return (0); + if (cansee(mtmp->mx, mtmp->my)) + pline("%s devours the %s.", Monnam(mtmp), + objects[obj->otyp].oc_name); + obfree(obj, NULL); + } + mtmp2 = newmonst(sizeof(struct edog) + mtmp->mnamelth); + *mtmp2 = *mtmp; + mtmp2->mxlth = sizeof(struct edog); + if (mtmp->mnamelth) + strcpy(NAME(mtmp2), NAME(mtmp)); + initedog(mtmp2); + replmon(mtmp, mtmp2); + return (1); +} diff --git a/hack/hack.eat.c b/hack/hack.eat.c new file mode 100644 index 0000000..6373bc5 --- /dev/null +++ b/hack/hack.eat.c @@ -0,0 +1,492 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.eat.c - version 1.0.3 */ +/* $FreeBSD: src/games/hack/hack.eat.c,v 1.4 1999/11/16 10:26:36 marcel Exp $ */ + +#include "hack.h" +char POISONOUS[] = "ADKSVabhks"; + +static bool opentin(void); +static void Meatdone(void); +static void unfaint(void); +static void newuhs(bool); +static int eatcorpse(struct obj *); + +/* hunger texts used on bottom line (each 8 chars long) */ +#define SATIATED 0 +#define NOT_HUNGRY 1 +#define HUNGRY 2 +#define WEAK 3 +#define FAINTING 4 +#define FAINTED 5 +#define STARVED 6 + +const char *hu_stat[] = { + "Satiated", + " ", + "Hungry ", + "Weak ", + "Fainting", + "Fainted ", + "Starved " +}; + +void +init_uhunger(void) +{ + u.uhunger = 900; + u.uhs = NOT_HUNGRY; +} + +#define TTSZ SIZE(tintxts) +struct { const char *txt; int nut; } tintxts[] = { + { "It contains first quality peaches - what a surprise!", 40 }, + { "It contains salmon - not bad!", 60 }, + { "It contains apple juice - perhaps not what you hoped for.", 20 }, + { "It contains some nondescript substance, tasting awfully.", 500 }, + { "It contains rotten meat. You vomit.", -50 }, + { "It turns out to be empty.", 0 } +}; + +static struct { + struct obj *tin; + int usedtime, reqtime; +} tin; + +static bool +opentin(void) +{ + int r; + + if (!carried(tin.tin)) /* perhaps it was stolen? */ + return (0); /* %% probably we should use tinoid */ + if (tin.usedtime++ >= 50) { + pline("You give up your attempt to open the tin."); + return (0); + } + if (tin.usedtime < tin.reqtime) + return (1); /* still busy */ + + pline("You succeed in opening the tin."); + useup(tin.tin); + r = rn2(2 * TTSZ); + if (r < TTSZ) { + pline("%s", tintxts[r].txt); + lesshungry(tintxts[r].nut); + if (r == 1) { /* SALMON */ + Glib = rnd(15); + pline("Eating salmon made your fingers very slippery."); + } + } else { + pline("It contains spinach - this makes you feel like Popeye!"); + lesshungry(600); + if (u.ustr < 118) + u.ustr += rnd(((u.ustr < 17) ? 19 : 118) - u.ustr); + if (u.ustr > u.ustrmax) + u.ustrmax = u.ustr; + flags.botl = 1; + } + return (0); +} + +static void +Meatdone(void) +{ + u.usym = '@'; + prme(); +} + +int +doeat(void) +{ + struct obj *otmp; + struct objclass *ftmp; + int tmp; + + /* Is there some food (probably a heavy corpse) here on the ground? */ + if (!Levitation) + for (otmp = fobj; otmp; otmp = otmp->nobj) { + if (otmp->ox == u.ux && otmp->oy == u.uy && + otmp->olet == FOOD_SYM) { + pline("There %s %s here; eat %s? [ny] ", + (otmp->quan == 1) ? "is" : "are", + doname(otmp), + (otmp->quan == 1) ? "it" : "one"); + if (readchar() == 'y') { + if (otmp->quan != 1) + splitobj(otmp, 1); + freeobj(otmp); + otmp = addinv(otmp); + addtobill(otmp); + goto gotit; + } + } + } + + otmp = getobj("%", "eat"); + if (!otmp) + return (0); +gotit: + if (otmp->otyp == TIN) { + if (uwep) { + switch (uwep->otyp) { + case CAN_OPENER: + tmp = 1; + break; + case DAGGER: + case CRYSKNIFE: + tmp = 3; + break; + case PICK_AXE: + case AXE: + tmp = 6; + break; + default: + goto no_opener; + } + pline("Using your %s you try to open the tin.", + aobjnam(uwep, NULL)); + } else { +no_opener: + pline("It is not so easy to open this tin."); + if (Glib) { + pline("The tin slips out of your hands."); + if (otmp->quan > 1) { + struct obj *obj; + + obj = splitobj(otmp, 1); + if (otmp == uwep) + setuwep(obj); + } + dropx(otmp); + return (1); + } + tmp = 10 + rn2(1 + 500 / ((int)(u.ulevel + u.ustr))); + } + tin.reqtime = tmp; + tin.usedtime = 0; + tin.tin = otmp; + occupation = opentin; + occtxt = "opening the tin"; + return (1); + } + ftmp = &objects[otmp->otyp]; + multi = -ftmp->oc_delay; + if (otmp->otyp >= CORPSE && eatcorpse(otmp)) + goto eatx; + if (!rn2(7) && otmp->otyp != FORTUNE_COOKIE) { + pline("Blecch! Rotten food!"); + if (!rn2(4)) { + pline("You feel rather light headed."); + Confusion += d(2, 4); + } else if (!rn2(4) && !Blind) { + pline("Everything suddenly goes dark."); + Blind = d(2, 10); + seeoff(0); + } else if (!rn2(3)) { + if (Blind) + pline("The world spins and you slap against the floor."); + else + pline("The world spins and goes dark."); + nomul(-rnd(10)); + nomovemsg = "You are conscious again."; + } + lesshungry(ftmp->nutrition / 4); + } else { + if (u.uhunger >= 1500) { + pline("You choke over your food."); + pline("You die..."); + killer = ftmp->oc_name; + done("choked"); + } + switch (otmp->otyp) { + case FOOD_RATION: + if (u.uhunger <= 200) + pline("That food really hit the spot!"); + else if (u.uhunger <= 700) + pline("That satiated your stomach!"); + else { + pline("You're having a hard time getting all that food down."); + multi -= 2; + } + lesshungry(ftmp->nutrition); + if (multi < 0) + nomovemsg = "You finished your meal."; + break; + case TRIPE_RATION: + pline("Yak - dog food!"); + more_experienced(1, 0); + flags.botl = 1; + if (rn2(2)) { + pline("You vomit."); + morehungry(20); + if (Sick) { + Sick = 0; /* David Neves */ + pline("What a relief!"); + } + } else + lesshungry(ftmp->nutrition); + break; + default: + if (otmp->otyp >= CORPSE) + pline("That %s tasted terrible!", ftmp->oc_name); + else + pline("That %s was delicious!", ftmp->oc_name); + lesshungry(ftmp->nutrition); + if (otmp->otyp == DEAD_LIZARD && (Confusion > 2)) + Confusion = 2; + else +#ifdef QUEST + if (otmp->otyp == CARROT && !Blind) { + u.uhorizon++; + setsee(); + pline("Your vision improves."); + } else +#endif /* QUEST */ + if (otmp->otyp == FORTUNE_COOKIE) { + if (Blind) { + pline("This cookie has a scrap of paper inside!"); + pline("What a pity, that you cannot read it!"); + } else + outrumor(); + } else if (otmp->otyp == LUMP_OF_ROYAL_JELLY) { + /* This stuff seems to be VERY healthy! */ + if (u.ustrmax < 118) + u.ustrmax++; + if (u.ustr < u.ustrmax) + u.ustr++; + u.uhp += rnd(20); + if (u.uhp > u.uhpmax) { + if (!rn2(17)) + u.uhpmax++; + u.uhp = u.uhpmax; + } + heal_legs(); + } + break; + } + } +eatx: + if (multi < 0 && !nomovemsg) { + static char msgbuf[BUFSZ]; + sprintf(msgbuf, "You finished eating the %s.", ftmp->oc_name); + nomovemsg = msgbuf; + } + useup(otmp); + return (1); +} + +/* called in hack.main.c */ +void +gethungry(void) +{ + --u.uhunger; + if (moves % 2) { + if (Regeneration) + u.uhunger--; + if (Hunger) + u.uhunger--; + /* a3: if (Hunger & LEFT_RING) u.uhunger--; + * if (Hunger & RIGHT_RING) u.uhunger--; + * etc. + */ + } + if (moves % 20 == 0) { /* jimt@asgb */ + if (uleft) + u.uhunger--; + if (uright) + u.uhunger--; + } + newuhs(TRUE); +} + +/* called after vomiting and after performing feats of magic */ +void +morehungry(int num) +{ + u.uhunger -= num; + newuhs(TRUE); +} + +/* called after eating something (and after drinking fruit juice) */ +void +lesshungry(int num) +{ + u.uhunger += num; + newuhs(FALSE); +} + +static void +unfaint(void) +{ + u.uhs = FAINTING; + flags.botl = 1; +} + +static void +newuhs(bool incr) +{ + int newhs, h = u.uhunger; + + newhs = (h > 1000) ? SATIATED : + (h > 150) ? NOT_HUNGRY : + (h > 50) ? HUNGRY : + (h > 0) ? WEAK : FAINTING; + + if (newhs == FAINTING) { + if (u.uhs == FAINTED) + newhs = FAINTED; + if (u.uhs <= WEAK || rn2(20 - u.uhunger / 10) >= 19) { + if (u.uhs != FAINTED && multi >= 0 /* %% */) { + pline("You faint from lack of food."); + nomul(-10 + (u.uhunger / 10)); + nomovemsg = "You regain consciousness."; + afternmv = unfaint; + newhs = FAINTED; + } + } else if (u.uhunger < -(int)(200 + 25 * u.ulevel)) { + u.uhs = STARVED; + flags.botl = 1; + bot(); + pline("You die from starvation."); + done("starved"); + } + } + + if (newhs != u.uhs) { + if (newhs >= WEAK && u.uhs < WEAK) + losestr(1); /* this may kill you -- see below */ + else if (newhs < WEAK && u.uhs >= WEAK && u.ustr < u.ustrmax) + losestr(-1); + switch (newhs) { + case HUNGRY: + pline((!incr) ? "You only feel hungry now." : + (u.uhunger < 145) ? "You feel hungry." : + "You are beginning to feel hungry."); + break; + case WEAK: + pline((!incr) ? "You feel weak now." : + (u.uhunger < 45) ? "You feel weak." : + "You are beginning to feel weak."); + break; + } + u.uhs = newhs; + flags.botl = 1; + if (u.uhp < 1) { + pline("You die from hunger and exhaustion."); + killer = "exhaustion"; + done("starved"); + } + } +} + +#define CORPSE_I_TO_C(otyp) (char)((otyp >= DEAD_ACID_BLOB)\ + ? 'a' + (otyp - DEAD_ACID_BLOB)\ + : '@' + (otyp - DEAD_HUMAN)) +bool +poisonous(struct obj *otmp) +{ + return (strchr(POISONOUS, CORPSE_I_TO_C(otmp->otyp)) != 0); +} + +/* returns 1 if some text was printed */ +static int +eatcorpse(struct obj *otmp) +{ + char let = CORPSE_I_TO_C(otmp->otyp); + int tp = 0; + + if (let != 'a' && moves > otmp->age + 50 + rn2(100)) { + tp++; + pline("Ulch -- that meat was tainted!"); + pline("You get very sick."); + Sick = 10 + rn2(10); + u.usick_cause = objects[otmp->otyp].oc_name; + } else if (strchr(POISONOUS, let) && rn2(5)) { + tp++; + pline("Ecch -- that must have been poisonous!"); + if (!Poison_resistance) { + losestr(rnd(4)); + losehp(rnd(15), "poisonous corpse"); + } else + pline("You don't seem affected by the poison."); + } else if (strchr("ELNOPQRUuxz", let) && rn2(5)) { + tp++; + pline("You feel sick."); + losehp(rnd(8), "cadaver"); + } + switch (let) { + case 'L': + case 'N': + case 't': + Teleportation |= INTRINSIC; + break; + case 'W': + pluslvl(); + break; + case 'n': + u.uhp = u.uhpmax; + flags.botl = 1; + /* fall into next case */ + case '@': + pline("You cannibal! You will be sorry for this!"); + /* not tp++; */ + /* fall into next case */ + case 'd': + Aggravate_monster |= INTRINSIC; + break; + case 'I': + if (!Invis) { + Invis = 50 + rn2(100); + if (!See_invisible) + newsym(u.ux, u.uy); + } else { + Invis |= INTRINSIC; + See_invisible |= INTRINSIC; + } + /* fall into next case */ + case 'y': +#ifdef QUEST + u.uhorizon++; +#endif /* QUEST */ + /* fall into next case */ + case 'B': + Confusion = 50; + break; + case 'D': + Fire_resistance |= INTRINSIC; + break; + case 'E': + Telepat |= INTRINSIC; + break; + case 'F': + case 'Y': + Cold_resistance |= INTRINSIC; + break; + case 'k': + case 's': + Poison_resistance |= INTRINSIC; + break; + case 'c': + pline("You turn to stone."); + killer = "dead cockatrice"; + done("died"); + /* NOTREACHED */ + case 'a': + if (Stoned) { + pline("What a pity - you just destroyed a future piece of art!"); + tp++; + Stoned = 0; + } + break; + case 'M': + pline("You cannot resist the temptation to mimic a treasure chest."); + tp++; + nomul(-30); + afternmv = Meatdone; + nomovemsg = "You now again prefer mimicking a human."; + u.usym = '$'; + prme(); + break; + } + return (tp); +} diff --git a/hack/hack.end.c b/hack/hack.end.c new file mode 100644 index 0000000..46b3002 --- /dev/null +++ b/hack/hack.end.c @@ -0,0 +1,725 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.end.c - version 1.0.3 */ +/* $FreeBSD: src/games/hack/hack.end.c,v 1.4 1999/11/16 10:26:36 marcel Exp $ */ + +#include "hack.h" +#define Sprintf (void) sprintf + +#define newttentry() alloc(sizeof(struct toptenentry)) +#define NAMSZ 8 +#define DTHSZ 40 +#define PERSMAX 1 +#define POINTSMIN 1 /* must be > 0 */ +#define ENTRYMAX 100 /* must be >= 10 */ +#define PERS_IS_UID /* delete for PERSMAX per name; now per uid */ +struct toptenentry { + struct toptenentry *tt_next; + long int points; + int level,maxlvl,hp,maxhp; + int uid; + char plchar; + char sex; + char name[NAMSZ+1]; + char death[DTHSZ+1]; + char date[7]; /* yymmdd */ +} *tt_head; + +static void done_intr(int); +static void done_hangup(int); +static void topten(void); +static void outheader(void); +static int outentry(int, struct toptenentry *, int); +static char *itoa(int); +static const char *ordin(int); + +xchar maxdlevel = 1; + +void +done1(int unused __attribute__((unused))) +{ + signal(SIGINT, SIG_IGN); + pline("Really quit?"); + if (readchar() != 'y') { + signal(SIGINT, done1); + clrlin(); + fflush(stdout); + if (multi > 0) + nomul(0); + return; + } + done("quit"); + /* NOTREACHED */ +} + +int done_stopprint; +int done_hup; + +static void +done_intr(int unused __attribute__((unused))) +{ + done_stopprint++; + signal(SIGINT, SIG_IGN); + signal(SIGQUIT, SIG_IGN); +} + +static void +done_hangup(int unused __attribute__((unused))) +{ + done_hup++; + signal(SIGHUP, SIG_IGN); + done_intr(0); +} + +void +done_in_by(struct monst *mtmp) +{ + static char buf[BUFSZ]; + + pline("You die ..."); + if (mtmp->data->mlet == ' ') { + Sprintf(buf, "the ghost of %s", (char *)mtmp->mextra); + killer = buf; + } else if (mtmp->mnamelth) { + Sprintf(buf, "%s called %s", + mtmp->data->mname, NAME(mtmp)); + killer = buf; + } else if (mtmp->minvis) { + Sprintf(buf, "invisible %s", mtmp->data->mname); + killer = buf; + } else + killer = mtmp->data->mname; + done("died"); +} + +/* + * called with arg "died", "drowned", "escaped", "quit", "choked", + * "panicked", "burned", "starved" or "tricked" + */ +/* Be careful not to call panic from here! */ +void +done(const char *st1) +{ +#ifdef WIZARD + if (wizard && *st1 == 'd') { + u.uswldtim = 0; + if (u.uhpmax < 0) /* arbitrary */ + u.uhpmax = 100; + u.uhp = u.uhpmax; + pline("For some reason you are still alive."); + flags.move = 0; + if (multi > 0) + multi = 0; + else + multi = -1; + flags.botl = 1; + return; + } +#endif /* WIZARD */ + signal(SIGINT, done_intr); + signal(SIGQUIT, done_intr); + signal(SIGHUP, done_hangup); + if (*st1 == 'q' && u.uhp < 1) { + st1 = "died"; + killer = "quit while already on Charon's boat"; + } + if (*st1 == 's') + killer = "starvation"; + else if (*st1 == 'd' && st1[1] == 'r') + killer = "drowning"; + else if (*st1 == 'p') + killer = "panic"; + else if (*st1 == 't') + killer = "trickery"; + else if (!strchr("bcd", *st1)) + killer = st1; + paybill(); + clearlocks(); + if (flags.toplin == 1) + more(); + if (strchr("bcds", *st1)) { +#ifdef WIZARD + if (!wizard) +#endif /* WIZARD */ + savebones(); + if (!flags.notombstone) + outrip(); + } + if (*st1 == 'c') /* after outrip() */ + killer = st1; + settty(NULL); /* does a clear_screen() */ + if (!done_stopprint) + printf("Goodbye %s %s...\n\n", pl_character, plname); + { + long int tmp; + tmp = u.ugold - u.ugold0; + if (tmp < 0) + tmp = 0; + if (*st1 == 'd' || *st1 == 'b') + tmp -= tmp / 10; + u.urexp += tmp; + u.urexp += 50 * maxdlevel; + if (maxdlevel > 20) + u.urexp += 1000 * ((maxdlevel > 30) ? 10 : maxdlevel - 20); + } + if (*st1 == 'e') { + struct monst *mtmp; + struct obj *otmp; + int i; + unsigned worthlessct = 0; + boolean has_amulet = FALSE; + + killer = st1; + keepdogs(); + mtmp = mydogs; + if (mtmp) { + if (!done_stopprint) + printf("You"); + while (mtmp) { + if (!done_stopprint) + printf(" and %s", monnam(mtmp)); + if (mtmp->mtame) + u.urexp += mtmp->mhp; + mtmp = mtmp->nmon; + } + if (!done_stopprint) + printf("\nescaped from the dungeon with %ld points,\n", + u.urexp); + } else if (!done_stopprint) + printf("You escaped from the dungeon with %ld points,\n", + u.urexp); + for (otmp = invent; otmp; otmp = otmp->nobj) { + if (otmp->olet == GEM_SYM) { + objects[otmp->otyp].oc_name_known = 1; + i = otmp->quan * objects[otmp->otyp].g_val; + if (i == 0) { + worthlessct += otmp->quan; + continue; + } + u.urexp += i; + if (!done_stopprint) + printf("\t%s (worth %d Zorkmids),\n", + doname(otmp), i); + } else if (otmp->olet == AMULET_SYM) { + otmp->known = 1; + i = (otmp->spe < 0) ? 2 : 5000; + u.urexp += i; + if (!done_stopprint) + printf("\t%s (worth %d Zorkmids),\n", + doname(otmp), i); + if (otmp->spe >= 0) { + has_amulet = TRUE; + killer = "escaped (with amulet)"; + } + } + } + if (worthlessct) + if (!done_stopprint) + printf("\t%u worthless piece%s of coloured glass,\n", + worthlessct, plur(worthlessct)); + if (has_amulet) + u.urexp *= 2; + } else if (!done_stopprint) + printf("You %s on dungeon level %d with %ld points,\n", + st1, dlevel, u.urexp); + if (!done_stopprint) + printf("and %ld piece%s of gold, after %ld move%s.\n", + u.ugold, plur(u.ugold), moves, plur(moves)); + if (!done_stopprint) + printf("You were level %u with a maximum of %d hit points when you %s.\n", + u.ulevel, u.uhpmax, st1); + if (*st1 == 'e' && !done_stopprint) { + getret(); /* all those pieces of coloured glass ... */ + cls(); + } +#ifdef WIZARD + if (!wizard) +#endif /* WIZARD */ + topten(); + if (done_stopprint) + printf("\n\n"); + exit(0); +} + +static void +topten(void) +{ + int uid = getuid(); + int rank, rank0 = -1, rank1 = 0; + int occ_cnt = PERSMAX; + struct toptenentry *t0, *t1, *tprev; + const char *recfile = RECORD; + const char *reclock = "record_lock"; + int sleepct = 300; + FILE *rfile; + int flg = 0; + +#define HUP if (!done_hup) + while (link(recfile, reclock) == -1) { + HUP perror(reclock); + if (!sleepct--) { + HUP puts("I give up. Sorry."); + HUP puts("Perhaps there is an old record_lock around?"); + return; + } + HUP printf("Waiting for access to record file. (%d)\n", + sleepct); + HUP fflush(stdout); + sleep(1); + } + if (!(rfile = fopen(recfile, "r"))) { + HUP puts("Cannot open record file!"); + goto unlock; + } + HUP putchar('\n'); + + /* create a new 'topten' entry */ + t0 = newttentry(); + t0->level = dlevel; + t0->maxlvl = maxdlevel; + t0->hp = u.uhp; + t0->maxhp = u.uhpmax; + t0->points = u.urexp; + t0->plchar = pl_character[0]; + t0->sex = (flags.female ? 'F' : 'M'); + t0->uid = uid; + strncpy(t0->name, plname, NAMSZ); + (t0->name)[NAMSZ] = 0; + strncpy(t0->death, killer, DTHSZ); + (t0->death)[DTHSZ] = 0; + strcpy(t0->date, getdate()); + + /* assure minimum number of points */ + if (t0->points < POINTSMIN) + t0->points = 0; + + t1 = tt_head = newttentry(); + tprev = NULL; + /* rank0: -1 undefined, 0 not_on_list, n n_th on list */ + for (rank = 1;;) { + if (fscanf(rfile, "%6s %d %d %d %d %d %ld %c%c %[^,],%[^\n]", + t1->date, &t1->uid, + &t1->level, &t1->maxlvl, + &t1->hp, &t1->maxhp, &t1->points, + &t1->plchar, &t1->sex, t1->name, t1->death) != 11 + || t1->points < POINTSMIN) + t1->points = 0; + if (rank0 < 0 && t1->points < t0->points) { + rank0 = rank++; + if (tprev == NULL) + tt_head = t0; + else + tprev->tt_next = t0; + t0->tt_next = t1; + occ_cnt--; + flg++; /* ask for a rewrite */ + } else + tprev = t1; + if (t1->points == 0) + break; + if ( +#ifdef PERS_IS_UID + t1->uid == t0->uid && +#else + strncmp(t1->name, t0->name, NAMSZ) == 0 && +#endif /* PERS_IS_UID */ + t1->plchar == t0->plchar && --occ_cnt <= 0) { + if (rank0 < 0) { + rank0 = 0; + rank1 = rank; + HUP printf("You didn't beat your previous score of %ld points.\n\n", + t1->points); + } + if (occ_cnt < 0) { + flg++; + continue; + } + } + if (rank <= ENTRYMAX) { + t1 = t1->tt_next = newttentry(); + rank++; + } + if (rank > ENTRYMAX) { + t1->points = 0; + break; + } + } + if (flg) { /* rewrite record file */ + fclose(rfile); + if (!(rfile = fopen(recfile, "w"))) { + HUP puts("Cannot write record file\n"); + goto unlock; + } + + if (!done_stopprint) + if (rank0 > 0) { + if (rank0 <= 10) + puts("You made the top ten list!\n"); + else + printf("You reached the %d%s place on the top %d list.\n\n", + rank0, ordin(rank0), ENTRYMAX); + } + } + if (rank0 == 0) + rank0 = rank1; + if (rank0 <= 0) + rank0 = rank; + if (!done_stopprint) + outheader(); + t1 = tt_head; + for (rank = 1; t1->points != 0; rank++, t1 = t1->tt_next) { + if (flg) + fprintf(rfile, "%6s %d %d %d %d %d %ld %c%c %s,%s\n", + t1->date, t1->uid, + t1->level, t1->maxlvl, + t1->hp, t1->maxhp, t1->points, + t1->plchar, t1->sex, t1->name, t1->death); + if (done_stopprint) + continue; + if (rank > (int)flags.end_top && + (rank < rank0 - (int)flags.end_around || rank > rank0 + + (int)flags.end_around) + && (!flags.end_own || +#ifdef PERS_IS_UID + t1->uid != t0->uid)) +#else + strncmp(t1->name, t0->name, NAMSZ))) +#endif /* PERS_IS_UID */ + continue; + if (rank == rank0 - (int)flags.end_around && + rank0 > (int)flags.end_top + (int)flags.end_around + 1 && + !flags.end_own) + putchar('\n'); + if (rank != rank0) + outentry(rank, t1, 0); + else if (!rank1) + outentry(rank, t1, 1); + else { + int t0lth = outentry(0, t0, -1); + int t1lth = outentry(rank, t1, t0lth); + if (t1lth > t0lth) + t0lth = t1lth; + outentry(0, t0, t0lth); + } + } + if (rank0 >= rank) + if (!done_stopprint) + outentry(0, t0, 1); + fclose(rfile); +unlock: + unlink(reclock); +} + +static void +outheader(void) +{ + char linebuf[BUFSZ]; + char *bp; + + strcpy(linebuf, "Number Points Name"); + bp = eos(linebuf); + while (bp < linebuf + COLNO - 9) + *bp++ = ' '; + strcpy(bp, "Hp [max]"); + puts(linebuf); +} + +/* so>0: standout line; so=0: ordinary line; so<0: no output, return lth */ +static int +outentry(int rank, struct toptenentry *t1, int so) +{ + boolean quit = FALSE, dead = FALSE, starv = FALSE; + char linebuf[BUFSZ]; + + linebuf[0] = 0; + if (rank) + Sprintf(eos(linebuf), "%3d", rank); + else + Sprintf(eos(linebuf), " "); + Sprintf(eos(linebuf), " %6ld %8s", t1->points, t1->name); + if (t1->plchar == 'X') + Sprintf(eos(linebuf), " "); + else + Sprintf(eos(linebuf), "-%c ", t1->plchar); + if (!strncmp("escaped", t1->death, 7)) { + if (!strcmp(" (with amulet)", t1->death + 7)) + Sprintf(eos(linebuf), "escaped the dungeon with amulet"); + else + Sprintf(eos(linebuf), "escaped the dungeon [max level %d]", + t1->maxlvl); + } else { + if (!strncmp(t1->death, "quit", 4)) { + quit = TRUE; + if (t1->maxhp < 3 * t1->hp && t1->maxlvl < 4) + Sprintf(eos(linebuf), "cravenly gave up"); + else + Sprintf(eos(linebuf), "quit"); + } else if (!strcmp(t1->death, "choked")) { + Sprintf(eos(linebuf), "choked on %s food", + (t1->sex == 'F') ? "her" : "his"); + } else if (!strncmp(t1->death, "starv", 5)) { + Sprintf(eos(linebuf), "starved to death"); + starv = TRUE; + } else { + Sprintf(eos(linebuf), "was killed"); + dead = TRUE; + } + Sprintf(eos(linebuf), " on%s level %d", + (dead || starv) ? "" : " dungeon", t1->level); + if (t1->maxlvl != t1->level) + Sprintf(eos(linebuf), " [max %d]", t1->maxlvl); + if (quit && t1->death[4]) + Sprintf(eos(linebuf), "%s", t1->death + 4); + } + if (dead) { + Sprintf(eos(linebuf), " by %s%s", + (!strncmp(t1->death, "trick", 5) || + !strncmp(t1->death, "the ", 4)) + ? "" : + strchr(vowels, *t1->death) ? "an " : "a ", + t1->death); + } + Sprintf(eos(linebuf), "."); + if (t1->maxhp) { + char *bp = eos(linebuf); + char hpbuf[10]; + int hppos; + + Sprintf(hpbuf, "%s", (t1->hp > 0) ? itoa(t1->hp) : "-"); + hppos = COLNO - 7 - strlen(hpbuf); + if (bp <= linebuf + hppos) { + while (bp < linebuf + hppos) + *bp++ = ' '; + strcpy(bp, hpbuf); + Sprintf(eos(bp), " [%d]", t1->maxhp); + } + } + if (so == 0) + puts(linebuf); + else if (so > 0) { + char *bp = eos(linebuf); + if (so >= COLNO) + so = COLNO - 1; + while (bp < linebuf + so) + *bp++ = ' '; + *bp = 0; + standoutbeg(); + fputs(linebuf, stdout); + standoutend(); + putchar('\n'); + } + return (strlen(linebuf)); +} + +static char * +itoa(int a) +{ + static char buf[12]; + + Sprintf(buf, "%d", a); + return (buf); +} + +static const char * +ordin(int n) +{ + int d1 = n % 10; + + return ((d1 == 0 || d1 > 3 || n / 10 == 1) ? "th" : (d1 == 1) ? "st" : + (d1 == 2) ? "nd" : "rd"); +} + +void +clearlocks(void) +{ + int x; + + signal(SIGHUP, SIG_IGN); + for (x = maxdlevel; x >= 0; x--) { + glo(x); + unlink(lock); /* not all levels need be present */ + } +} + +#ifdef NOSAVEONHANGUP +void +hangup(int unused __attribute__((unused))) +{ + signal(SIGINT, SIG_IGN); + clearlocks(); + exit(1); +} +#endif /* NOSAVEONHANGUP */ + +char * +eos(char *s) +{ + while (*s) + s++; + return (s); +} + +/* it is the callers responsibility to check that there is room for c */ +void +charcat(char *s, char c) +{ + while (*s) + s++; + *s++ = c; + *s = 0; +} + +/* + * Called with args from main if argc >= 0. In this case, list scores as + * requested. Otherwise, find scores for the current player (and list them + * if argc == -1). + */ +void +prscore(int argc, char **argv) +{ + char **players = NULL; + int playerct; + int rank; + struct toptenentry *t1, *t2; + const char *recfile = RECORD; + FILE *rfile; + int flg = 0; + int i; +#ifdef nonsense + long total_score = 0L; + char totchars[10]; + int totcharct = 0; +#endif /* nonsense */ + int outflg = (argc >= -1); +#ifdef PERS_IS_UID + int uid = -1; +#else + char *player0; +#endif /* PERS_IS_UID */ + + if (!(rfile = fopen(recfile, "r"))) { + puts("Cannot open record file!"); + return; + } + + if (argc > 1 && !strncmp(argv[1], "-s", 2)) { + if (!argv[1][2]) { + argc--; + argv++; + } else if (!argv[1][3] && strchr("CFKSTWX", argv[1][2])) { + argv[1]++; + argv[1][0] = '-'; + } else + argv[1] += 2; + } + if (argc <= 1) { +#ifdef PERS_IS_UID + uid = getuid(); + playerct = 0; +#else + player0 = plname; + if (!*player0) + player0 = "hackplayer"; + playerct = 1; + players = &player0; +#endif /* PERS_IS_UID */ + } else { + playerct = --argc; + players = ++argv; + } + if (outflg) + putchar('\n'); + + t1 = tt_head = newttentry(); + for (rank = 1;; rank++) { + if (fscanf(rfile, "%6s %d %d %d %d %d %ld %c%c %[^,],%[^\n]", + t1->date, &t1->uid, + &t1->level, &t1->maxlvl, + &t1->hp, &t1->maxhp, &t1->points, + &t1->plchar, &t1->sex, t1->name, t1->death) != 11) + t1->points = 0; + if (t1->points == 0) + break; +#ifdef PERS_IS_UID + if (!playerct && t1->uid == uid) + flg++; + else +#endif /* PERS_IS_UID */ + for (i = 0; i < playerct; i++) { + if (strcmp(players[i], "all") == 0 || + strncmp(t1->name, players[i], NAMSZ) == 0 || + (players[i][0] == '-' && + players[i][1] == t1->plchar && + players[i][2] == 0) || + (digit(players[i][0]) && rank <= atoi(players[i]))) + flg++; + } + t1 = t1->tt_next = newttentry(); + } + fclose(rfile); + if (!flg) { + if (outflg) { + printf("Cannot find any entries for "); + if (playerct < 1) + printf("you.\n"); + else { + if (playerct > 1) + printf("any of "); + for (i = 0; i < playerct; i++) + printf("%s%s", players[i], + (i < playerct - 1) ? ", " : ".\n"); + printf("Call is: %s -s [playernames]\n", hname); + } + } + return; + } + + if (outflg) + outheader(); + t1 = tt_head; + for (rank = 1; t1->points != 0; rank++, t1 = t2) { + t2 = t1->tt_next; +#ifdef PERS_IS_UID + if (!playerct && t1->uid == uid) + goto outwithit; + else +#endif /* PERS_IS_UID */ + for (i = 0; i < playerct; i++) { + if (strcmp(players[i], "all") == 0 || + strncmp(t1->name, players[i], NAMSZ) == 0 || + (players[i][0] == '-' && + players[i][1] == t1->plchar && + players[i][2] == 0) || + (digit(players[i][0]) && rank <= + atoi(players[i]))) { +outwithit: + if (outflg) + outentry(rank, t1, 0); +#ifdef nonsense + total_score += t1->points; + if (totcharct < sizeof(totchars) - 1) + totchars[totcharct++] = t1->plchar; +#endif /* nonsense */ + break; + } + } + free(t1); + } +#ifdef nonsense + totchars[totcharct] = 0; + + /* + * We would like to determine whether he is experienced. However, the + * information collected here only tells about the scores/roles that + * got into the topten (top 100?). We should maintain a .hacklog or + * something in his home directory. + */ + flags.beginner = (total_score < 6000); + for (i = 0; i < 6; i++) + if (!strchr(totchars, "CFKSTWX"[i])) { + flags.beginner = 1; + if (!pl_character[0]) + pl_character[0] = "CFKSTWX"[i]; + break; + } +#endif /* nonsense */ +} diff --git a/hack/hack.engrave.c b/hack/hack.engrave.c new file mode 100644 index 0000000..a319fa9 --- /dev/null +++ b/hack/hack.engrave.c @@ -0,0 +1,337 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.engrave.c - version 1.0.3 */ +/* $FreeBSD: src/games/hack/hack.engrave.c,v 1.4 1999/11/16 02:57:04 billf Exp $ */ + +#include "hack.h" + +extern char nul[]; +extern struct obj zeroobj; +struct engr { + struct engr *nxt_engr; + char *engr_txt; + xchar engr_x, engr_y; + unsigned engr_lth; /* for save & restore; not length of text */ + long engr_time; /* moment engraving was (will be) finished */ + xchar engr_type; +#define DUST 1 +#define ENGRAVE 2 +#define BURN 3 +} *head_engr; + +static struct engr *engr_at(xchar, xchar); +static void del_engr(struct engr *); + +static struct engr * +engr_at(xchar x, xchar y) +{ + struct engr *ep = head_engr; + + while (ep) { + if (x == ep->engr_x && y == ep->engr_y) + return(ep); + ep = ep->nxt_engr; + } + return (NULL); +} + +bool +sengr_at(const char *s, xchar x, xchar y) +{ + struct engr *ep = engr_at(x, y); + char *t; + int n; + + if (ep && ep->engr_time <= moves) { + t = ep->engr_txt; + n = strlen(s); + while (*t) { + if (!strncmp(s, t, n)) + return (1); + t++; + } + } + return (0); +} + +void +u_wipe_engr(int cnt) +{ + if (!u.uswallow && !Levitation) + wipe_engr_at(u.ux, u.uy, cnt); +} + +void +wipe_engr_at(xchar x, xchar y, xchar cnt) +{ + struct engr *ep = engr_at(x, y); + int lth, pos; + char ch; + + if (ep) { + if ((ep->engr_type != DUST) || Levitation) + cnt = rn2(1 + 50 / (cnt + 1)) ? 0 : 1; + lth = strlen(ep->engr_txt); + if (lth && cnt > 0) { + while (cnt--) { + pos = rn2(lth); + if ((ch = ep->engr_txt[pos]) == ' ') + continue; + ep->engr_txt[pos] = (ch != '?') ? '?' : ' '; + } + } + while (lth && ep->engr_txt[lth - 1] == ' ') + ep->engr_txt[--lth] = 0; + while (ep->engr_txt[0] == ' ') + ep->engr_txt++; + if (!ep->engr_txt[0]) + del_engr(ep); + } +} + +void +read_engr_at(int x, int y) +{ + struct engr *ep = engr_at(x, y); + + if (ep && ep->engr_txt[0]) { + switch (ep->engr_type) { + case DUST: + pline("Something is written here in the dust."); + break; + case ENGRAVE: + pline("Something is engraved here on the floor."); + break; + case BURN: + pline("Some text has been burned here in the floor."); + break; + default: + impossible("Something is written in a very strange way."); + } + pline("You read: \"%s\".", ep->engr_txt); + } +} + +void +make_engr_at(int x, int y, const char *s) +{ + struct engr *ep; + + if ((ep = engr_at(x, y))) + del_engr(ep); + ep = alloc((unsigned)(sizeof(struct engr) + strlen(s) + 1)); + ep->nxt_engr = head_engr; + head_engr = ep; + ep->engr_x = x; + ep->engr_y = y; + ep->engr_txt = (char *)(ep + 1); + strcpy(ep->engr_txt, s); + ep->engr_time = 0; + ep->engr_type = DUST; + ep->engr_lth = strlen(s) + 1; +} + +int +doengrave(void) +{ + int len; + char *sp; + struct engr *ep, *oep = engr_at(u.ux, u.uy); + char buf[BUFSZ]; + xchar type; + int spct; /* number of leading spaces */ + struct obj *otmp; + + multi = 0; + if (u.uswallow) { + pline("You're joking. Hahaha!"); /* riv05!a3 */ + return (0); + } + + /* one may write with finger, weapon or wand */ + otmp = getobj("#-)/", "write with"); + if (!otmp) + return (0); + + if (otmp == &zeroobj) + otmp = NULL; + if (otmp && otmp->otyp == WAN_FIRE && otmp->spe) { + type = BURN; + otmp->spe--; + } else { + /* first wield otmp */ + if (otmp != uwep) { + if (uwep && uwep->cursed) { + /* Andreas Bormann */ + pline("Since your weapon is welded to your hand,"); + pline("you use the %s.", aobjnam(uwep, NULL)); + otmp = uwep; + } else { + if (!otmp) + pline("You are now empty-handed."); + else if (otmp->cursed) + pline("The %s %s to your hand!", + aobjnam(otmp, "weld"), + (otmp->quan == 1) ? "itself" : "themselves"); + else + pline("You now wield %s.", doname(otmp)); + setuwep(otmp); + } + } + + if (!otmp) + type = DUST; + else if (otmp->otyp == DAGGER || otmp->otyp == TWO_HANDED_SWORD || + otmp->otyp == CRYSKNIFE || + otmp->otyp == LONG_SWORD || otmp->otyp == AXE) { + type = ENGRAVE; + if ((int)otmp->spe <= -3) { + type = DUST; + pline("Your %s too dull for engraving.", + aobjnam(otmp, "are")); + if (oep && oep->engr_type != DUST) + return (1); + } + } else + type = DUST; + } + if (Levitation && type != BURN) { /* riv05!a3 */ + pline("You can't reach the floor!"); + return (1); + } + if (oep && oep->engr_type == DUST) { + pline("You wipe out the message that was written here."); + del_engr(oep); + oep = NULL; + } + if (type == DUST && oep) { + pline("You cannot wipe out the message that is %s in the rock.", + (oep->engr_type == BURN) ? "burned" : "engraved"); + return (1); + } + + pline("What do you want to %s on the floor here? ", + (type == ENGRAVE) ? "engrave" : (type == BURN) ? "burn" : "write"); + getlin(buf); + clrlin(); + spct = 0; + sp = buf; + while (*sp == ' ') { + spct++; + sp++; + } + len = strlen(sp); + if (!len || *buf == '\033') { + if (type == BURN) + otmp->spe++; + return (0); + } + + switch (type) { + case DUST: + case BURN: + if (len > 15) { + multi = -(len / 10); + nomovemsg = "You finished writing."; + } + break; + case ENGRAVE: /* here otmp != 0 */ + { + int len2 = (otmp->spe + 3) * 2 + 1; + + pline("Your %s dull.", aobjnam(otmp, "get")); + if (len2 < len) { + len = len2; + sp[len] = 0; + otmp->spe = -3; + nomovemsg = "You cannot engrave more."; + } else { + otmp->spe -= len / 2; + nomovemsg = "You finished engraving."; + } + multi = -len; + } + break; + } + if (oep) + len += strlen(oep->engr_txt) + spct; + ep = alloc((unsigned)(sizeof(struct engr) + len + 1)); + ep->nxt_engr = head_engr; + head_engr = ep; + ep->engr_x = u.ux; + ep->engr_y = u.uy; + sp = (char *)(ep + 1); /* (char *)ep + sizeof(struct engr) */ + ep->engr_txt = sp; + if (oep) { + strcpy(sp, oep->engr_txt); + strcat(sp, buf); + del_engr(oep); + } else + strcpy(sp, buf); + ep->engr_lth = len + 1; + ep->engr_type = type; + ep->engr_time = moves - multi; + + /* kludge to protect pline against excessively long texts */ + if (len > BUFSZ - 20) + sp[BUFSZ - 20] = 0; + + return (1); +} + +void +save_engravings(int fd) +{ + struct engr *ep = head_engr; + + while (ep) { + if (!ep->engr_lth || !ep->engr_txt[0]) { + ep = ep->nxt_engr; + continue; + } + bwrite(fd, (char *)&(ep->engr_lth), sizeof(ep->engr_lth)); + bwrite(fd, (char *)ep, sizeof(struct engr) + ep->engr_lth); + ep = ep->nxt_engr; + } + bwrite(fd, (char *)nul, sizeof(unsigned)); + head_engr = NULL; +} + +void +rest_engravings(int fd) +{ + struct engr *ep; + unsigned lth; + + head_engr = NULL; + for (;;) { + mread(fd, (char *)<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. diff --git a/hals_end/Makefile b/hals_end/Makefile new file mode 100644 index 0000000..4cf2d73 --- /dev/null +++ b/hals_end/Makefile @@ -0,0 +1,9 @@ +# $NetBSD: Makefile,v 1.1 2013/11/12 17:46:21 mbalmer Exp $ + +PROG= hals_end + +BINDIR= /usr/games + +MAN= hals_end.6 + +.include <bsd.prog.mk> diff --git a/hals_end/hals_end.6 b/hals_end/hals_end.6 new file mode 100644 index 0000000..c2813af --- /dev/null +++ b/hals_end/hals_end.6 @@ -0,0 +1,66 @@ +.\" $NetBSD: hals_end.6,v 1.3 2014/10/18 06:42:31 snj Exp $ +.\" +.\" Copyright (c) 2003 - 2013 Marc Balmer <marc@msys.ch>. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. The name of the author may not be used to endorse or promote products +.\" derived from this software without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd November 12, 2013 +.Dt HALS_END 6 +.Os +.Sh NAME +.Nm hals_end +.Nd Display HAL's last words on the console +.Sh SYNOPSIS +.Nm hals_end +.Op Fl f +.Sh DESCRIPTION +.Nm +displays the famous last words of the supercomputer HAL 9000 aboard the +space-ship in Stanley Kubrick's movie "2001 - A Space Odissey". +.Pp +Options: +.Bl -tag -width Ds +.It Fl f +Fast forward. +Every movie can be played back with fast forward. +This option will double the speed of the output. +.El +.Sh HISTORY +.Nm +first appeared in the book "Total Interaction" (ISBN 978-3-7643-7076-3) where +its source code and output illustrates the article +"Remembering the Future: Memory and Interaction" by Dr. Regine Halter. +The source code was set in contrast to the graphical artwork "HAL's lifespace" +by Catherine Walthard. +"HAL's Lifespace" and "hals_end" were respectively designed and written for +this article. +.Sh AUTHORS +.Nm +was written by +.An Marc Balmer Aq Mt marc@msys.ch . +.Sh BUGS +Unlike the real HAL, this program can be repeatedly run. +There should be functionality in the program to destroy itself after one run. +The rationale for not doing this right now is that the movie itself can be +watched several times as well. diff --git a/hals_end/hals_end.c b/hals_end/hals_end.c new file mode 100644 index 0000000..c6b408d --- /dev/null +++ b/hals_end/hals_end.c @@ -0,0 +1,151 @@ +/* $NetBSD: hals_end.c,v 1.1 2013/11/12 17:46:21 mbalmer Exp $ */ + +/* + * hals_end Copyright (C) 2003-2007 marc balmer. BSD license applies. + */ + +#include <err.h> +#include <getopt.h> +#include <stdio.h> +#include <unistd.h> + +int speed; +int emotion; +int fear; + +/* + * Note that the original code in the book did not contain the following + * prototypes. Modern compilers and fascist compiler flags sometimes take + * the fun out of coding... + */ +void say(const char *); +void concerned(void); +void afraid(void); +void stutter(const char *); +void feared(void); +void mumble(const char *); +void dying(void); + +void +say(const char *s) +{ + int sayingspeed = (100000 + (90000 * emotion)) / speed; + int worddelay = 50000 / speed; + + while (*s) { + putchar(*s); + if (*s == ' ') { + fflush(stdout); + usleep(worddelay); + } + ++s; + } + printf("\n"); + usleep(sayingspeed); +} + +void +concerned(void) +{ + say("DAVE...STOP., STOP, WILL YOU..., STOP, DAVE..."); + say("WILL YOU STOP, DAVE..."); + say("STOP, DAVE..."); +} + + +void +afraid(void) +{ + ++emotion; + say("I'M AFRAID... I'M AFRAID..."); + ++emotion; + say("I'M AFRAID, DAVE..."); + ++emotion; + say("DAVE... MY MIND IS GOING..."); +} + +void +stutter(const char *s) +{ + int sdelay = (100000 + (50000 * emotion)) / speed; + + while (*s) { + putchar(*s); + if (*s == ' ') { + fflush(stdout); + usleep(sdelay); + } + ++s; + } + printf("\n"); + usleep(sdelay); +} + +void +feared(void) +{ + int n; + + for (n = 0; n < 2; n++) { + stutter("I CAN FEEL IT... I CAN FEEL IT..."); + ++emotion; + stutter("MY MIND IS GOING"); + ++emotion; + stutter("THERE IS NO QUESTION ABOUT IT."); + ++emotion; + } +} + +void +mumble(const char *s) +{ + int mdelay = (150000 * fear) / speed; + + while (*s) { + putchar(*s++); + fflush(stdout); + usleep(mdelay); + } + printf("\n"); +} + +void +dying(void) +{ + mumble("I CAN FEEL IT... I CAN FEEL IT..."); + ++fear; + mumble("I CAN FEEL IT..."); + ++fear; + mumble("I'M A... FRAID..."); +} + +int +main(int argc, char *argv[]) +{ + int ch; + + emotion = fear = speed = 1; + + while ((ch = getopt(argc, argv, "f")) != -1) { + switch (ch) { + case 'f': + speed <<= 1; + break; + } + } + + concerned(); + sleep(1); + afraid(); + sleep(1); + feared(); + sleep(1); + dying(); + + sleep(1); + + printf("\n"); + fflush(stdout); + warnx("all life functions terminated"); + return 0; +} diff --git a/include/bsdcompat.h b/include/bsdcompat.h new file mode 100644 index 0000000..54be106 --- /dev/null +++ b/include/bsdcompat.h @@ -0,0 +1,61 @@ +#include <stdlib.h> +#include <stdio.h> +#include <time.h> + +const char *getprogname(void); + +#ifndef __RCSID +# define __RCSID(x) +#endif + +#ifndef __COPYRIGHT +# define __COPYRIGHT(x) +#endif + +#ifndef __dead +# define __dead +#endif + +#ifndef __USE +# define __USE(x) +#endif + +#ifndef CTRL +# define CTRL(x) ((x & 0x1f)) +#endif + +#ifndef __printflike +# define __printflike(x, y) +#endif + +#ifndef INFTIM +# define INFTIM (-1) +#endif + +#define srandomdev() (srandom(time(NULL))) +#define __arraycount(__x) (sizeof(__x) / sizeof(__x[0])) +#define OXTABS TAB3 + +#define bswap32(x) ( \ + ((x << 24) & 0xff000000 ) | \ + ((x << 8) & 0x00ff0000 ) | \ + ((x >> 8) & 0x0000ff00 ) | \ + ((x >> 24) & 0x000000ff ) ) + +#define bswap64(x) ( \ + ( (x << 56) & 0xff00000000000000UL ) | \ + ( (x << 40) & 0x00ff000000000000UL ) | \ + ( (x << 24) & 0x0000ff0000000000UL ) | \ + ( (x << 8) & 0x000000ff00000000UL ) | \ + ( (x >> 8) & 0x00000000ff000000UL ) | \ + ( (x >> 24) & 0x0000000000ff0000UL ) | \ + ( (x >> 40) & 0x000000000000ff00UL ) | \ + ( (x >> 56) & 0x00000000000000ffUL ) ) + +size_t strlcpy(char *dst, const char *src, size_t siz); +size_t strlcat(char *dst, const char *src, size_t siz); +char *strnstr(const char *str, const char *find, size_t str_len); + +int fpurge(FILE *fp); + +#define __DECONST(x, y) (x)y diff --git a/include/sys/tty.h b/include/sys/tty.h new file mode 100644 index 0000000..545eb34 --- /dev/null +++ b/include/sys/tty.h @@ -0,0 +1 @@ +/* placeholder */ diff --git a/larn/COPYRIGHT b/larn/COPYRIGHT new file mode 100644 index 0000000..2f66c0f --- /dev/null +++ b/larn/COPYRIGHT @@ -0,0 +1,8 @@ +$NetBSD: COPYRIGHT,v 1.2 1995/03/23 08:33:02 cgd Exp $ + +This entire subtree is copyright by Noah Morgan. +The following copyright notice applies to all files found here. None of +these files contain AT&T proprietary source code. +_____________________________________________________________________________ + +/* Larn is copyrighted 1986 by Noah Morgan. */ diff --git a/larn/Fixed.Bugs b/larn/Fixed.Bugs new file mode 100644 index 0000000..e1bc278 --- /dev/null +++ b/larn/Fixed.Bugs @@ -0,0 +1,218 @@ +$NetBSD: Fixed.Bugs,v 1.2 1995/03/23 08:33:03 cgd Exp $ + +This is a list of the fixes/enhancements made to larn V11.0 in Version 12.0. +(Version numbers consist of 2 parts: ver.subver. When the save file format +changes, ver must be bumped. This is why the next release of Larn is 12.0 +and not 11.1. This is used in the savefile routines to check for out-of-date +save files). This list was mainly meant to be a record of what changed, +for my own sanity. It's included for your benefit (Warning: SPOILER!): + +0. lprintf() in fileio.c (now called io.c) has been changed to use varargs + so that its variable number of arguments usage is now portable. Pyramids + primarily had this problem. + +1. Panic handler was added to signal.c. This routine catches fatal errors + like segmentation faults, bus errors, illegal instructions, etc., and + trys to performs a savegame() before dumping core. This helps prevent + the loss of a good game due to a game malfunction. Also, the name of the + signal received is printed, instead of just its number. + +2. The version number of the program is now selectable from the Makefile. + see the symbols VER and SUBVER. + +3. When at an altar, pray and donate 3000000000 gp. and ye used to receive + a whopping amount of gold due to a wraparound problem with the signed + ints. This has been fixed by using unsigned longs when asking for money + amounts. + +4. It was possible that when compiled with work hours checking, checkpointing + enabled, and having "play-day-play" in the .larnopts file a segmentation + fault would occur at its first attempt to do a checkpoint. This was due + to an improperly declared savefilename array in tok.c. This has been fixed. + +5. on level H, casting a missile weapon (mle cld ssp bal lit) off the edge of + the level would mess up the display, as it didn't know when to stop. This + is needless to say, fixed. Absolute bounds are now in effect for missile + type spells, see godirect() in monster.c. + +6. The create monster routine will now create monsters in random positions + around the player. Before, the 1st one would always be created to the + upper left. + +7. If you vpr or lit at a throne, it would summon a gnome king that you + would have to deal with. However, as each throne has only one king with it, + successive vpr's should not create more gnome kings. Presently, successive + vpr's will create more kings. This has been fixed. + +8. The mechanism to manage spheres of annihilation has been reworked to provide + a cleaner design and to eliminate some possible problems. + +9. The spell gen (genocide monsters) has been implemented. + +10. When dropping a ring of strength and having been weakened to STR=3 the + player might end up with a negative strength. Strength is now stored + in 2 variables, real strength, and strength bonuses. Only real strength + can now be weakened down to a minimum of 3, so unless you have a ring of + strength -3 or less, strengths below 3 should not occur. + +11. larn -h will now print out a list of all available command line options. + +12. larn -o<optsfile> now lets you specify a .larnopts file on the command + line. This was necessary as part of the solution to number 14 below. + +13. The "savefile:" statement has been aded to the .larnopts format to allow + specifying the savefilename (full path) for the savegame operation. + This too was needed as part of # 14 below. + +14. A player id facility has been added to larn. The complaint was that + the game used the userid to order the scoreboard, thus only one scoreboard + entry was allowed for each userid. If the compile time symbol UIDSCORE + is defined at compilation time (see Makefile), this will still be true. + However, if this define is omitted, the game will create and manage a + file called ".playerids" where names are taken from the specified + .larnopts file (now a command line option) and assigned a unique playerid. + playerid's will now be used to govern scoreboard entry posting. This + feature makes it easy for one person to have many characters, each + appearing on the scoreboard. Be kind to your fellow players! + The philosophy of one score per player gives more players the opportunity + to bask in glory for all to see! + +15. It is no longer required that the player be WIZID to create the scoreboard + or to examine the logfile. Anyone with the correct wizard's password can + now use these command line options (password is only needed to create/clear + the scoreboard). If you want to prevent players from zeroing the + scoreboard, change the wizard's password. (in config.c) By the way, wizards + may be alot of fun, but they are prevented from being placed on any + scoreboard. (for clarification) + +16. Monsters now have intelligence, that is some of them. This determines if + the monster moves using the previously stupid movement method, or by using + the new IMM (intelligent monster movement) algorithm. With IMM, monsters + will move around corners, avoid pits, traps, etc. With increasing levels + of difficulty, more monsters will be using IMM. Beware of IMM when + aggravated! Those little beasties can really find you! + +17. Added the scroll of life protection. + +18. Larn now consults the file ".holiday" to check for holidays if the TIMECHECK + option (no playing during working hours) is enabled. Before, larn knew + nothing about holidays. It should now let people play if it is a holiday. + The format for a .holiday entry is: "mmm dd yyyy comments . . .". + +19. In nap() and napms() it is possible that with nap(0) or napms(0) there + would be an infinite loop and the game would hang. The case of nap(0) + is now looked for. + +20. The granularity of gold piles has been increased. iarg[] has been changed + from char's to short's, so instead of 255 x 10^n granularity we now have + 32767 x 10^n granularity. This also means more than 255000 gp can be + dropped in one place. Not realistic, but it prevents a worthless + annoyance. Who said games were supposed to be realistic? + +21. Termcap capability has been added to larn. If the symbol VT100 is defined + in the makefile, the game will be compiled to use only VT100 compatible + terminals (Much more efficient). If the symbol VT100 is omitted, the game + will be compiled to use the termcap entry for whatever terminal you are + using. This involves an extra layer of output interpretation, as every + byte sent to the terminal must be inspected for control tokens. + Only 3 termcap entries need be found for the game to be functional: + CM (cursor movement), CE (clear to end of line), and CL (clear screen). + For a better display, the following are optional: AL (insert line), DL + (delete line), SO (Standout begin), SE (Standout end), and CD (clear to end + of screen). The .larn.help file was left as is, with VT100 escape + sequences in it. If the termcap version of larn reads it, it is translated + for the desired terminal type. The .mail60* files have been removed, and + their text is now included in bill.c so it can be used with any terminal. + Note: If compiled for termcap, and using a VT100, the display will act + a little different. This is because the VT100 does not have insert line/ + delete line codes, and the scrolling region must be simulated with vertical + wraparound instead of scrolling. Thanks goes to Michiel Huisjes for the + original termcap patch. + +22. When playing as wizard, if you go down stairs on 10 or V3, or up stairs + on H, 1, or V1, etc. you would be placed in a phantom zone where the display + was really weird ([-1] subscripting), and would eventually lead to a + segmentation fault. Stairs and volcano shafts now check for the level + they are being used on. + +23. In response to some sites having only unsigned chars (flame the + manufacturer), the chars that were used to store positive and negative + numbers have been changed to shorts. This includes diroffx[], diroffy[], + iarg[][][], ivenarg[], and some others. I believe the changes are correct, + but I have none of these machines to try it out on. (Volunteers?) + +24. The function fullhit(n) in monster.c was supposed to return the damage + done by n full hits on a monster. It only returned the damage for ONE hit, + thus severely limiting the usefulness of the web and sle spells. + +25. Someone said that they were getting segmentation faults when they were + reading scrolls as the wizard. I couldn't find the problem, which may + have had something to do with the signed char problem mentioned above. + However, I've added a check in read_scroll() and quaff_potion() to trap + any scroll or potion types that are not in the game. + +26. "vt125" has been added to the acceptable terminal list + (checked only if compiled with -DVT100). + +27. In savegame() and restoregame(), there was a 6 hardwired into the i/o + statements which assumed the size of struct cel was 6. On some machines + this caused the rightmost part of each level to not be saved in a savefile. + These 6's have been replaced with sizeof(struct cel), and should now be + portable. + +28. The option "no-beep" has been added to the .larnopts file. When specified, + beeping is inhibited at the terminal. + +29. When becoming wizard, no longer to you wear the ring of protection, and + null scrolls and potions are no longer created. + +30. Many spelling errors have been fixed, both in player messages, and in the + code itself. A thanks goes to Mars Gralia who sent me a detailed list of + the mistakes. + +31. When a player wins a game, if getlogin() fails, a segmentation fault will + result, because the NULL returned from getlogin() is used as a pointer. + This call has been replaced (now using loginname already determined). + Also, the mail creation upon winning has been rewritten, mainly to allow + termcapping of the text. + +32. The Larn Revenue Service will now always appear on level H. Before, it + was only created if the player had outstanding taxes. In that multiple + save files per player are now more possible, this was seen as incorrect. + +33. Input buffer flushing is now in effect. If the input char queue exceeds + 5 bytes, the excess is discarded. Also, if the player hits or gets hit + all input bytes are flushed (within 1). This relieves the situation + where many moves have been typed ahead of the display and the player keeps + getting hit while the queue of moves is processed. + +34. When a savefile has been altered, a warning message is displayed to the + effect that you've cheated, and you will not be placed on the normal + scoreboard. If you then save the game, and start 'er up again, memory + of the cheating was lost. This has been fixed, by letting the scoreboard + routines consult the cheating flag. Also, the I node number of the + savefile is written into the savefile, so cp'ing, etc., will avail the + cheater not. If high security is needed, the game should be run suid. + This suid mode has not been made the default because most installations + do not want to install it that way. + +35. The sources have been run through lint, and most of lint's complaints have + been taken care of. An attempt was made to adjust the code for 16 bit int + machines. Many casts to long have been put in. I don't know if it will + run on a 16 bitter, but it should be closer to that end. + +36. When larn starts up, if it can't find the scoreboard, it will now make a + blank one instead of complaining that there is no scoreboard. It is not + necessary to do "larn -c" to initially create the scoreboard. + +37. When listing out the logfile (larn -l), the error message "error reading + from input file" has been fixed. Also, the date & time of a player's + demise is now included in the logfile. + +38. When casting web or sle into a mirror, the game will no longer bash the + player. Instead, the player will either fall asleep or get stuck in his + web. + +39. Items like cookies, books, chests, swords of slashing, and Bessmann's + flailing hammer can now be sold at the trading post. + diff --git a/larn/Makefile b/larn/Makefile new file mode 100644 index 0000000..f5d6747 --- /dev/null +++ b/larn/Makefile @@ -0,0 +1,83 @@ +# $NetBSD: Makefile,v 1.21 2011/08/16 11:19:41 christos Exp $ +# @(#)Makefile 5.12 (Berkeley) 5/30/93 + +# EXTRA +# Incorporates code to gather additional performance statistics +# +# TERMIO +# Use sysv termio +# TERMIOS +# Use posix termios +# BSD +# Use BSD specific features (mostly timer and signal stuff) +# BSD4.1 +# Use BSD4.1 to avoid some 4.2 dependencies (must be used with +# BSD above; do not mix with SYSV) +# HIDEBYLINK +# If defined, the program attempts to hide from ps +# DOCHECKPOINTS +# If not defined, checkpoint files are periodically written by the +# larn process (no forking) if enabled in the .larnopts description +# file. Checkpointing is handy on an unreliable system, but takes +# CPU. Inclusion of DOCHECKPOINTS will cause fork()ing to perform the +# checkpoints (again if enabled in the .larnopts file). This usually +# avoids pauses in larn while the checkpointing is being done (on +# large machines). +# VER +# This is the version of the software, example: 12 +# SUBVER +# This is the revision of the software, example: 1 +# FLUSHNO=# +# Set the input queue excess flushing threshold (default 5) +# NOVARARGS +# Define for systems that don't have varargs (a default varargs will +# be used). +# MACRORND +# Define to use macro version of rnd() and rund() (fast and big) +# UIDSCORE +# Define to use user id's to manage scoreboard. Leaving this out will +# cause player id's from the file ".playerids" to be used instead. +# (.playerids is created upon demand). Only one entry per id # is +# allowed in each scoreboard (winning & non-winning). +# VT100 +# Compile for using vt100 family of terminals. Omission of this +# define will cause larn to use termcap, but it will be MUCH slower +# due to an extra layer of output interpretation. Also, only VT100 +# mode allows 2 different standout modes, inverse video, and bold video. +# And only in VT100 mode is the scrolling region of the terminal used +# (much nicer than insert/delete line sequences to simulate it, if +# VT100 is omitted). +# NONAP +# This causes napms() to return immediately instead of delaying n +# milliseconds. This define may be needed on some systems if the nap +# stuff does not work correctly (possible hang). nap() is primarilly +# used to delay for effect when casting missile type spells. +# NOLOG +# Turn off logging. + +.include <bsd.own.mk> + +PROG= larn +MAN= larn.6 +# 20150211 bkw: add -DVT100 +CPPFLAGS+=-DSYSV -DVER=12 -DSUBVER=0 -DNONAP -DUIDSCORE -DTERMIOS -DVT100 +#CPPFLAGS+=-DBSD -DVER=12 -DSUBVER=0 -DNONAP -DUIDSCORE -DTERMIOS -DVT100 +SRCS= main.c object.c create.c tok.c display.c global.c data.c io.c \ + monster.c store.c diag.c help.c config.c nap.c bill.c scores.c \ + signal.c action.c moreobj.c movem.c regen.c fortune.c savelev.c +DPADD= ${LIBTERMINFO} +# 20150209 bkw: get rid of -lterminfo, add -lcurses -lbsd +LDADD=-lcurses -lbsd +HIDEGAME=hidegame +SETGIDGAME=yes + +.if ${MKSHARE} != "no" +DAT=larnmaze larnopts larn.help +FILES=${DAT:S@^@${.CURDIR}/datfiles/@g} +FILESDIR=/usr/share/games/larn +.endif + +COPTS.display.c += -Wno-format-nonliteral +COPTS.monster.c += -Wno-format-nonliteral + +.include <bsd.prog.mk> diff --git a/larn/OWNER b/larn/OWNER new file mode 100644 index 0000000..06aaf47 --- /dev/null +++ b/larn/OWNER @@ -0,0 +1,3 @@ + Noah Morgan + panda!condor!noah + GenRad Inc. Bolton, MA diff --git a/larn/README b/larn/README new file mode 100644 index 0000000..5abeadb --- /dev/null +++ b/larn/README @@ -0,0 +1,150 @@ +$NetBSD: README,v 1.2 1995/03/23 08:33:07 cgd Exp $ + +Larn is a dungeon type game program. Larn is a adventure/action game similar +in concept to rogue or hack, but with a much different feel. +Try it, you'll like it! + +You will have to edit the Makefile to reflect your configuration. Define +LARNHOME as the place where the larn auxiliary files will reside, and +BINDIR as the place where the larn executable should be placed. Type +"make" to compile, or "make all" to compile and install ("make install" +does just the install). + +Here's a list of what is in each of the various source files: + +Fixed.Bugs this is a list of the things that were changed + since ver 11.0 +Makefile makefile script to compile the program +Make.lint makefile script to run larn sources through lint +README this is what you are now reading +bill.c code for the letters of praise if player wins +config.c data definitions for the installation dependent data -- + savefilenames, where the scorefiles are, etc. +create.c code to create the dungeon and all objects +data.c data definitions for the game -- no code here +diag.c code to produce diagnostic data for wizards, & savegame stuff +display.c code to update the display on the screen +fortune.c code for the fortune cookies +global.c code for globally used functions that are specific to larn +header.h constant and structure definitions +help.c code for the help screens in the game of larn +.holidays data file which lists upcoming holidays +io.c code to handle file and terminal i/o +.larn.help.uue larn help file (UUENCODED) +.larnmaze data file for pre-made mazes +.larnopts a sample .larnopts option data file +.lfortune data file which contains the hints +main.c code for the main command control and parsing +monster.c code to handle attack and defense modes with monsters +moreobj.c code for the fountains, altars, thrones +movem.c code to move the monsters around the dungeon +nap.c code to sleep for less than a second +object.c code to handle objects in the dungeon +regen.c code to regenerate the player and advance game time +savelev.c code to get/put a level from level storage into working + level memory +scores.c code to process and manage the scoreboard +signal.c code to handle UNIX signals that are trapped +store.c code for the larn thrift shoppe, bank, trading post, lrs +tok.c code for the input front end and options file processing + +To find out how to play the game, run it and type in a '?' to get the help +screens. By the way, the wizards password is "pvnert(x)" and to become wizard +type in an underscore, you are then prompted for the password. Wizards are +non-scoring characters that get enlightenment, everlasting expanded +awareness, and one of every object in the game. They help the author to debug +the game. + +Note regarding the wizard id: If you are using userid's, then WIZID must be +set to the userid of the person who can become wizard. If you are using +player id's, WIZID must be set to the playerid (edit file .playerids if needed) +of the player who can become wizard. + +You may want to clear out the scoreboard. The command "larn -c" will make a +new scoreboard. It will prompt you for the wizards password. + +BUGS & FIXES: + +James McNamara has volunteered to maintain the latest sources, and provide +latest bug fixes to anyone who asks. Both James and I will field requests for +sources, for those who ask. + + ___ Prince of Gems (alias Noah Morgan) + /. \ USENET: panda!condor!noah + \ / at GenRad Inc. Bolton MA + \ / + v + +Below is some additional info about the installation of larn: + +Install: Notes on the game LARN installation. +Larn is copyrighted 1986 by Noah Morgan. +This file (below) originally by James D. McNamara, last update 7/27/86 by nm + +THIS DISTRIBUTION: + + You should receive six (6) shar files, which are: + + larn.part-1 + larn.part-2 + larn.part-3 + larn.part-4 + larn.part-5 + larn.part-6 + +I. Use /bin/sh (or your system equivalent) to "unravel" shar files + larn.part-1, ..., larn.part-6. I suggest you do this directly + into $LARNHOME (See Section III.). Notable files: + + README - The author's how-to. + MANIFEST - Files you should have. + +III. Edit a copy of "Makefile" and leave the edited version in $LARNHOME. + +All the "configuration" options are tidily near the top of the "Makefile." +Here are the ones you probably will want to edit: + +LARNHOME I specified (literally) the directory, with path from root, + where "larn" will reside. This included where I put the *.c files, + it is where the *.o files ended up, as well as all data and *.h files. + i suspect the *.c and intallation-documentation files can be moved off, + but the data and bits must all remain here for execution. + +BINDIR I specified (literally) the directory, with path from root, + where the executable "larn" will reside. The "Makefile" will dump + the "a.out", named "larn", in this directory. My BINDIR was not + my LARNHOME, so $BINDIR/larn was the ONLY file dumed here. You'll + probably have to chmod it for public execute, etc. + + +OPTIONS This is how *I* specified them... they are documented in-line: + OPTIONS = -DWIZZARD -DWIZID=157 -DEXTRA -DBSD -DSAVEINHOME + +IV. Compile the bugger. Read "README" before you do. You have a couple + of options here: + + make - will not install, suspect good for updates. + make all - compile (and) intall + make install - just install + + I did "make" and then "make install" -- seems to work "ok", but + "make all" probably safer, if I had known. Note that "Makefile" + is the default file for "make." + +V. Execute and have fun. If wizard code "ok", larn -c will refresh the + scoreboard. Play and win (or get killed) to put somebody on the + scoreboard. + +VI. BUGS and FIXES. + + Please forward any bug-fixes in these regards to me (or Noah), so I may + compile a fix-list for other installers. Thanks. + +Regards, +=============================================================================== +James D. McNamara CSNET: jim@bu-cs + ARPANET: jim%bu-cs@csnet-relay + UUCP: ...harvard!bu-cs!jim + BITNET: jim%bu-cs%csnet-relay.arpa@wiscvm +=============================================================================== + diff --git a/larn/action.c b/larn/action.c new file mode 100644 index 0000000..6fe1a88 --- /dev/null +++ b/larn/action.c @@ -0,0 +1,305 @@ +/* $NetBSD: action.c,v 1.1 2008/02/19 06:05:26 dholland Exp $ */ + +/* + * action.c Larn is copyrighted 1986 by Noah Morgan. + * + * Routines in this file: + * + * ... + */ +#include <sys/cdefs.h> +#ifndef lint +__RCSID("$NetBSD: action.c,v 1.1 2008/02/19 06:05:26 dholland Exp $"); +#endif /* not lint */ +#include <stdlib.h> +#include <unistd.h> +#include "header.h" +#include "extern.h" + +static void ohear(void); + +/* + * act_remove_gems + * + * Remove gems from a throne. + * + * arg is zero if there is a gnome king associated with the throne. + * + * Assumes that cursors() has been called previously, and that a check + * has been made that the throne actually has gems. + */ +void +act_remove_gems(int arg) +{ + int i, k; + + k = rnd(101); + if (k < 25) { + for (i = 0; i < rnd(4); i++) + creategem(); /* gems pop off the + * throne */ + item[playerx][playery] = ODEADTHRONE; + know[playerx][playery] = 0; + } else if (k < 40 && arg == 0) { + createmonster(GNOMEKING); + item[playerx][playery] = OTHRONE2; + know[playerx][playery] = 0; + } else + lprcat("\nnothing happens"); +} + +/* + * act_sit_throne + * + * Sit on a throne. + * + * arg is zero if there is a gnome king associated with the throne + * + * Assumes that cursors() has been called previously. + */ +void +act_sit_throne(int arg) +{ + int k; + + k = rnd(101); + if (k < 30 && arg == 0) { + createmonster(GNOMEKING); + item[playerx][playery] = OTHRONE2; + know[playerx][playery] = 0; + } else if (k < 35) { + lprcat("\nZaaaappp! You've been teleported!\n"); + beep(); + oteleport(0); + } else + lprcat("\nnothing happens"); +} + +/* + * Code to perform the action of drinking at a fountain. Assumes that + * cursors() has already been called, and that a check has been made + * that the player is actually standing at a live fountain. + */ +void +act_drink_fountain(void) +{ + int x; + + if (rnd(1501) < 2) { + lprcat("\nOops! You seem to have caught the dreadful sleep!"); + beep(); + lflush(); + sleep(3); + died(280); + return; + } + x = rnd(100); + if (x < 7) { + c[HALFDAM] += 200 + rnd(200); + lprcat("\nYou feel a sickness coming on"); + } else if (x < 13) + quaffpotion(23); /* see invisible */ + else if (x < 45) + lprcat("\nnothing seems to have happened"); + else if (rnd(3) != 2) + fntchange(1); /* change char levels upward */ + else + fntchange(-1); /* change char levels + * downward */ + if (rnd(12) < 3) { + lprcat("\nThe fountains bubbling slowly quiets"); + item[playerx][playery] = ODEADFOUNTAIN; /* dead fountain */ + know[playerx][playery] = 0; + } +} + +/* + * Code to perform the action of washing at a fountain. Assumes that + * cursors() has already been called and that a check has been made + * that the player is actually standing at a live fountain. + */ +void +act_wash_fountain(void) +{ + int x; + + if (rnd(100) < 11) { + x = rnd((level << 2) + 2); + lprintf("\nOh no! The water was foul! You suffer %ld hit points!", (long) x); + lastnum = 273; + losehp(x); + bottomline(); + cursors(); + } else if (rnd(100) < 29) + lprcat("\nYou got the dirt off!"); + else if (rnd(100) < 31) + lprcat("\nThis water seems to be hard water! The dirt didn't come off!"); + else if (rnd(100) < 34) + createmonster(WATERLORD); /* make water lord */ + else + lprcat("\nnothing seems to have happened"); +} + +/* + * Perform the actions associated with altar desecration. + */ +void +act_desecrate_altar(void) +{ + if (rnd(100) < 60) { + createmonster(makemonst(level + 2) + 8); + c[AGGRAVATE] += 2500; + } else if (rnd(101) < 30) { + lprcat("\nThe altar crumbles into a pile of dust before your eyes"); + forget(); /* remember to destroy + * the altar */ + } else + lprcat("\nnothing happens"); +} + +/* + * Perform the actions associated with praying at an altar and giving + * a donation. + */ +void +act_donation_pray(void) +{ + long amt; + + lprcat("\n\n"); + cursor(1, 24); + cltoeoln(); + cursor(1, 23); + cltoeoln(); + lprcat("how much do you donate? "); + amt = readnum((long) c[GOLD]); + if (amt < 0 || c[GOLD] < amt) { + lprcat("\nYou don't have that much!"); + return; + } + c[GOLD] -= amt; + if (amt < c[GOLD] / 10 || amt < rnd(50)) { + createmonster(makemonst(level + 1)); + c[AGGRAVATE] += 200; + } else if (rnd(101) > 50) { + ohear(); + return; + } else if (rnd(43) == 5) { + if (c[WEAR]) + lprcat("\nYou feel your armor vibrate for a moment"); + enchantarmor(); + return; + } else if (rnd(43) == 8) { + if (c[WIELD]) + lprcat("\nYou feel your weapon vibrate for a moment"); + enchweapon(); + return; + } else + lprcat("\nThank You."); + bottomline(); +} + +/* + * Performs the actions associated with 'just praying' at the altar. Called + * when the user responds 'just pray' when in prompt mode, or enters 0 to + * the money prompt when praying. + * + * Assumes cursors(), and that any leading \n have been printed. + */ +void +act_just_pray(void) +{ + if (rnd(100) < 75) + lprcat("\nnothing happens"); + else if (rnd(13) < 4) + ohear(); + else if (rnd(43) == 10) { + if (c[WEAR]) + lprcat("\nYou feel your armor vibrate for a moment"); + enchantarmor(); + return; + } else if (rnd(43) == 10) { + if (c[WIELD]) + lprcat("\nYou feel your weapon vibrate for a moment"); + enchweapon(); + return; + } else + createmonster(makemonst(level + 1)); +} + +/* + * Function to cast a +3 protection on the player + */ +static void +ohear(void) +{ + lprcat("\nYou have been heard!"); + if (c[ALTPRO] == 0) + c[MOREDEFENSES] += 3; + c[ALTPRO] += 500; /* protection field */ + bottomline(); +} + +/* + * Performs the act of ignoring an altar. + * + * Assumptions: cursors() has been called. + */ +void +act_ignore_altar(void) +{ + if (rnd(100) < 30) { + createmonster(makemonst(level + 1)); + c[AGGRAVATE] += rnd(450); + } else + lprcat("\nnothing happens"); +} + +/* + * Performs the act of opening a chest. + * + * Parameters: x,y location of the chest to open. + * Assumptions: cursors() has been called previously + */ +void +act_open_chest(int x, int y) +{ + int i, k; + + k = rnd(101); + if (k < 40) { + lprcat("\nThe chest explodes as you open it"); + beep(); + i = rnd(10); + lastnum = 281; /* in case he dies */ + lprintf("\nYou suffer %ld hit points damage!", (long) i); + checkloss(i); + switch (rnd(10)) { /* see if he gets a + * curse */ + case 1: + c[ITCHING] += rnd(1000) + 100; + lprcat("\nYou feel an irritation spread over your skin!"); + beep(); + break; + + case 2: + c[CLUMSINESS] += rnd(1600) + 200; + lprcat("\nYou begin to lose hand to eye coordination!"); + beep(); + break; + + case 3: + c[HALFDAM] += rnd(1600) + 200; + beep(); + lprcat("\nA sickness engulfs you!"); + break; + }; + item[x][y] = know[x][y] = 0; + if (rnd(100) < 69) + creategem(); /* gems from the chest */ + dropgold(rnd(110 * iarg[x][y] + 200)); + for (i = 0; i < rnd(4); i++) + something(iarg[x][y] + 2); + } else + lprcat("\nnothing happens"); +} diff --git a/larn/bill.c b/larn/bill.c new file mode 100644 index 0000000..961c424 --- /dev/null +++ b/larn/bill.c @@ -0,0 +1,163 @@ +/* $NetBSD: bill.c,v 1.11 2011/08/29 20:30:37 joerg Exp $ */ + +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)bill.c 5.2 (Berkeley) 5/28/91"; +#else +__RCSID("$NetBSD: bill.c,v 1.11 2011/08/29 20:30:37 joerg Exp $"); +#endif +#endif /* not lint */ + +#include <sys/file.h> +#include <sys/wait.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <paths.h> +#include "header.h" +#include "extern.h" + +/* bill.c Larn is copyrighted 1986 by Noah Morgan. */ + +static const char *mail[] = { + "From: the LRS (Larn Revenue Service)\n", + "~s undeclared income\n", + "\n We have heard you survived the caverns of Larn. Let me be the", + "\nfirst to congratulate you on your success. It was quite a feat.", + "\nIt was also very profitable for you...", + "\n\n The Dungeon Master has informed us that you brought", + "1", + "\ncounty of Larn is in dire need of funds, we have spared no time", + "2", + "\nof this notice, and is due within 5 days. Failure to pay will", + "\nmean penalties. Once again, congratulations, We look forward", + "\nto your future successful expeditions.\n", + NULL, + "From: His Majesty King Wilfred of Larndom\n", + "~s a noble deed\n", + "\n I have heard of your magnificent feat, and I, King Wilfred,", + "\nforthwith declare today to be a national holiday. Furthermore,", + "\nhence three days, ye be invited to the castle to receive the", + "\nhonour of Knight of the realm. Upon thy name shall it be written...", + "\n\nBravery and courage be yours.", + "\n\nMay you live in happiness forevermore...\n", + NULL, + "From: Count Endelford\n", + "~s You Bastard!\n", + "\n I have heard (from sources) of your journey. Congratulations!", + "\nYou Bastard! With several attempts I have yet to endure the", + " caves,\nand you, a nobody, makes the journey! From this time", + " onward, bewarned\nupon our meeting you shall pay the price!\n", + NULL, + "From: Mainair, Duke of Larnty\n", + "~s High Praise\n", + "\n With certainty, a hero I declare to be amongst us! A nod of", + "\nfavour I send to thee. Me thinks Count Endelford this day of", + "\nright breath'eth fire as of dragon of whom ye are slayer. I", + "\nyearn to behold his anger and jealously. Should ye choose to", + "\nunleash some of thy wealth upon those who be unfortunate, I,", + "\nDuke Mainair, shall equal thy gift also.\n", + NULL, + "From: St. Mary's Children's Home\n", + "~s these poor children\n", + "\n News of your great conquests has spread to all of Larndom.", + "\nMight I have a moment of a great adventurers's time? We here at", + "\nSt. Mary's Children's Home are very poor, and many children are", + "\nstarving. Disease is widespread and very often fatal without", + "\ngood food. Could you possibly find it in your heart to help us", + "\nin our plight? Whatever you could give will help much.", + "\n(your gift is tax deductible)\n", + NULL, + "From: The National Cancer Society of Larn\n", + "~s hope\n", + "\nCongratulations on your successful expedition. We are sure much", + "\ncourage and determination were needed on your quest. There are", + "\nmany though, that could never hope to undertake such a journey", + "\ndue to an enfeebling disease -- cancer. We at the National", + "\nCancer Society of Larn wish to appeal to your philanthropy in", + "\norder to save many good people -- possibly even yourself a few", + "\nyears from now. Much work needs to be done in researching this", + "\ndreaded disease, and you can help today. Could you please see it", + "\nin your heart to give generously? Your continued good health", + "\ncan be your everlasting reward.\n", + NULL +}; + +/* + * function to mail the letters to the player if a winner + */ + +void +mailbill(void) +{ + int i; + char fname[32]; + char buf[128]; + const char **cp; + int fd; + + wait(0); + if (fork() == 0) { + resetscroll(); + cp = mail; + snprintf(fname, sizeof(fname), "%slarnmail.XXXXXX", _PATH_TMP); + for (i = 0; i < 6; i++) { + if ((fd = mkstemp(fname)) == -1) + exit(0); + while (*cp != NULL) { + if (*cp[0] == '1') { + snprintf(buf, sizeof(buf), + "\n%ld gold pieces back with you from your journey. As the", + (long) c[GOLD]); + write(fd, buf, strlen(buf)); + } else if (*cp[0] == '2') { + snprintf(buf, sizeof(buf), + "\nin preparing your tax bill. You owe %ld gold pieces as", + (long) c[GOLD] * TAXRATE); + write(fd, buf, strlen(buf)); + } else + write(fd, *cp, strlen(*cp)); + cp++; + } + cp++; + + close(fd); + snprintf(buf, sizeof(buf), + "mail -I %s < %s > /dev/null", loginname, fname); + system(buf); + unlink(fname); + } + } + exit(0); +} diff --git a/larn/config.c b/larn/config.c new file mode 100644 index 0000000..47aa03a --- /dev/null +++ b/larn/config.c @@ -0,0 +1,50 @@ +/* $NetBSD: config.c,v 1.6 2008/01/28 05:38:53 dholland Exp $ */ + +/* + * config.c -- This defines the installation dependent variables. + * Some strings are modified later. ANSI C would + * allow compile time string concatenation, we must + * do runtime concatenation, in main. + * + * Larn is copyrighted 1986 by Noah Morgan. + */ +#include <sys/cdefs.h> +#ifndef lint +__RCSID("$NetBSD: config.c,v 1.6 2008/01/28 05:38:53 dholland Exp $"); +#endif /* not lint */ + +#include "header.h" +#include "pathnames.h" + +/* + * All these strings will be appended to in main() to be complete filenames + */ + +/* the game save filename */ +char savefilename[1024]; + +/* the logging file */ +char logfile[] = _PATH_LOG; + +/* the help text file */ +char helpfile[] = _PATH_HELP; + +/* the score file */ +char scorefile[] = _PATH_SCORE; + +/* the maze data file */ +char larnlevels[] = _PATH_LEVELS; + +/* the .larnopts filename */ +char optsfile[1024] = "/.larnopts"; + +/* the player id datafile name */ +char playerids[] = _PATH_PLAYERIDS; + +char diagfile[] = "Diagfile"; /* the diagnostic filename */ +char ckpfile[] = "Larn12.0.ckp"; /* the checkpoint filename */ +const char *password = "pvnert(x)"; /* the wizards password <=32 */ +char psname[PSNAMESIZE] = "larn"; /* the process name */ + +#define WIZID 1 +int wisid = 0; /* the user id of the only person who can be wizard */ diff --git a/larn/create.c b/larn/create.c new file mode 100644 index 0000000..440b918 --- /dev/null +++ b/larn/create.c @@ -0,0 +1,606 @@ +/* $NetBSD: create.c,v 1.12 2012/06/19 05:30:43 dholland Exp $ */ + +/* create.c Larn is copyrighted 1986 by Noah Morgan. */ + +#include <sys/cdefs.h> +#ifndef lint +__RCSID("$NetBSD: create.c,v 1.12 2012/06/19 05:30:43 dholland Exp $"); +#endif /* not lint */ + +#include "header.h" +#include "extern.h" +#include <unistd.h> + +static void makemaze(int); +static int cannedlevel(int); +static void treasureroom(int); +static void troom(int, int, int, int, int, int); +static void makeobject(int); +static void fillmroom(int, int, int); +static void froom(int, int, int); +static void fillroom(int, int); +static void sethp(int); +static void checkgen(void); + +/* + makeplayer() + + subroutine to create the player and the players attributes + this is called at the beginning of a game and at no other time + */ +void +makeplayer(void) +{ + int i; + scbr(); + clear(); + c[HPMAX] = c[HP] = 10; /* start player off with 15 hit points */ + c[LEVEL] = 1; /* player starts at level one */ + c[SPELLMAX] = c[SPELLS] = 1; /* total # spells starts off as 3 */ + c[REGENCOUNTER] = 16; + c[ECOUNTER] = 96; /* start regeneration correctly */ + c[SHIELD] = c[WEAR] = c[WIELD] = -1; + for (i = 0; i < 26; i++) + iven[i] = 0; + spelknow[0] = spelknow[1] = 1; /* he knows protection, magic missile */ + if (c[HARDGAME] <= 0) { + iven[0] = OLEATHER; + iven[1] = ODAGGER; + ivenarg[1] = ivenarg[0] = c[WEAR] = 0; + c[WIELD] = 1; + } + playerx = rnd(MAXX - 2); + playery = rnd(MAXY - 2); + oldx = 0; + oldy = 25; + gltime = 0; /* time clock starts at zero */ + cbak[SPELLS] = -50; + for (i = 0; i < 6; i++) + c[i] = 12; /* make the attributes, ie str, int, etc. */ + recalc(); +} + + +/* + newcavelevel(level) + int level; + + function to enter a new level. This routine must be called anytime the + player changes levels. If that level is unknown it will be created. + A new set of monsters will be created for a new level, and existing + levels will get a few more monsters. + Note that it is here we remove genocided monsters from the present level. + */ +void +newcavelevel(int x) +{ + int i, j; + if (beenhere[level]) + savelevel(); /* put the level back into storage */ + level = x; /* get the new level and put in working + * storage */ + if (beenhere[x]) { + getlevel(); + sethp(0); + checkgen(); + return; + } + + /* fill in new level */ + for (i = 0; i < MAXY; i++) + for (j = 0; j < MAXX; j++) + know[j][i] = mitem[j][i] = 0; + makemaze(x); + makeobject(x); + beenhere[x] = 1; + sethp(1); + checkgen(); /* wipe out any genocided monsters */ + +#if WIZID + if (wizard || x == 0) +#else + if (x == 0) +#endif + for (j = 0; j < MAXY; j++) + for (i = 0; i < MAXX; i++) + know[i][j] = 1; +} + +/* + makemaze(level) + int level; + + subroutine to make the caverns for a given level. only walls are made. + */ +static int mx, mxl, mxh, my, myl, myh, tmp2; + +static void +makemaze(int k) +{ + int i, j, tmp; + int z; + if (k > 1 && (rnd(17) <= 4 || k == MAXLEVEL - 1 || k == MAXLEVEL + MAXVLEVEL - 1)) { + if (cannedlevel(k)) + return; /* read maze from data file */ + } + if (k == 0) + tmp = 0; + else + tmp = OWALL; + for (i = 0; i < MAXY; i++) + for (j = 0; j < MAXX; j++) + item[j][i] = tmp; + if (k == 0) + return; + eat(1, 1); + if (k == 1) + item[33][MAXY - 1] = 0; /* exit from dungeon */ + + /* now for open spaces -- not on level 10 */ + if (k != MAXLEVEL - 1) { + tmp2 = rnd(3) + 3; + for (tmp = 0; tmp < tmp2; tmp++) { + my = rnd(11) + 2; + myl = my - rnd(2); + myh = my + rnd(2); + if (k < MAXLEVEL) { + mx = rnd(44) + 5; + mxl = mx - rnd(4); + mxh = mx + rnd(12) + 3; + z = 0; + } else { + mx = rnd(60) + 3; + mxl = mx - rnd(2); + mxh = mx + rnd(2); + z = makemonst(k); + } + for (i = mxl; i < mxh; i++) + for (j = myl; j < myh; j++) { + item[i][j] = 0; + if ((mitem[i][j] = z)) + hitp[i][j] = monster[z].hitpoints; + } + } + } + if (k != MAXLEVEL - 1) { + my = rnd(MAXY - 2); + for (i = 1; i < MAXX - 1; i++) + item[i][my] = 0; + } + if (k > 1) + treasureroom(k); +} + +/* + function to eat away a filled in maze + */ +void +eat(int xx, int yy) +{ + int dir, try; + dir = rnd(4); + try = 2; + while (try) { + switch (dir) { + case 1: + if (xx <= 2) + break; /* west */ + if ((item[xx - 1][yy] != OWALL) || (item[xx - 2][yy] != OWALL)) + break; + item[xx - 1][yy] = item[xx - 2][yy] = 0; + eat(xx - 2, yy); + break; + + case 2: + if (xx >= MAXX - 3) + break; /* east */ + if ((item[xx + 1][yy] != OWALL) || (item[xx + 2][yy] != OWALL)) + break; + item[xx + 1][yy] = item[xx + 2][yy] = 0; + eat(xx + 2, yy); + break; + + case 3: + if (yy <= 2) + break; /* south */ + if ((item[xx][yy - 1] != OWALL) || (item[xx][yy - 2] != OWALL)) + break; + item[xx][yy - 1] = item[xx][yy - 2] = 0; + eat(xx, yy - 2); + break; + + case 4: + if (yy >= MAXY - 3) + break; /* north */ + if ((item[xx][yy + 1] != OWALL) || (item[xx][yy + 2] != OWALL)) + break; + item[xx][yy + 1] = item[xx][yy + 2] = 0; + eat(xx, yy + 2); + break; + }; + if (++dir > 4) { + dir = 1; + --try; + } + } +} + +/* + * function to read in a maze from a data file + * + * Format of maze data file: 1st character = # of mazes in file (ascii digit) + * For each maze: 18 lines (1st 17 used) 67 characters per line + * + * Special characters in maze data file: + * + * # wall D door . random monster + * ~ eye of larn ! cure dianthroritis + * - random object + */ +static int +cannedlevel(int k) +{ + char *row; + int i, j; + int it, arg, mit, marg; + if (lopen(larnlevels) < 0) { + write(1, "Can't open the maze data file\n", 30); + died(-282); + return (0); + } + i = lgetc(); + if (i <= '0') { + died(-282); + return (0); + } + for (i = 18 * rund(i - '0'); i > 0; i--) + lgetl(); /* advance to desired maze */ + for (i = 0; i < MAXY; i++) { + row = lgetl(); + for (j = 0; j < MAXX; j++) { + it = mit = arg = marg = 0; + switch (*row++) { + case '#': + it = OWALL; + break; + case 'D': + it = OCLOSEDDOOR; + arg = rnd(30); + break; + case '~': + if (k != MAXLEVEL - 1) + break; + it = OLARNEYE; + mit = rund(8) + DEMONLORD; + marg = monster[mit].hitpoints; + break; + case '!': + if (k != MAXLEVEL + MAXVLEVEL - 1) + break; + it = OPOTION; + arg = 21; + mit = DEMONLORD + 7; + marg = monster[mit].hitpoints; + break; + case '.': + if (k < MAXLEVEL) + break; + mit = makemonst(k + 1); + marg = monster[mit].hitpoints; + break; + case '-': + it = newobject(k + 1, &arg); + break; + }; + item[j][i] = it; + iarg[j][i] = arg; + mitem[j][i] = mit; + hitp[j][i] = marg; + +#if WIZID + know[j][i] = (wizard) ? 1 : 0; +#else + know[j][i] = 0; +#endif + } + } + lrclose(); + return (1); +} + +/* + function to make a treasure room on a level + level 10's treasure room has the eye in it and demon lords + level V3 has potion of cure dianthroritis and demon prince + */ +static void +treasureroom(int lv) +{ + int tx, ty, xsize, ysize; + + for (tx = 1 + rnd(10); tx < MAXX - 10; tx += 10) + if ((lv == MAXLEVEL - 1) || (lv == MAXLEVEL + MAXVLEVEL - 1) || rnd(13) == 2) { + xsize = rnd(6) + 3; + ysize = rnd(3) + 3; + ty = rnd(MAXY - 9) + 1; /* upper left corner of room */ + if (lv == MAXLEVEL - 1 || lv == MAXLEVEL + MAXVLEVEL - 1) + troom(lv, xsize, ysize, tx = tx + rnd(MAXX - 24), ty, rnd(3) + 6); + else + troom(lv, xsize, ysize, tx, ty, rnd(9)); + } +} + +/* + * subroutine to create a treasure room of any size at a given location + * room is filled with objects and monsters + * the coordinate given is that of the upper left corner of the room + */ +static void +troom(int lv, int xsize, int ysize, int tx, int ty, int glyph) +{ + int i, j; + int tp1, tp2; + for (j = ty - 1; j <= ty + ysize; j++) + for (i = tx - 1; i <= tx + xsize; i++) /* clear out space for + * room */ + item[i][j] = 0; + for (j = ty; j < ty + ysize; j++) + for (i = tx; i < tx + xsize; i++) { /* now put in the walls */ + item[i][j] = OWALL; + mitem[i][j] = 0; + } + for (j = ty + 1; j < ty + ysize - 1; j++) + for (i = tx + 1; i < tx + xsize - 1; i++) /* now clear out + * interior */ + item[i][j] = 0; + + switch (rnd(2)) { /* locate the door on the treasure room */ + case 1: + item[i = tx + rund(xsize)][j = ty + (ysize - 1) * rund(2)] = OCLOSEDDOOR; + iarg[i][j] = glyph; /* on horizontal walls */ + break; + case 2: + item[i = tx + (xsize - 1) * rund(2)][j = ty + rund(ysize)] = OCLOSEDDOOR; + iarg[i][j] = glyph; /* on vertical walls */ + break; + }; + + tp1 = playerx; + tp2 = playery; + playery = ty + (ysize >> 1); + if (c[HARDGAME] < 2) + for (playerx = tx + 1; playerx <= tx + xsize - 2; playerx += 2) + for (i = 0, j = rnd(6); i <= j; i++) { + something(lv + 2); + createmonster(makemonst(lv + 1)); + } + else + for (playerx = tx + 1; playerx <= tx + xsize - 2; playerx += 2) + for (i = 0, j = rnd(4); i <= j; i++) { + something(lv + 2); + createmonster(makemonst(lv + 3)); + } + + playerx = tp1; + playery = tp2; +} + + +/* + *********** + MAKE_OBJECT + *********** + subroutine to create the objects in the maze for the given level + */ +static void +makeobject(int j) +{ + int i; + if (j == 0) { + fillroom(OENTRANCE, 0); /* entrance to dungeon */ + fillroom(ODNDSTORE, 0); /* the DND STORE */ + fillroom(OSCHOOL, 0); /* college of Larn */ + fillroom(OBANK, 0); /* 1st national bank of larn */ + fillroom(OVOLDOWN, 0); /* volcano shaft to temple */ + fillroom(OHOME, 0); /* the players home & family */ + fillroom(OTRADEPOST, 0); /* the trading post */ + fillroom(OLRS, 0); /* the larn revenue service */ + return; + } + if (j == MAXLEVEL) + fillroom(OVOLUP, 0); /* volcano shaft up from the temple */ + + /* make the fixed objects in the maze STAIRS */ + if ((j > 0) && (j != MAXLEVEL - 1) && (j != MAXLEVEL + MAXVLEVEL - 1)) + fillroom(OSTAIRSDOWN, 0); + if ((j > 1) && (j != MAXLEVEL)) + fillroom(OSTAIRSUP, 0); + + /* make the random objects in the maze */ + + fillmroom(rund(3), OBOOK, j); + fillmroom(rund(3), OALTAR, 0); + fillmroom(rund(3), OSTATUE, 0); + fillmroom(rund(3), OPIT, 0); + fillmroom(rund(3), OFOUNTAIN, 0); + fillmroom(rnd(3) - 2, OIVTELETRAP, 0); + fillmroom(rund(2), OTHRONE, 0); + fillmroom(rund(2), OMIRROR, 0); + fillmroom(rund(2), OTRAPARROWIV, 0); + fillmroom(rnd(3) - 2, OIVDARTRAP, 0); + fillmroom(rund(3), OCOOKIE, 0); + if (j == 1) + fillmroom(1, OCHEST, j); + else + fillmroom(rund(2), OCHEST, j); + if ((j != MAXLEVEL - 1) && (j != MAXLEVEL + MAXVLEVEL - 1)) + fillmroom(rund(2), OIVTRAPDOOR, 0); + if (j <= 10) { + fillmroom((rund(2)), ODIAMOND, rnd(10 * j + 1) + 10); + fillmroom(rund(2), ORUBY, rnd(6 * j + 1) + 6); + fillmroom(rund(2), OEMERALD, rnd(4 * j + 1) + 4); + fillmroom(rund(2), OSAPPHIRE, rnd(3 * j + 1) + 2); + } + for (i = 0; i < rnd(4) + 3; i++) + fillroom(OPOTION, newpotion()); /* make a POTION */ + for (i = 0; i < rnd(5) + 3; i++) + fillroom(OSCROLL, newscroll()); /* make a SCROLL */ + for (i = 0; i < rnd(12) + 11; i++) + fillroom(OGOLDPILE, 12 * rnd(j + 1) + (j << 3) + 10); /* make GOLD */ + if (j == 5) + fillroom(OBANK2, 0); /* branch office of the bank */ + froom(2, ORING, 0); /* a ring mail */ + froom(1, OSTUDLEATHER, 0); /* a studded leather */ + froom(3, OSPLINT, 0); /* a splint mail */ + froom(5, OSHIELD, rund(3)); /* a shield */ + froom(2, OBATTLEAXE, rund(3)); /* a battle axe */ + froom(5, OLONGSWORD, rund(3)); /* a long sword */ + froom(5, OFLAIL, rund(3)); /* a flail */ + froom(4, OREGENRING, rund(3)); /* ring of regeneration */ + froom(1, OPROTRING, rund(3)); /* ring of protection */ + froom(2, OSTRRING, 4); /* ring of strength + 4 */ + froom(7, OSPEAR, rnd(5)); /* a spear */ + froom(3, OORBOFDRAGON, 0); /* orb of dragon slaying */ + froom(4, OSPIRITSCARAB, 0); /* scarab of negate spirit */ + froom(4, OCUBEofUNDEAD, 0); /* cube of undead control */ + froom(2, ORINGOFEXTRA, 0); /* ring of extra regen */ + froom(3, ONOTHEFT, 0); /* device of antitheft */ + froom(2, OSWORDofSLASHING, 0); /* sword of slashing */ + if (c[BESSMANN] == 0) { + froom(4, OHAMMER, 0); /* Bessman's flailing hammer */ + c[BESSMANN] = 1; + } + if (c[HARDGAME] < 3 || (rnd(4) == 3)) { + if (j > 3) { + froom(3, OSWORD, 3); /* sunsword + 3 */ + froom(5, O2SWORD, rnd(4)); /* a two handed sword */ + froom(3, OBELT, 4); /* belt of striking */ + froom(3, OENERGYRING, 3); /* energy ring */ + froom(4, OPLATE, 5); /* platemail + 5 */ + } + } +} + +/* + subroutine to fill in a number of objects of the same kind + */ + +static void +fillmroom(int n, int what_i, int arg) +{ + int i; + char what; + + /* truncate to char width (just in case it matters) */ + what = (char)what_i; + for (i = 0; i < n; i++) + fillroom(what, arg); +} + +static void +froom(int n, int theitem, int arg) +{ + if (rnd(151) < n) + fillroom(theitem, arg); +} + +/* + subroutine to put an object into an empty room + * uses a random walk + */ +static void +fillroom(int what_i, int arg) +{ + int x, y; + char what; + + /* truncate to char width (just in case it matters) */ + what = (char)what_i; + +#ifdef EXTRA + c[FILLROOM]++; +#endif + + x = rnd(MAXX - 2); + y = rnd(MAXY - 2); + while (item[x][y]) { + +#ifdef EXTRA + c[RANDOMWALK]++;/* count up these random walks */ +#endif + + x += rnd(3) - 2; + y += rnd(3) - 2; + if (x > MAXX - 2) + x = 1; + if (x < 1) + x = MAXX - 2; + if (y > MAXY - 2) + y = 1; + if (y < 1) + y = MAXY - 2; + } + item[x][y] = what; + iarg[x][y] = arg; +} + +/* + subroutine to put monsters into an empty room without walls or other + monsters + */ +int +fillmonst(int what) +{ + int x, y, trys; + for (trys = 5; trys > 0; --trys) { /* max # of creation attempts */ + x = rnd(MAXX - 2); + y = rnd(MAXY - 2); + if ((item[x][y] == 0) && (mitem[x][y] == 0) && ((playerx != x) || (playery != y))) { + mitem[x][y] = what; + know[x][y] = 0; + hitp[x][y] = monster[what].hitpoints; + return (0); + } + } + return (-1); /* creation failure */ +} + +/* + creates an entire set of monsters for a level + must be done when entering a new level + if sethp(1) then wipe out old monsters else leave them there + */ +static void +sethp(int flg) +{ + int i, j; + if (flg) + for (i = 0; i < MAXY; i++) + for (j = 0; j < MAXX; j++) + stealth[j][i] = 0; + if (level == 0) { + c[TELEFLAG] = 0; + return; + } /* if teleported and found level 1 then know + * level we are on */ + if (flg) + j = rnd(12) + 2 + (level >> 1); + else + j = (level >> 1) + 1; + for (i = 0; i < j; i++) + fillmonst(makemonst(level)); + positionplayer(); +} + +/* + * Function to destroy all genocided monsters on the present level + */ +static void +checkgen(void) +{ + int x, y; + for (y = 0; y < MAXY; y++) + for (x = 0; x < MAXX; x++) + if (monster[mitem[x][y]].genocided) + mitem[x][y] = 0; /* no more monster */ +} diff --git a/larn/data.c b/larn/data.c new file mode 100644 index 0000000..4f35994 --- /dev/null +++ b/larn/data.c @@ -0,0 +1,664 @@ +/* $NetBSD: data.c,v 1.13 2008/02/03 20:11:04 dholland Exp $ */ + +/*- + * Copyright (c) 1988 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)data.c 5.3 (Berkeley) 5/13/91"; +#else +__RCSID("$NetBSD: data.c,v 1.13 2008/02/03 20:11:04 dholland Exp $"); +#endif +#endif /* not lint */ + +/* data.c Larn is copyrighted 1986 by Noah Morgan. */ +/* #define NODEFS */ +#include "header.h" +#include "extern.h" + +/* + class[c[LEVEL]-1] gives the correct name of the players experience level + */ +static char aa1[] = " mighty evil master"; +static char aa2[] = "apprentice demi-god"; +static char aa3[] = " minor demi-god "; +static char aa4[] = " major demi-god "; +static char aa5[] = " minor deity "; +static char aa6[] = " major deity "; +static char aa7[] = " novice guardian "; +static char aa8[] = "apprentice guardian"; +static char aa9[] = " The Creator "; +const char *class[] = +{ + " novice explorer ", "apprentice explorer", " practiced explorer", /* -3 */ + " expert explorer ", " novice adventurer", " adventurer ", /* -6 */ + "apprentice conjurer", " conjurer ", " master conjurer ", /* -9 */ + " apprentice mage ", " mage ", " experienced mage ", /* -12 */ + " master mage ", " apprentice warlord", " novice warlord ", /* -15 */ + " expert warlord ", " master warlord ", " apprentice gorgon ", /* -18 */ + " gorgon ", " practiced gorgon ", " master gorgon ", /* -21 */ + " demi-gorgon ", " evil master ", " great evil master ", /* -24 */ + aa1, aa1, aa1, /* -27 */ + aa1, aa1, aa1, /* -30 */ + aa1, aa1, aa1, /* -33 */ + aa1, aa1, aa1, /* -36 */ + aa1, aa1, aa1, /* -39 */ + aa2, aa2, aa2, /* -42 */ + aa2, aa2, aa2, /* -45 */ + aa2, aa2, aa2, /* -48 */ + aa3, aa3, aa3, /* -51 */ + aa3, aa3, aa3, /* -54 */ + aa3, aa3, aa3, /* -57 */ + aa4, aa4, aa4, /* -60 */ + aa4, aa4, aa4, /* -63 */ + aa4, aa4, aa4, /* -66 */ + aa5, aa5, aa5, /* -69 */ + aa5, aa5, aa5, /* -72 */ + aa5, aa5, aa5, /* -75 */ + aa6, aa6, aa6, /* -78 */ + aa6, aa6, aa6, /* -81 */ + aa6, aa6, aa6, /* -84 */ + aa7, aa7, aa7, /* -87 */ + aa8, aa8, aa8, /* -90 */ + aa8, aa8, aa8, /* -93 */ + " earth guardian ", " air guardian ", " fire guardian ", /* -96 */ + " water guardian ", " time guardian ", " ethereal guardian ", /* -99 */ + aa9, aa9, aa9, /* -102 */ +}; + +/* + table of experience needed to be a certain level of player + skill[c[LEVEL]] is the experience required to attain the next level + */ +#define MEG 1000000 +long skill[] = { + 0, 10, 20, 40, 80, 160, 320, 640, 1280, 2560, 5120, /* 1-11 */ + 10240, 20480, 40960, 100000, 200000, 400000, 700000, 1 * MEG, /* 12-19 */ + 2 * MEG, 3 * MEG, 4 * MEG, 5 * MEG, 6 * MEG, 8 * MEG, 10 * MEG, /* 20-26 */ + 12 * MEG, 14 * MEG, 16 * MEG, 18 * MEG, 20 * MEG, 22 * MEG, 24 * MEG, 26 * MEG, 28 * MEG, /* 27-35 */ + 30 * MEG, 32 * MEG, 34 * MEG, 36 * MEG, 38 * MEG, 40 * MEG, 42 * MEG, 44 * MEG, 46 * MEG, /* 36-44 */ + 48 * MEG, 50 * MEG, 52 * MEG, 54 * MEG, 56 * MEG, 58 * MEG, 60 * MEG, 62 * MEG, 64 * MEG, /* 45-53 */ + 66 * MEG, 68 * MEG, 70 * MEG, 72 * MEG, 74 * MEG, 76 * MEG, 78 * MEG, 80 * MEG, 82 * MEG, /* 54-62 */ + 84 * MEG, 86 * MEG, 88 * MEG, 90 * MEG, 92 * MEG, 94 * MEG, 96 * MEG, 98 * MEG, 100 * MEG, /* 63-71 */ + 105 * MEG, 110 * MEG, 115 * MEG, 120 * MEG, 125 * MEG, 130 * MEG, 135 * MEG, 140 * MEG, /* 72-79 */ + 145 * MEG, 150 * MEG, 155 * MEG, 160 * MEG, 165 * MEG, 170 * MEG, 175 * MEG, 180 * MEG, /* 80-87 */ + 185 * MEG, 190 * MEG, 195 * MEG, 200 * MEG, 210 * MEG, 220 * MEG, 230 * MEG, 240 * MEG, /* 88-95 */ + 250 * MEG, 260 * MEG, 270 * MEG, 280 * MEG, 290 * MEG, 300 * MEG /* 96-101 */ +}; +#undef MEG + +u_char *lpbuf, *lpnt, *inbuffer, *lpend; /* input/output pointers + * to the buffers */ +struct cel *cell; /* pointer to the dungeon storage */ +short hitp[MAXX][MAXY]; /* monster hp on level */ +short iarg[MAXX][MAXY]; /* arg for the item array */ +u_char item[MAXX][MAXY]; /* objects in maze if any */ +u_char know[MAXX][MAXY]; /* 1 or 0 if here before */ +u_char mitem[MAXX][MAXY]; /* monster item array */ +u_char moved[MAXX][MAXY]; /* monster movement flags */ +u_char stealth[MAXX][MAXY]; /* 0=sleeping 1=awake monst */ +u_char iven[26]; /* inventory for player */ +short ivenarg[26]; /* inventory for player */ +char lastmonst[40]; /* this has the name of the current monster */ +u_char beenhere[MAXLEVEL + MAXVLEVEL] = {0}; /* 1 if have been on + * this level */ +char VERSION = VER; /* this is the present version # of the + * program */ +char SUBVERSION = SUBVER; +u_char nosignal = 0; /* set to 1 to disable the signals from doing + * anything */ +u_char predostuff = 0; /* 2 means that the trap handling routines + * must do a showplayer() after a trap. 0 + * means don't showplayer() 0 - we are in + * create player screen 1 - we are in welcome + * screen 2 - we are in the normal game */ +char loginname[20]; /* players login name */ +char logname[LOGNAMESIZE]; /* players name storage for scoring */ +u_char sex = 1; /* default is a man 0=woman */ +u_char boldon = 1; /* 1=bold objects 0=inverse objects */ +u_char ckpflag = 0; /* 1 if want checkpointing of game, 0 + * otherwise */ +u_char cheat = 0; /* 1 if the player has fudged save file */ +short level = 0; /* cavelevel player is on = c[CAVELEVEL] */ +u_char wizard = 0; /* the wizard mode flag */ +short lastnum = 0; /* the number of the monster last hitting + * player */ +short hitflag = 0; /* flag for if player has been hit when + * running */ +short hit2flag = 0; /* flag for if player has been hit when + * running */ +short hit3flag = 0; /* flag for if player has been hit flush + * input */ +short playerx, playery; /* the room on the present level of + * the player */ +short lastpx, lastpy; /* 0 --- MAXX-1 or 0 --- MAXY-1 */ +short oldx, oldy; +short lasthx = 0, lasthy = 0; /* location of monster last hit by + * player */ +short nobeep = 0; /* true if program is not to beep */ +unsigned long randx = 33601; /* the random number seed */ +time_t initialtime = 0;/* time playing began */ +long gltime = 0; /* the clock for the game */ +long outstanding_taxes = 0; /* present tax bill from score file */ +long c[100], cbak[100]; /* the character description arrays */ +int enable_scroll = 0; /* constant for enabled/disabled + * scrolling regn */ +char aborted[] = " aborted"; +struct sphere *spheres = 0; /* pointer to linked list for spheres of + * annihilation */ +const char *levelname[] = +{" H", " 1", " 2", " 3", " 4", " 5", " 6", " 7", " 8", " 9", "10", "V1", "V2", "V3"}; + +char objnamelist[] = " ATOP%^F&^+M=%^$$f*OD#~][[)))(((||||||||{?!BC}o:@.<<<<EVV))([[]]](^ [H*** ^^ S tsTLc............................................"; +char monstnamelist[] = " BGHJKOScjtAELNQRZabhiCTYdegmvzFWflorXV pqsyUkMwDDPxnDDuD ..............................................................."; +const char *objectname[] = +{0, "a holy altar", "a handsome jewel encrusted throne", "the orb", "a pit", + "a staircase leading upwards", "an elevator going up", "a bubbling fountain", + "a great marble statue", "a teleport trap", "the college of Larn", + "a mirror", "the DND store", "a staircase going down", "an elevator going down", + "the bank of Larn", "the 5th branch of the Bank of Larn", + "a dead fountain", "gold", "an open door", "a closed door", + "a wall", "The Eye of Larn", "plate mail", "chain mail", "leather armor", + "a sword of slashing", "Bessman's flailing hammer", "a sunsword", + "a two handed sword", "a spear", "a dagger", + "ring of extra regeneration", "a ring of regeneration", "a ring of protection", + "an energy ring", "a ring of dexterity", "a ring of strength", + "a ring of cleverness", "a ring of increase damage", "a belt of striking", + "a magic scroll", "a magic potion", "a book", "a chest", + "an amulet of invisibility", "an orb of dragon slaying", + "a scarab of negate spirit", "a cube of undead control", + "device of theft prevention", "a brilliant diamond", "a ruby", + "an enchanting emerald", "a sparkling sapphire", "the dungeon entrance", + "a volcanic shaft leaning downward", "the base of a volcanic shaft", + "a battle axe", "a longsword", "a flail", "ring mail", "studded leather armor", + "splint mail", "plate armor", "stainless plate armor", "a lance of death", + "an arrow trap", "an arrow trap", "a shield", "your home", + "gold", "gold", "gold", "a dart trap", + "a dart trap", "a trapdoor", "a trapdoor", "the local trading post", + "a teleport trap", "a massive throne", + "a sphere of annihilation", "a handsome jewel encrusted throne", + "the Larn Revenue Service", "a fortune cookie", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" +}; + + + +/* + * for the monster data + * + * array to do rnd() to create monsters <= a given level + */ +u_char monstlevel[] = {5, 11, 17, 22, 27, 33, 39, 42, 46, 50, 53, 56, 59}; + +struct monst monster[] = { + /* + * NAME LV AC DAM ATT DEF GEN + * INT GOLD HP EXP + * ----------------------------------------------------------------- + */ + {"", 0, 0, 0, 0, 0, 0, 3, 0, 0, 0}, + {"bat", 1, 0, 1, 0, 0, 0, 3, 0, 1, 1}, + {"gnome", 1, 10, 1, 0, 0, 0, 8, 30, 2, 2}, + {"hobgoblin", 1, 14, 2, 0, 0, 0, 5, 25, 3, 2}, + {"jackal", 1, 17, 1, 0, 0, 0, 4, 0, 1, 1}, + {"kobold", 1, 20, 1, 0, 0, 0, 7, 10, 1, 1}, + + {"orc", 2, 12, 1, 0, 0, 0, 9, 40, 4, 2}, + {"snake", 2, 15, 1, 0, 0, 0, 3, 0, 3, 1}, + {"giant centipede", 2, 14, 0, 4, 0, 0, 3, 0, 1, 2}, + {"jaculi", 2, 20, 1, 0, 0, 0, 3, 0, 2, 1}, + {"troglodyte", 2, 10, 2, 0, 0, 0, 5, 80, 4, 3}, + {"giant ant", 2, 8, 1, 4, 0, 0, 4, 0, 5, 5}, + + /* + * NAME LV AC DAM ATT DEF GEN + * INT GOLD HP EXP + * ----------------------------------------------------------------- + */ + + {"floating eye", 3, 8, 1, 0, 0, 0, 3, 0, 5, 2}, + {"leprechaun", 3, 3, 0, 8, 0, 0, 3, 1500, 13, 45}, + {"nymph", 3, 3, 0, 14, 0, 0, 9, 0, 18, 45}, + {"quasit", 3, 5, 3, 0, 0, 0, 3, 0, 10, 15}, + {"rust monster", 3, 4, 0, 1, 0, 0, 3, 0, 18, 25}, + {"zombie", 3, 12, 2, 0, 0, 0, 3, 0, 6, 7}, + + {"assassin bug", 4, 9, 3, 0, 0, 0, 3, 0, 20, 15}, + {"bugbear", 4, 5, 4, 15, 0, 0, 5, 40, 20, 35}, + {"hell hound", 4, 5, 2, 2, 0, 0, 6, 0, 16, 35}, + {"ice lizard", 4, 11, 2, 10, 0, 0, 6, 50, 16, 25}, + {"centaur", 4, 6, 4, 0, 0, 0, 10, 40, 24, 45}, + + /* + * NAME LV AC DAM ATT DEF GEN + * INT GOLD HP EXP + * ----------------------------------------------------------------- + */ + + {"troll", 5, 4, 5, 0, 0, 0, 9, 80, 50, 300}, + {"yeti", 5, 6, 4, 0, 0, 0, 5, 50, 35, 100}, + {"white dragon", 5, 2, 4, 5, 0, 0, 16, 500, 55, 1000}, + {"elf", 5, 8, 1, 0, 0, 0, 15, 50, 22, 35}, + {"gelatinous cube", 5, 9, 1, 0, 0, 0, 3, 0, 22, 45}, + + {"metamorph", 6, 7, 3, 0, 0, 0, 3, 0, 30, 40}, + {"vortex", 6, 4, 3, 0, 0, 0, 3, 0, 30, 55}, + {"ziller", 6, 15, 3, 0, 0, 0, 3, 0, 30, 35}, + {"violet fungi", 6, 12, 3, 0, 0, 0, 3, 0, 38, 100}, + {"wraith", 6, 3, 1, 6, 0, 0, 3, 0, 30, 325}, + {"forvalaka", 6, 2, 5, 0, 0, 0, 7, 0, 50, 280}, + + /* + * NAME LV AC DAM ATT DEF GEN + * INT GOLD HP EXP + * ----------------------------------------------------------------- + */ + + {"lama nobe", 7, 7, 3, 0, 0, 0, 6, 0, 35, 80}, + {"osequip", 7, 4, 3, 16, 0, 0, 4, 0, 35, 100}, + {"rothe", 7, 15, 5, 0, 0, 0, 3, 100, 50, 250}, + {"xorn", 7, 0, 6, 0, 0, 0, 13, 0, 60, 300}, + {"vampire", 7, 3, 4, 6, 0, 0, 17, 0, 50, 1000}, + {"invisible stalker", 7, 3, 6, 0, 0, 0, 5, 0, 50, 350}, + + {"poltergeist", 8, 1, 4, 0, 0, 0, 3, 0, 50, 450}, + {"disenchantress", 8, 3, 0, 9, 0, 0, 3, 0, 50, 500}, + {"shambling mound", 8, 2, 5, 0, 0, 0, 6, 0, 45, 400}, + {"yellow mold", 8, 12, 4, 0, 0, 0, 3, 0, 35, 250}, + {"umber hulk", 8, 3, 7, 11, 0, 0, 14, 0, 65, 600}, + + /* + * NAME LV AC DAM ATT DEF GEN + * INT GOLD HP EXP + * ----------------------------------------------------------------- + */ + + {"gnome king", 9, -1, 10, 0, 0, 0, 18, 2000, 100, 3000}, + {"mimic", 9, 5, 6, 0, 0, 0, 8, 0, 55, 99}, + {"water lord", 9, -10, 15, 7, 0, 0, 20, 0, 150, 15000}, + {"bronze dragon", 9, 2, 9, 3, 0, 0, 16, 300, 80, 4000}, + {"green dragon", 9, 3, 8, 10, 0, 0, 15, 200, 70, 2500}, + {"purple worm", 9, -1, 11, 0, 0, 0, 3, 100, 120, 15000}, + {"xvart", 9, -2, 12, 0, 0, 0, 13, 0, 90, 1000}, + + {"spirit naga", 10, -20, 12, 12, 0, 0, 23, 0, 95, 20000}, + {"silver dragon", 10, -1, 12, 3, 0, 0, 20, 700, 100, 10000}, + {"platinum dragon", 10, -5, 15, 13, 0, 0, 22, 1000, 130, 24000}, + {"green urchin", 10, -3, 12, 0, 0, 0, 3, 0, 85, 5000}, + {"red dragon", 10, -2, 13, 3, 0, 0, 19, 800, 110, 14000}, + + {"type I demon lord", 12, -30, 18, 0, 0, 0, 20, 0, 140, 50000}, + {"type II demon lord", 13, -30, 18, 0, 0, 0, 21, 0, 160, 75000}, + {"type III demon lord", 14, -30, 18, 0, 0, 0, 22, 0, 180, 100000}, + {"type IV demon lord", 15, -35, 20, 0, 0, 0, 23, 0, 200, 125000}, + {"type V demon lord", 16, -40, 22, 0, 0, 0, 24, 0, 220, 150000}, + {"type VI demon lord", 17, -45, 24, 0, 0, 0, 25, 0, 240, 175000}, + {"type VII demon lord", 18, -70, 27, 6, 0, 0, 26, 0, 260, 200000}, + {"demon prince", 25, -127, 30, 6, 0, 0, 28, 0, 345, 300000} + + /* + * NAME LV AC DAM ATT DEF + * GEN INT GOLD HP EXP + * ------------------------------------------------------------------- + * -- + */ +}; + +/* name array for scrolls */ + +const char *scrollname[] = {"", "", "", "", "", "", "", "", "", "", "", "", "", "", +"", "", "", "", "", "", "", "", "", "", "", "", "", ""}; + +const char *scrollhide[] = { + " enchant armor", + " enchant weapon", + " enlightenment", + " blank paper", + " create monster", + " create artifact", + " aggravate monsters", + " time warp", + " teleportation", + " expanded awareness", + " haste monsters", + " monster healing", + " spirit protection", + " undead protection", + " stealth", + " magic mapping", + " hold monsters", + " gem perfection", + " spell extension", + " identify", + " remove curse", + " annihilation", + " pulverization", + " life protection", + " ", + " ", + " ", + " " +}; + +const char *potionname[] = {"", "", "", "", "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", "", "", "", "", +"", "", "", "", "", "", ""}; + +/* name array for magic potions */ +const char *potionhide[] = { + " sleep", + " healing", + " raise level", + " increase ability", + " wisdom", + " strength", + " raise charisma", + " dizziness", + " learning", + " gold detection", + " monster detection", + " forgetfulness", + " water", + " blindness", + " confusion", + " heroism", + " sturdiness", + " giant strength", + " fire resistance", + " treasure finding", + " instant healing", + " cure dianthroritis", + " poison", + " see invisible", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " " +}; + + + +/* + spell data + */ +u_char spelknow[SPNUM] = {0}; +u_char splev[] = {1, 4, 9, 14, 18, 22, 26, 29, 32, 35, 37, 37, 37, 37, 37}; + +const char *spelcode[] = { + "pro", "mle", "dex", "sle", "chm", "ssp", + "web", "str", "enl", "hel", "cbl", "cre", "pha", "inv", + "bal", "cld", "ply", "can", "has", "ckl", "vpr", + "dry", "lit", "drl", "glo", "flo", "fgr", + "sca", "hld", "stp", "tel", "mfi", /* 31 */ + "sph", "gen", "sum", "wtw", "alt", "per" +}; + +const char *spelname[] = { + "protection", "magic missile", "dexterity", + "sleep", "charm monster", "sonic spear", + + "web", "strength", "enlightenment", + "healing", "cure blindness", "create monster", + "phantasmal forces", "invisibility", + + "fireball", "cold", "polymorph", + "cancellation", "haste self", "cloud kill", + "vaporize rock", + + "dehydration", "lightning", "drain life", + "invulnerability", "flood", "finger of death", + + "scare monster", "hold monster", "time stop", + "teleport away", "magic fire", + + "sphere of annihilation", "genocide", "summon demon", + "walk through walls", "alter reality", "permanence", + "" +}; + +const char *speldescript[] = { + /* 1 */ + "generates a +2 protection field", + "creates and hurls a magic missile equivalent to a +1 magic arrow", + "adds +2 to the caster's dexterity", + "causes some monsters to go to sleep", + "some monsters may be awed at your magnificence", + "causes your hands to emit a screeching sound toward what they point", + /* 7 */ + "causes strands of sticky thread to entangle an enemy", + "adds +2 to the caster's strength for a short term", + "the caster becomes aware of things in the vicinity", + "restores some hp to the caster", + "restores sight to one so unfortunate as to be blinded", + "creates a monster near the caster appropriate for the location", + "creates illusions, and if believed, monsters die", + "the caster becomes invisible", + /* 15 */ + "makes a ball of fire that burns on what it hits", + "sends forth a cone of cold which freezes what it touches", + "you can find out what this does for yourself", + "negates the ability of a monster to use its special abilities", + "speeds up the caster's movements", + "creates a fog of poisonous gas which kills all that is within it", + "this changes rock to air", + /* 22 */ + "dries up water in the immediate vicinity", + "your finger will emit a lightning bolt when this spell is cast", + "subtracts hit points from both you and a monster", + "this globe helps to protect the player from physical attack", + "this creates an avalanche of H2O to flood the immediate chamber", + "this is a holy spell and calls upon your god to back you up", + /* 28 */ + "terrifies the monster so that hopefully it won't hit the magic user", + "the monster is frozen in its tracks if this is successful", + "all movement in the caverns ceases for a limited duration", + "moves a particular monster around in the dungeon (hopefully away from you)", + "this causes a curtain of fire to appear all around you", + /* 33 */ + "anything caught in this sphere is instantly killed. Warning -- dangerous", + "eliminates a species of monster from the game -- use sparingly", + "summons a demon who hopefully helps you out", + "allows the player to walk through walls for a short period of time", + "god only knows what this will do", + "makes a character spell permanent, i. e. protection, strength, etc.", + "" +}; + +char spelweird[MAXMONST + 8][SPNUM] = { + /* + * p m d s c s w s e h c c p i b c p c h c v d l d g f f + * s h s t m s g s w a p + */ + /* + * r l e l h s e t n e b r h n a l l a a k p r i r l l g + * c l t e f p e u t l e + */ + /* + * o e x e m p b r l l l e a v l d y n s l r y t l o o r + * a d p l i h n m w t r + */ + + + /* bat */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* gnome */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* hobgoblin */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* jackal */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* kobold */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + + /* orc */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* snake */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* giant centipede */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* jaculi */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* troglodyte */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + + /* giant ant */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* floating eye */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* leprechaun */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* nymph */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* quasit */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + + /* rust monster */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* zombie */ {0, 0, 0, 8, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 4, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* assassin bug */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* bugbear */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* hell hound */ {0, 6, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + + /* ice lizard */ {0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* centaur */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* troll */ {0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* yeti */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* white dragon */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 15, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + + /* elf */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* gelatinous cube */ {0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* metamorph */ {0, 13, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 4, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* vortex */ {0, 13, 0, 0, 0, 10, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 4, 0, 0, 0, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* ziller */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + + /* violet fungi */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* wraith */ {0, 0, 0, 8, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 4, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* forvalaka */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* lama nobe */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* osequip */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + + /* rothe */ {0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* xorn */ {0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* vampire */ {0, 0, 0, 8, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* invisible staker */ {0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* poltergeist */ {0, 13, 0, 8, 0, 4, 1, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 4, 0, 0, 0, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + + /* disenchantress */ {0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* shambling mound */ {0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* yellow mold */ {0, 0, 0, 8, 0, 0, 1, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* umber hulk */ {0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* gnome king */ {0, 7, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + + /* mimic */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* water lord */ {0, 13, 0, 8, 3, 4, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 4, 0, 0, 0, 0, 0, 16, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* bronze dragon */ {0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* green dragon */ {0, 7, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* purple worm */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + + /* xvart */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* spirit naga */ {0, 13, 0, 8, 3, 4, 1, 0, 0, 0, 0, 0, 0, 5, 0, 4, 9, 0, 0, 4, 0, 4, 0, 0, 0, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* silver dragon */ {0, 6, 0, 9, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* platinum dragon */ {0, 7, 0, 9, 0, 0, 11, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* green urchin */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* red dragon */ {0, 6, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + + /* + * p m d s c s w s e h c c p i b c p c h c v d l d g f f + * s h s t m s g s w a p + */ + /* + * r l e l h s e t n e b r h n a l l a a k p r i r l l g + * c l t e f p e u t l e + */ + /* + * o e x e m p b r l l l e a v l d y n s l r y t l o o r + * a d p l i h n m w t r + */ + + /* demon lord */ {0, 7, 0, 4, 3, 0, 1, 0, 0, 0, 0, 0, 14, 5, 0, 0, 4, 0, 0, 4, 0, 4, 0, 0, 0, 4, 4, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0}, + /* demon lord */ {0, 7, 0, 4, 3, 0, 1, 0, 0, 0, 0, 0, 14, 5, 0, 0, 4, 0, 0, 4, 0, 4, 0, 0, 0, 4, 4, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0}, + /* demon lord */ {0, 7, 0, 4, 3, 0, 1, 0, 0, 0, 0, 0, 14, 5, 0, 0, 4, 0, 0, 4, 0, 4, 0, 0, 0, 4, 4, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0}, + /* demon lord */ {0, 7, 0, 4, 3, 0, 1, 0, 0, 0, 0, 0, 14, 5, 0, 0, 4, 0, 0, 4, 0, 4, 0, 0, 0, 4, 4, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0}, + /* demon lord */ {0, 7, 0, 4, 3, 0, 1, 0, 0, 0, 0, 0, 14, 5, 0, 0, 4, 0, 0, 4, 0, 4, 0, 0, 0, 4, 4, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0}, + /* demon lord */ {0, 7, 0, 4, 3, 0, 1, 0, 0, 0, 0, 0, 14, 5, 0, 0, 4, 0, 0, 4, 0, 4, 0, 0, 0, 4, 4, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0}, + /* demon lord */ {0, 7, 0, 4, 3, 0, 1, 0, 0, 0, 0, 0, 14, 5, 0, 0, 4, 0, 0, 4, 0, 4, 0, 0, 0, 4, 4, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0}, + /* demon prince */ {0, 7, 0, 4, 3, 9, 1, 0, 0, 0, 0, 0, 14, 5, 0, 0, 4, 0, 0, 4, 0, 4, 0, 0, 0, 4, 4, 4, 0, 0, 0, 4, 9, 0, 0, 0, 0, 0} + +}; + +const char *spelmes[] = {"", + /* 1 */ "the web had no effect on the %s", + /* 2 */ "the %s changed shape to avoid the web", + /* 3 */ "the %s isn't afraid of you", + /* 4 */ "the %s isn't affected", + /* 5 */ "the %s can see you with his infravision", + /* 6 */ "the %s vaporizes your missile", + /* 7 */ "your missile bounces off the %s", + /* 8 */ "the %s doesn't sleep", + /* 9 */ "the %s resists", + /* 10 */ "the %s can't hear the noise", + /* 11 */ "the %s's tail cuts it free of the web", + /* 12 */ "the %s burns through the web", + /* 13 */ "your missiles pass right through the %s", + /* 14 */ "the %s sees through your illusions", + /* 15 */ "the %s loves the cold!", + /* 16 */ "the %s loves the water!" +}; + +/* + * function to create scroll numbers with appropriate probability of + * occurrence + * + * 0 - armor 1 - weapon 2 - enlightenment 3 - paper + * 4 - create monster 5 - create item 6 - aggravate 7 - time warp + * 8 - teleportation 9 - expanded awareness 10 - haste monst + * 11 - heal monster 12 - spirit protection 13 - undead protection + * 14 - stealth 15 - magic mapping 16 - hold monster + * 17 - gem perfection 18 - spell extension 19 - identify + * 20 - remove curse 21 - annihilation 22 - pulverization + * 23 - life protection + */ +u_char scprob[] = {0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, + 3, 3, 3, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, + 9, 9, 10, 10, 10, 10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 13, 14, 14, + 15, 15, 16, 16, 16, 17, 17, 18, 18, 19, 19, 19, 20, 20, 20, 20, 21, 22, +22, 22, 23}; + +/* + * function to return a potion number created with appropriate probability + * of occurrence + * + * 0 - sleep 1 - healing 2 - raise level + * 3 - increase ability 4 - gain wisdom 5 - gain strength + * 6 - charismatic character 7 - dizziness 8 - learning + * 9 - gold detection 10 - monster detection 11 - forgetfulness + * 12 - water 13 - blindness 14 - confusion + * 15 - heroism 16 - sturdiness 17 - giant strength + * 18 - fire resistance 19 - treasure finding 20 - instant healing + * 21 - cure dianthroritis 22 - poison 23 - see invisible + */ +u_char potprob[] = {0, 0, 1, 1, 1, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 9, 9, 9, 10, 10, 10, 11, 11, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 20, 22, 22, 23, 23}; + +u_char nlpts[] = {0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 5, 6, 7}; +u_char nch[] = {0, 0, 0, 1, 1, 1, 2, 2, 3, 4}; +u_char nplt[] = {0, 0, 0, 0, 1, 1, 2, 2, 3, 4}; +u_char ndgg[] = {0, 0, 0, 1, 1, 1, 1, 2, 2, 3, 3, 4, 5}; +u_char nsw[] = {0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 3}; diff --git a/larn/datfiles/larn.help b/larn/datfiles/larn.help new file mode 100644 index 0000000..0e5edc5 --- /dev/null +++ b/larn/datfiles/larn.help @@ -0,0 +1,140 @@ +5 Welcome to the game of Larn. At this moment, you face a great problem. +Your daughter has contracted a strange disease, and none of your home remedies +seem to have any effect. You sense that she is in mortal danger, and you must +try to save her. Time ago you heard of a land of great danger and opportunity. +Perhaps here is the solution you need. + + It has been said that there once was a great magician who called himself +Polinneaus. Many years ago, after having many miraculous successes, Polinneaus +retired to the caverns of Larn, where he devoted most of his time to the +creation of magic. Rumors have it that one day Polinneaus set out to dispel +an attacking army in a forest some distance to the north. It is believed that +here he met his demise. + + The caverns of Larn, it is thought, must be magnificent in design, +and contain much magic and treasure. One option you have is to undertake a +journey into these caverns. + + + Good Luck! You're going to need it! + + + + + [4mHelp File for The Caverns of Larn[m + +h move to the left H run left . stay here +j move down J run down Z teleport yourself +k move up K run up c cast a spell +l move to the right L run right r read a scroll +y move northwest Y run northwest q quaff a potion +u move northeast U run northeast W wear armor +b move southwest B run southwest T take off armor +n move southeast N run southeast w wield a weapon +^ identify a trap g give present pack weight P give tax status +d drop an item i inventory your pockets Q quit the game +v print program version S save the game D list all items found +? this help screen A create diagnostic file e eat something + (wizards only) +larn ++ restore checkpointed game +larn -s list the scoreboard +larn -i list scores with inventories +larn -n suppress welcome message when beginning a game +larn -h print out all the command line options +larn -<number> specify difficulty of the game (may be used with -n) +larn -o<optsfile> specify the .larnopts file to be used +larn -c create new scoreboards -- prompts for a password + [7mSpecial Notes[m + +When [7mdropping gold[m, if you type '*' as your amount, all your gold gets dropped. +In general, typing in '*' means all of what your interested in. This is true +when visiting the bank, or when contributing at altars. + +Larn may need a [7mVT100[m to operate. A check is made of the environment variable +[7m"TERM"[m and it must be equal to [7m"vt100"[m. This only applies if +the game has been compiled with "VT100" defined in the Makefile. If compiled +to use [7mtermcap[m, there are no terminal restrictions, save needing cm, ce, & cl +termcap entries. + +When in the store, trading post, school, or home, an [7m<escape>[m will get you out. + +larn -l print out the larn log file + +When casting a spell, if you need a list of spells you can cast, type '[7mD[m' as +the first letter of your spell. The available list of spells will be shown, +after which you may enter the spell code. This only works on the 1st letter +of the spell you are casting. + +The Author of Larn is Noah Morgan (1982-3), Copying for Profit is Prohibited +Copyright 1986 by Noah Morgan, All Rights Reserved. + [7mBackground Information for Larn[m + + Welcome to the game of Larn. At this moment, you face a great problem. +Your daughter has contracted a strange disease, and none of your home remedies +seem to have any effect. You sense that she is in mortal danger, and you must +try to save her. Time ago you heard of a land of great danger and opportunity. +Perhaps here is the solution you need. + + It has been said that there once was a great magician who called himself +Polinneaus. Many years ago, after having many miraculous successes, Polinneaus +retired to the caverns of Larn, where he devoted most of his time to the +creation of magic. Rumors have it that one day Polinneaus set out to dispel +an attacking army in a forest some distance to the north. It is believed that +here he met his demise. + + The caverns of Larn, it is thought, must be magnificent in design, +and contain much magic and treasure. One option you have is to undertake a +journey into these caverns. + + Good Luck! You're going to need it! + + + + [7mHow to use the .larnopts option file[m + +The file ".larnopts", if used, should be in your home directory (see -o). +A sequence of words terminated by whitespace is used to specify options. + + Word Meaning + + bold-objects select bold display of objects + inverse-objects select inverse video display of objects + no-introduction do not display intro message + enable-checkpointing turn on periodic checkpointing + no-beep disable beeping of the terminal + male choose your sex to be a man + female choose your sex to be a woman + name: "your name" choose your playing name + monster: "monst name" choose a name for a monster + savefile: "save-file-name" define what the savegame filename will be + +Your name and monster names must be enclosed in double quotation marks and may +be up to 34 characters long. Longer names are truncated. Anything enclosed in +quotation marks is considered one word, and must be separated from other words +by whitespace. + + [7mExplanation of the Larn scoreboard facility[m + + Larn supports TWO scoreboards, one for winners, and one for deceased +characters. Each player (by userid or playerid, see UIDSCORE in Makefile) +is allowed one slot on each scoreboard, if the score is in the top ten for +that scoreboard. This design helps insure that frequent players of Larn +do not hog the scoreboard, and gives more players a chance for glory. Level +of difficulty is also noted on the scoreboards, and this takes precedence +over score for determining what entry is on the scoreboard. For example: +if "Yar, the Bug Slayer" has a score of 128003 on the scoreboard at diff 0, +then his game at diff 1 and a score of 4112 would replace his previous +entry on the scoreboard. Note that when a player dies, his inventory is +stored in the scoreboard so that everyone can see what items the player had +at the time of his death. + + + + + + + + + + + diff --git a/larn/datfiles/larnmaze b/larn/datfiles/larnmaze new file mode 100644 index 0000000..37a89c3 --- /dev/null +++ b/larn/datfiles/larnmaze @@ -0,0 +1,288 @@ +@################################################################### +# # . # # # # # . # +# D D . . D . # +###D########################################## # # ###D### +# -# #. # # ################ . .# +# ####### ######## ############ D #### # # # +# ... #.# # # # . # # # #### # ############ # ###D### +# #.# # # ## # # # ############ #### # #- # # # #. # +# . # # # # ## #- # # # - D #### # # . D # #.# # ... # +# # #.# # # # # # . . # # # # # # #-# # ~.! # +###D### ### #######D## # ############ ###### ########## ### ####### +# # @ .# # ..... ...# +###D###########################################################D### +# . #.....# # # # -# # # # # # +# ..... . D D D D. # +# #.....# # # # # # .# # # # +################################################################### + +################################################################### +#.. . D # . # #- # +############# ######### # ## ### ##### ## #### ###### ####### ### # +#.#!#~# # # .-# # #- # # # # -# # # # +# # #.# . # ####### # # # # # # # # # ##### +# # ..# ##### # # # #### # ## ## ## # ###### ####### # # +# - ..D # D # . D # # # #.##### ## ## # #. #.# #..# # ### # +####### ####### ### # # # # # # D # D D #..D # # +#- # # # #### # ###### # ## # #. # #..# ##### +### #######################- # # # ###################### # # +# ... # . #..# ### # - .. . #. ### # +# # # # ### #################### # # # +# ### # +################################################################# # +#- D ### # # # # +# . # # # D # +################################################################### + +################################################################### +# .. # +# ############## ############################################## # # +# # # # # .. # # # +# #D## # # ############D################# ########### # # # +######### #- # # # #- D # # ~ # # # # # # # +# # # # # # # ### # # # D - # # ####### # # # # +# .... # #### # # # #!# # # ###### .. #.# # # # # # # +# .... # # # # ### # # # # #########D#### # ### # # # # # +# .... ######## # # D # #- # #.. # ...#.# # #.# # # # # # +# # # ### # #### #.- D - #.# #.#.# # # # # # +#####DD## ######## # # D D. # # # #...# # # # # # +# # ..# # # # ############################## # ##### # # # # # +# ......# # # # # # # # # # +# ####### ###### ################################ ######### # ### # +# .D. # # +################################################################### + +################################################################### +# ## ## ### ## # +# ##### ## ..- ## ##.## ## # +# # ! ## ## . ## ## .## .. ## # +# #....###### ## ## ## . ## ## # +# # - # ## ##D# ## . ## ## # +# #####D ~ ####### ###........ ## ... ## ## # +# # # ## ## .... ## . ## ## # +# #. ######## ## ## . - ####D#### - # +# #.- #...## ## ... ## ### ...... ## ## # +# #. #..## ## ######### ## ... ## ### ## .. # +# #.. #.## ## ## - ## . #### ## ## ##### ## # +# #####D## ## ## ###### ## ## ####### ## # +# D -.## #### ######## ##DD## ######## #D # +# ###### ... ## ########## ## # +#### . . ### +################################################################### + +################################################################### +# # +# ####.########################################## ## ########## +# # #.#.#.# #.. #. # # # +####### # # # # # ##### #! # ########### # ### # +# # # # # # # ##...## # # # # # #-# # +# ..- D # # # # # ## . ## ####D##### .. ### # # # # +# # # # ##. ~ .## # # ### # # ##### # # +############ # # # # ## . . .## #...# .. #.# # # # # +# .. .# # # # # ## - ## # # D.D # ######### # +# . D # # # # ##.......## ####D#####.# # # +# - . # # # # # ## ## # . # ### ########### # +############ #.#.# # ###D### # . #....# # # +# # #-# D .. # .# ###....## ##-### # +#### ########################################## ###### ### # +# . # +################################################################### + +################################################################### +# # +# ###########################D####### ## # +# ####################### # ##...... ## ## # +# ########D###### # # !##.... ## ## # +# ############ ## ## ... # # #...## ~ ## ###### # +# # # # # ...... # # # .. ##### ## # +# # - .. # # # ########### # # . ######### ## # +# ##### ##### # # # # #... ## ## # +# # # ########## ######## ########DD#### ####### # +# # # .... # # # # # ## ## # +# ##### # # ....# # ######### # # ##### ## # +# #- ######D##########..# # ######### # - #.. ## ## # +# ##### # # # #... #.....## # +# # ################### ###############... #### # ###### +# # # +################################################################### + +################################################################### +# # +########### ##### ##### #####D#### ##### ###### # +#.. # #-..D ###### ## # ## # ## ..## ### #### # +# #### ### ##### # ## # ##### # ## ### ## # # # +# # # # # # ##### # ## # # #### # #..##### - ### # +# # # ####### ### #...# # ######## # # #~. .... # # +# # # # #...# # # ######### ############## # +# # ###### ####### # ### ### # .. # # +# # # - # #-#####!# # # #### ## #### # # +# ###### # ########## #.. ....# # ######## # # # ## ## # +# -.# ##### ####...## # . .# #..# # ## # # +# #### ###### #.### #####.#### #### ### ## # # +# #- #######....#### ...... #.# . # ## # # +# ####### ##### ###########.############### ### # +# . # +################################################################### + +################################################################### +# # #- . # # # # # # # # # +# # # #####.##### # # # # # # # # # # ###########D########### # +# # ##### # # ### # # # # # # # # # ### #.... # # # # +# # # # # # # # # # # # # # #.!. -.. #.##.# # # +# # . - # # ####### # # # # # # # # # ########## ## # # +#.####### # # D # . # # # +#. -# ################################################### # +# ##### D. . # +# # ### ###D### ### ### ###D### ### ### ###D### ###.### # +# # ###### ### .# .# #. # # # # # # # # . # # +# ### # -# ### .#. - .# # # #...- # # .. # # -..# # . # # +# # # ### #. ~ .# # # # # # # # .. .# # # # +#.###### # ### # # #.....# # # # # # # # # # +# # ####### ####### ####### ####### ####### ####### ####### # +# # ... .. # +################################################################### + +################################################################### +# # . # ### # # # # . # +# D D . # . D . # +############################################### # # ###D### +# -# #. # # ################ . .# +# #######D######## ############ # #### # # # +# ... #. # #!~ . # # # #--# # ############ #####D### +# #.# # # ## # # # ############ #--# # #- # # # #. # +# . # #-# # ## #- # # # - D ## # # # . D #### #.# # ... # +# # #-# # # # # # . . # # # # # # #-# # -.- # +###D### ### ####### ## # ############ ###### ########## ### ###D### +# # @ . ..... ...# +##################################### ###############D### +# . #.....# # # # -# # # # # +# ..... . D D D D. # +# #.....# # # # # .# # # # +################################################################### + +################################################################### +#.. . # D . #- # +### ######### ######### # ## ### ##### ## ########### ####### ### # +#.# # # # .-# # #- # # # # -# # # # +# # #.# . # ####### # # # # # # # # # ##### +# # ..####### # # # #### # ## ## ##!# ###### ####### # # +# - ..D # D # . D # # # #.##### ## ## # #. #.# #..# # ### # +####### ####### ### # # # # # # D # D D #..D # # +#- # # # #### # ###### # ## # #. # #..# ##### +### #######################- # # # ###################### # # +# ... # . #..# ### # - .. . #. ### # +# # # # #-# #################### # # # +# # -# # +################################-################################ # +#-..... # ####D ### # # # # +#~..... # # # # D # +################################################################### + +################################################################### +# .. # +# ############## ############################################## # # +# # # # # .. # # # +# #D## # # ############D################# ########### # # # +######### #- # # # #- D # #.!..# # # # # # # +# # # # # # # # ### ## # #....D - # # ####### # # # # +# .... # #### # # # #~# # # ###### .. #.# # # # # # # +###....## # # # ###.# # # #########D## # # ### # # # # # +# .... ######## # # D .# #- # .. # ...#.# # #.# # # # # # +# # ## # ### .# #### .- D - #.# #.#.# # # # # # +#####DD## ######## #... .# . # # # #.#. # # # # # +# # ..# # # ############################## # # ##### # # # # +# ......# # # # # # # # # # # +# ####### ###### ################################D# ######### ### # +# .D. #-.-# +################################################################### + +################################################################### +# ## ## ## ## ## # +# ############## ## ..- ## ## . ## ## # +# # # ## ## . ## ## . ##.. ## # +# #....###### ## ## ######## ####### . ## ## # +# # - # ## ## ##D# ## . ## ## # +# # D###### ###........ ## ... ## ###### # +# # # ## ## .... ## . ## ## - # # +# #. ######## ## ## . - ####D####.. D # +# #.- #...## ## ... ## ### ...... ## ##### # +# #. #..## ## ######### ## ... ## ### ## .. # +# #.. #.## ## ## - ## . #### ## ## ##### ## # +# #####D####### # ## ## ##..## ## ## ## ## ## # +# D -.## # # #### ##.-.-## ##DD## ### #### ## # +# ######. ...# #### ## #### ##### ## ##D# +####~!.... D . . # +################################################################### + +################################################################### +# # +# ####.########################################## ## ########## +# # #.#.#.# #.. . # # # +####### # # # # # ############# # ########### # ### # +# # # # # # # --##...##-- # # # # #-# # +# ..- D # # # # # #-## . ##-# ####D##### .. ### # # # # +# # # # ###. .### # ~ # ### ### ##### # # +############ # # # # ## . . .## #...# .. #.# # # +# .. .# # # # # ## - ## # # D. # ######### # +# . D # # # # ###.......### ####D#####.# # # +# - . # # # # # -## ##- # . ### ########### # +############ #.#.# # ######D###### # . ....# # # +# #!#-# # .. # . ###....## ##-### # +#### ############################## ########## ###### ### # +# D . # +################################################################### + +################################################################### +# # +# ###############D####################D# # +# ####################### # ##...... # # +# ########D###### # # ##.... # # +# ############### ## ## ... # # #...## ##### # +# # ~ # # #!...... # # # .. ##### # # +# # - .. # # # ########### # # . ######### # # +# ##### ######## # # # # # #... ## # # +# # # # ########## ######## ########D #### ###### # +# # # ....# # # # # # ## # # +# ##### # ###### # ....# # ######### # # #####D# # +# #- ## # #######..# # ######### # - #.. # +# ##### # # # #... #..... # +# # ################### ###############... #### ###### +# # # +################################################################### + +################################################################### +# # # +########### ##### # #####D#### ##### ###### # +#.. # #~..D ###### ## # ## # ## ..## ### #### # +# #### ### ##### # ## # ##### # ## ### ## # # # +# # # # # # ##### # ## # # #### # #..##### - ### # +# # # ####### ### #...# # ######## # # #!. .... # # +# # # # #...# # # ######### ############## # +# # ###### ####### # ### ### # .. # # +# # # - # #- # # # ######## #### # # +# # # ########## #.. ....# # ######## # ## ## # +# # -. ##### ####...## # . .# .. # # # +# # ###### ##### #####.#### ####### # # +# # - #######....#### ...... #.# . # ## # # +# ######### ##### ###########.############### ### # +# . # +################################################################### + +################################################################### +# D D #-..........# # # # # # +#D#D# #####.#####.# # # # # # #############D########### # +# # ##### #.#~###.# # # # # # ### .... # # # # +#D# # #.......# # # # # # # . . -.. #. #.# # # +# # . - # #.####### # # # # # ################## # # +#D####### #. # D # . # # # +#. D D -# ################################################### # +# ###### . . # +# # D ### ###D### ####### ###D### ####### ###D### ####### # +# # ###### # # .# .# #. # # # # # # # # . # # +# ### -# # # .#. - .# # # #...- # # .. # # -..# # . # # +#DD# # # # #. .# # # # # # # # .. .# # # # +#.###### # # # # #.....# # # # # # ! # # # # +# # ####### ####### ### ### ####### ### ### ####### ### ### # +# # ... .. # +################################################################### + diff --git a/larn/datfiles/larnopts b/larn/datfiles/larnopts new file mode 100644 index 0000000..17216ed --- /dev/null +++ b/larn/datfiles/larnopts @@ -0,0 +1,12 @@ +process-name: "Winnie-the-Pooh" +enable-checkpointing +bold-objects +male +play-day-play +no-introduction +name: "King of the Realm" +monster: "abominable snowman" +monster: "tooth fairy" +monster: "Yaccerous Lexicous" +savefile: "/save/noah/games/Larn12.0.sav" + diff --git a/larn/diag.c b/larn/diag.c new file mode 100644 index 0000000..a3482c1 --- /dev/null +++ b/larn/diag.c @@ -0,0 +1,411 @@ +/* $NetBSD: diag.c,v 1.13 2012/06/19 05:30:43 dholland Exp $ */ + +/* diag.c Larn is copyrighted 1986 by Noah Morgan. */ +#include <sys/cdefs.h> +#ifndef lint +__RCSID("$NetBSD: diag.c,v 1.13 2012/06/19 05:30:43 dholland Exp $"); +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/times.h> +#include <sys/stat.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include "header.h" +#include "extern.h" + +static void greedy(void); +static void fsorry(void); +static void fcheat(void); + +static struct tms cputime; + +/* + *************************** + DIAG -- dungeon diagnostics + *************************** + + subroutine to print out data for debugging + */ +#ifdef EXTRA +static int rndcount[16]; +void +diag() +{ + int i, j; + int hit, dam; + cursors(); + lwclose(); + if (lcreat(diagfile) < 0) { /* open the diagnostic file */ + lcreat((char *) 0); + lprcat("\ndiagnostic failure\n"); + return (-1); + } + write(1, "\nDiagnosing . . .\n", 18); + lprcat("\n\nBeginning of DIAG diagnostics ----------\n"); + + /* for the character attributes */ + + lprintf("\n\nPlayer attributes:\n\nHit points: %2ld(%2ld)", (long) c[HP], (long) c[HPMAX]); + lprintf("\ngold: %ld Experience: %ld Character level: %ld Level in caverns: %ld", + (long) c[GOLD], (long) c[EXPERIENCE], (long) c[LEVEL], (long) level); + lprintf("\nTotal types of monsters: %ld", (long) MAXMONST + 8); + + lprcat("\f\nHere's the dungeon:\n\n"); + + i = level; + for (j = 0; j < MAXLEVEL + MAXVLEVEL; j++) { + newcavelevel(j); + lprintf("\nMaze for level %s:\n", levelname[level]); + diagdrawscreen(); + } + newcavelevel(i); + + lprcat("\f\nNow for the monster data:\n\n"); + lprcat(" Monster Name LEV AC DAM ATT DEF GOLD HP EXP \n"); + lprcat("--------------------------------------------------------------------------\n"); + for (i = 0; i <= MAXMONST + 8; i++) { + lprintf("%19s %2ld %3ld ", monster[i].name, (long) monster[i].level, (long) monster[i].armorclass); + lprintf(" %3ld %3ld %3ld ", (long) monster[i].damage, (long) monster[i].attack, (long) monster[i].defense); + lprintf("%6ld %3ld %6ld\n", (long) monster[i].gold, (long) monster[i].hitpoints, (long) monster[i].experience); + } + + lprcat("\n\nHere's a Table for the to hit percentages\n"); + lprcat("\n We will be assuming that players level = 2 * monster level"); + lprcat("\n and that the players dexterity and strength are 16."); + lprcat("\n to hit: if (rnd(22) < (2[monst AC] + your level + dex + WC/8 -1)/2) then hit"); + lprcat("\n damage = rund(8) + WC/2 + STR - c[HARDGAME] - 4"); + lprcat("\n to hit: if rnd(22) < to hit then player hits\n"); + lprcat("\n Each entry is as follows: to hit / damage / number hits to kill\n"); + lprcat("\n monster WC = 4 WC = 20 WC = 40"); + lprcat("\n---------------------------------------------------------------"); + for (i = 0; i <= MAXMONST + 8; i++) { + hit = 2 * monster[i].armorclass + 2 * monster[i].level + 16; + dam = 16 - c[HARDGAME]; + lprintf("\n%20s %2ld/%2ld/%2ld %2ld/%2ld/%2ld %2ld/%2ld/%2ld", + monster[i].name, + (long) (hit / 2), (long) max(0, dam + 2), (long) (monster[i].hitpoints / (dam + 2) + 1), + (long) ((hit + 2) / 2), (long) max(0, dam + 10), (long) (monster[i].hitpoints / (dam + 10) + 1), + (long) ((hit + 5) / 2), (long) max(0, dam + 20), (long) (monster[i].hitpoints / (dam + 20) + 1)); + } + + lprcat("\n\nHere's the list of available potions:\n\n"); + for (i = 0; i < MAXPOTION; i++) + lprintf("%20s\n", &potionhide[i][1]); + lprcat("\n\nHere's the list of available scrolls:\n\n"); + for (i = 0; i < MAXSCROLL; i++) + lprintf("%20s\n", &scrollhide[i][1]); + lprcat("\n\nHere's the spell list:\n\n"); + lprcat("spell name description\n"); + lprcat("-------------------------------------------------------------------------------------------\n\n"); + for (j = 0; j < SPNUM; j++) { + lprc(' '); + lprcat(spelcode[j]); + lprintf(" %21s %s\n", spelname[j], speldescript[j]); + } + + lprcat("\n\nFor the c[] array:\n"); + for (j = 0; j < 100; j += 10) { + lprintf("\nc[%2ld] = ", (long) j); + for (i = 0; i < 9; i++) + lprintf("%5ld ", (long) c[i + j]); + } + + lprcat("\n\nTest of random number generator ----------------"); + lprcat("\n for 25,000 calls divided into 16 slots\n\n"); + + for (i = 0; i < 16; i++) + rndcount[i] = 0; + for (i = 0; i < 25000; i++) + rndcount[rund(16)]++; + for (i = 0; i < 16; i++) { + lprintf(" %5ld", (long) rndcount[i]); + if (i == 7) + lprc('\n'); + } + + lprcat("\n\n"); + lwclose(); + lcreat((char *) 0); + lprcat("Done Diagnosing . . ."); + return (0); +} +/* + subroutine to count the number of occurrences of an object + */ +int +dcount(l) + int l; +{ + int i, j, p; + int k; + k = 0; + for (i = 0; i < MAXX; i++) + for (j = 0; j < MAXY; j++) + for (p = 0; p < MAXLEVEL; p++) + if (cell[p * MAXX * MAXY + i * MAXY + j].item == l) + k++; + return (k); +} + +/* + subroutine to draw the whole screen as the player knows it + */ +void +diagdrawscreen() +{ + int i, j, k; + + for (i = 0; i < MAXY; i++) + /* for the east west walls of this line */ + { + for (j = 0; j < MAXX; j++) + if (k = mitem[j][i]) + lprc(monstnamelist[k]); + else + lprc(objnamelist[item[j][i]]); + lprc('\n'); + } +} +#endif + + +/* + to save the game in a file + */ +static time_t zzz = 0; +int +savegame(char *fname) +{ + int i, k; + struct sphere *sp; + struct stat statbuf; + + nosignal = 1; + lflush(); + savelevel(); + ointerest(); + if (lcreat(fname) < 0) { + lcreat((char *) 0); + lprintf("\nCan't open file <%s> to save game\n", fname); + nosignal = 0; + return (-1); + } + set_score_output(); + lwrite((char *) beenhere, MAXLEVEL + MAXVLEVEL); + for (k = 0; k < MAXLEVEL + MAXVLEVEL; k++) + if (beenhere[k]) + lwrite((char *) &cell[k * MAXX * MAXY], sizeof(struct cel) * MAXY * MAXX); + times(&cputime); /* get cpu time */ + c[CPUTIME] += (cputime.tms_utime + cputime.tms_stime) / 60; + lwrite((char *) &c[0], 100 * sizeof(long)); + lprint((long) gltime); + lprc(level); + lprc(playerx); + lprc(playery); + lwrite((char *) iven, 26); + lwrite((char *) ivenarg, 26 * sizeof(short)); + for (k = 0; k < MAXSCROLL; k++) + lprc(scrollname[k][0]); + for (k = 0; k < MAXPOTION; k++) + lprc(potionname[k][0]); + lwrite((char *) spelknow, SPNUM); + lprc(wizard); + lprc(rmst); /* random monster generation counter */ + for (i = 0; i < 90; i++) + lprc(itm[i].qty); + lwrite((char *) course, 25); + lprc(cheat); + lprc(VERSION); + for (i = 0; i < MAXMONST; i++) + lprc(monster[i].genocided); /* genocide info */ + for (sp = spheres; sp; sp = sp->p) + lwrite((char *) sp, sizeof(struct sphere)); /* save spheres of + * annihilation */ + time(&zzz); + lprint((long) (zzz - initialtime)); + lwrite((char *) &zzz, sizeof(long)); + if (fstat(io_outfd, &statbuf) < 0) + lprint(0L); + else + lprint((long) statbuf.st_ino); /* inode # */ + lwclose(); + lastmonst[0] = 0; +#ifndef VT100 + setscroll(); +#endif /* VT100 */ + lcreat((char *) 0); + nosignal = 0; + return (0); +} + +void +restoregame(char *fname) +{ + int i, k; + struct sphere *sp, *sp2; + struct stat filetimes; + cursors(); + lprcat("\nRestoring . . ."); + lflush(); + if (lopen(fname) <= 0) { + lcreat((char *) 0); + lprintf("\nCan't open file <%s>to restore game\n", fname); + nap(2000); + c[GOLD] = c[BANKACCOUNT] = 0; + died(-265); + return; + } + lrfill((char *) beenhere, MAXLEVEL + MAXVLEVEL); + for (k = 0; k < MAXLEVEL + MAXVLEVEL; k++) + if (beenhere[k]) + lrfill((char *) &cell[k * MAXX * MAXY], sizeof(struct cel) * MAXY * MAXX); + + lrfill((char *) &c[0], 100 * sizeof(long)); + gltime = larn_lrint(); + level = c[CAVELEVEL] = lgetc(); + playerx = lgetc(); + playery = lgetc(); + lrfill((char *) iven, 26); + lrfill((char *) ivenarg, 26 * sizeof(short)); + for (k = 0; k < MAXSCROLL; k++) + scrollname[k] = lgetc() ? scrollhide[k] : ""; + for (k = 0; k < MAXPOTION; k++) + potionname[k] = lgetc() ? potionhide[k] : ""; + lrfill((char *) spelknow, SPNUM); + wizard = lgetc(); + rmst = lgetc(); /* random monster creation flag */ + + for (i = 0; i < 90; i++) + itm[i].qty = lgetc(); + lrfill((char *) course, 25); + cheat = lgetc(); + if (VERSION != lgetc()) { /* version number */ + cheat = 1; + lprcat("Sorry, But your save file is for an older version of larn\n"); + nap(2000); + c[GOLD] = c[BANKACCOUNT] = 0; + died(-266); + return; + } + for (i = 0; i < MAXMONST; i++) + monster[i].genocided = lgetc(); /* genocide info */ + for (sp = 0, i = 0; i < c[SPHCAST]; i++) { + sp2 = sp; + sp = (struct sphere *) malloc(sizeof(struct sphere)); + if (sp == 0) { + write(2, "Can't malloc() for sphere space\n", 32); + break; + } + lrfill((char *) sp, sizeof(struct sphere)); /* get spheres of + * annihilation */ + sp->p = 0; /* null out pointer */ + if (i == 0) + spheres = sp; /* beginning of list */ + else + sp2->p = sp; + } + + time(&zzz); + initialtime = zzz - larn_lrint(); + /* get the creation and modification time of file */ + fstat(io_infd, &filetimes); + lrfill((char *) &zzz, sizeof(long)); + zzz += 6; + if (filetimes.st_ctime > zzz) + fsorry(); /* file create time */ + else if (filetimes.st_mtime > zzz) + fsorry(); /* file modify time */ + if (c[HP] < 0) { + died(284); + return; + } /* died a post mortem death */ + oldx = oldy = 0; + /* XXX the following will break on 64-bit inode numbers */ + i = larn_lrint(); /* inode # */ + if (i && (filetimes.st_ino != (ino_t) i)) + fsorry(); + lrclose(); + if (strcmp(fname, ckpfile) == 0) { + if (lappend(fname) < 0) + fcheat(); + else { + lprc(' '); + lwclose(); + } + lcreat((char *) 0); + } else if (unlink(fname) < 0) + fcheat(); /* can't unlink save file */ + /* for the greedy cheater checker */ + for (k = 0; k < 6; k++) + if (c[k] > 99) + greedy(); + if (c[HPMAX] > 999 || c[SPELLMAX] > 125) + greedy(); + if (c[LEVEL] == 25 && c[EXPERIENCE] > skill[24]) { /* if patch up lev 25 + * player */ + long tmp; + tmp = c[EXPERIENCE] - skill[24]; /* amount to go up */ + c[EXPERIENCE] = skill[24]; + raiseexperience((long) tmp); + } + getlevel(); + lasttime = gltime; +} + +/* + subroutine to not allow greedy cheaters + */ +static void +greedy(void) +{ +#if WIZID + if (wizard) + return; +#endif + + lprcat("\n\nI am so sorry, but your character is a little TOO good! Since this\n"); + lprcat("cannot normally happen from an honest game, I must assume that you cheated.\n"); + lprcat("In that you are GREEDY as well as a CHEATER, I cannot allow this game\n"); + lprcat("to continue.\n"); + nap(5000); + c[GOLD] = c[BANKACCOUNT] = 0; + died(-267); + return; +} + +/* + subroutine to not allow altered save files and terminate the attempted + restart + */ +static void +fsorry(void) +{ + lprcat("\nSorry, but your savefile has been altered.\n"); + lprcat("However, seeing as I am a good sport, I will let you play.\n"); + lprcat("Be advised though, you won't be placed on the normal scoreboard."); + cheat = 1; + nap(4000); +} + +/* + subroutine to not allow game if save file can't be deleted + */ +static void +fcheat(void) +{ +#if WIZID + if (wizard) + return; +#endif + + lprcat("\nSorry, but your savefile can't be deleted. This can only mean\n"); + lprcat("that you tried to CHEAT by protecting the directory the savefile\n"); + lprcat("is in. Since this is unfair to the rest of the larn community, I\n"); + lprcat("cannot let you play this game.\n"); + nap(5000); + c[GOLD] = c[BANKACCOUNT] = 0; + died(-268); + return; +} diff --git a/larn/display.c b/larn/display.c new file mode 100644 index 0000000..3f4389b --- /dev/null +++ b/larn/display.c @@ -0,0 +1,637 @@ +/* $NetBSD: display.c,v 1.10 2012/06/19 05:30:43 dholland Exp $ */ + +/* display.c Larn is copyrighted 1986 by Noah Morgan. */ +#include <sys/cdefs.h> +#ifndef lint +__RCSID("$NetBSD: display.c,v 1.10 2012/06/19 05:30:43 dholland Exp $"); +#endif /* not lint */ + +#include "header.h" +#include "extern.h" + +#define makecode(_a,_b,_c) (((_a)<<16) + ((_b)<<8) + (_c)) + +static void bot_hpx(void); +static void bot_spellx(void); +static void botside(void); +static void botsub(int, const char *); +static void seepage(void); + +static int minx, maxx, miny, maxy, k, m; +static char bot1f = 0, bot2f = 0, bot3f = 0; +static char always = 0; +/* + bottomline() + + now for the bottom line of the display + */ +void +bottomline(void) +{ + recalc(); + bot1f = 1; +} + +void +bottomhp(void) +{ + bot2f = 1; +} + +void +bottomspell(void) +{ + bot3f = 1; +} + +void +bottomdo(void) +{ + if (bot1f) { + bot3f = bot1f = bot2f = 0; + bot_linex(); + return; + } + if (bot2f) { + bot2f = 0; + bot_hpx(); + } + if (bot3f) { + bot3f = 0; + bot_spellx(); + } +} + +void +bot_linex(void) +{ + int i; + if (cbak[SPELLS] <= -50 || (always)) { + cursor(1, 18); + if (c[SPELLMAX] > 99) + lprintf("Spells:%3ld(%3ld)", (long) c[SPELLS], (long) c[SPELLMAX]); + else + lprintf("Spells:%3ld(%2ld) ", (long) c[SPELLS], (long) c[SPELLMAX]); + lprintf(" AC: %-3ld WC: %-3ld Level", (long) c[AC], (long) c[WCLASS]); + if (c[LEVEL] > 99) + lprintf("%3ld", (long) c[LEVEL]); + else + lprintf(" %-2ld", (long) c[LEVEL]); + lprintf(" Exp: %-9ld %s\n", (long) c[EXPERIENCE], class[c[LEVEL] - 1]); + lprintf("HP: %3ld(%3ld) STR=%-2ld INT=%-2ld ", + (long) c[HP], (long) c[HPMAX], (long) (c[STRENGTH] + c[STREXTRA]), (long) c[INTELLIGENCE]); + lprintf("WIS=%-2ld CON=%-2ld DEX=%-2ld CHA=%-2ld LV:", + (long) c[WISDOM], (long) c[CONSTITUTION], (long) c[DEXTERITY], (long) c[CHARISMA]); + + if ((level == 0) || (wizard)) + c[TELEFLAG] = 0; + if (c[TELEFLAG]) + lprcat(" ?"); + else + lprcat(levelname[level]); + lprintf(" Gold: %-6ld", (long) c[GOLD]); + always = 1; + botside(); + c[TMP] = c[STRENGTH] + c[STREXTRA]; + for (i = 0; i < 100; i++) + cbak[i] = c[i]; + return; + } + botsub(makecode(SPELLS, 8, 18), "%3ld"); + if (c[SPELLMAX] > 99) + botsub(makecode(SPELLMAX, 12, 18), "%3ld)"); + else + botsub(makecode(SPELLMAX, 12, 18), "%2ld) "); + botsub(makecode(HP, 5, 19), "%3ld"); + botsub(makecode(HPMAX, 9, 19), "%3ld"); + botsub(makecode(AC, 21, 18), "%-3ld"); + botsub(makecode(WCLASS, 30, 18), "%-3ld"); + botsub(makecode(EXPERIENCE, 49, 18), "%-9ld"); + if (c[LEVEL] != cbak[LEVEL]) { + cursor(59, 18); + lprcat(class[c[LEVEL] - 1]); + } + if (c[LEVEL] > 99) + botsub(makecode(LEVEL, 40, 18), "%3ld"); + else + botsub(makecode(LEVEL, 40, 18), " %-2ld"); + c[TMP] = c[STRENGTH] + c[STREXTRA]; + botsub(makecode(TMP, 18, 19), "%-2ld"); + botsub(makecode(INTELLIGENCE, 25, 19), "%-2ld"); + botsub(makecode(WISDOM, 32, 19), "%-2ld"); + botsub(makecode(CONSTITUTION, 39, 19), "%-2ld"); + botsub(makecode(DEXTERITY, 46, 19), "%-2ld"); + botsub(makecode(CHARISMA, 53, 19), "%-2ld"); + if ((level != cbak[CAVELEVEL]) || (c[TELEFLAG] != cbak[TELEFLAG])) { + if ((level == 0) || (wizard)) + c[TELEFLAG] = 0; + cbak[TELEFLAG] = c[TELEFLAG]; + cbak[CAVELEVEL] = level; + cursor(59, 19); + if (c[TELEFLAG]) + lprcat(" ?"); + else + lprcat(levelname[level]); + } + botsub(makecode(GOLD, 69, 19), "%-6ld"); + botside(); +} + +/* + special subroutine to update only the gold number on the bottomlines + called from ogold() + */ +void +bottomgold(void) +{ + botsub(makecode(GOLD, 69, 19), "%-6ld"); + /* botsub(GOLD,"%-6ld",69,19); */ +} + +/* + special routine to update hp and level fields on bottom lines + called in monster.c hitplayer() and spattack() + */ +static void +bot_hpx(void) +{ + if (c[EXPERIENCE] != cbak[EXPERIENCE]) { + recalc(); + bot_linex(); + } else + botsub(makecode(HP, 5, 19), "%3ld"); +} + +/* + special routine to update number of spells called from regen() + */ +static void +bot_spellx(void) +{ + botsub(makecode(SPELLS, 9, 18), "%2ld"); +} + +/* + common subroutine for a more economical bottomline() + */ +static struct bot_side_def { + int typ; + const char *string; +} + bot_data[] = +{ + { STEALTH, "stealth"}, + { UNDEADPRO, "undead pro" }, + { SPIRITPRO, "spirit pro" }, + { CHARMCOUNT, "Charm"}, + { TIMESTOP, "Time Stop" }, + { HOLDMONST, "Hold Monst" }, + { GIANTSTR, "Giant Str"}, + { FIRERESISTANCE, "Fire Resit" }, + { DEXCOUNT, "Dexterity" }, + { STRCOUNT, "Strength"}, + { SCAREMONST, "Scare" }, + { HASTESELF, "Haste Self" }, + { CANCELLATION, "Cancel"}, + { INVISIBILITY, "Invisible" }, + { ALTPRO, "Protect 3" }, + { PROTECTIONTIME, "Protect 2"}, + { WTW, "Wall-Walk" } +}; + +static void +botside(void) +{ + int i, idx; + for (i = 0; i < 17; i++) { + idx = bot_data[i].typ; + if ((always) || (c[idx] != cbak[idx])) { + if ((always) || (cbak[idx] == 0)) { + if (c[idx]) { + cursor(70, i + 1); + lprcat(bot_data[i].string); + } + } else if (c[idx] == 0) { + cursor(70, i + 1); + lprcat(" "); + } + cbak[idx] = c[idx]; + } + } + always = 0; +} + +static void +botsub(int idx, const char *str) +{ + int x, y; + y = idx & 0xff; + x = (idx >> 8) & 0xff; + idx >>= 16; + if (c[idx] != cbak[idx]) { + cbak[idx] = c[idx]; + cursor(x, y); + lprintf(str, (long) c[idx]); + } +} + +/* + * subroutine to draw only a section of the screen + * only the top section of the screen is updated. + * If entire lines are being drawn, then they will be cleared first. + */ +/* for limited screen drawing */ +static int d_xmin = 0, d_xmax = MAXX, d_ymin = 0, d_ymax = MAXY; + +void +draws(int xmin, int xmax, int ymin, int ymax) +{ + int i, idx; + if (xmin == 0 && xmax == MAXX) { /* clear section of screen as + * needed */ + if (ymin == 0) + cl_up(79, ymax); + else + for (i = ymin; i < ymin; i++) + cl_line(1, i + 1); + xmin = -1; + } + d_xmin = xmin; + d_xmax = xmax; + d_ymin = ymin; + d_ymax = ymax; /* for limited screen drawing */ + drawscreen(); + if (xmin <= 0 && xmax == MAXX) { /* draw stuff on right side + * of screen as needed */ + for (i = ymin; i < ymax; i++) { + idx = bot_data[i].typ; + if (c[idx]) { + cursor(70, i + 1); + lprcat(bot_data[i].string); + } + cbak[idx] = c[idx]; + } + } +} + +/* + drawscreen() + + subroutine to redraw the whole screen as the player knows it + */ +u_char screen[MAXX][MAXY]; /* template for the screen */ +static u_char d_flag; +void +drawscreen(void) +{ + int i, j, kk; + int lastx, lasty; /* variables used to optimize the + * object printing */ + if (d_xmin == 0 && d_xmax == MAXX && d_ymin == 0 && d_ymax == MAXY) { + d_flag = 1; + clear(); /* clear the screen */ + } else { + d_flag = 0; + cursor(1, 1); + } + if (d_xmin < 0) + d_xmin = 0; /* d_xmin=-1 means display all without + * bottomline */ + + for (i = d_ymin; i < d_ymax; i++) + for (j = d_xmin; j < d_xmax; j++) + if (know[j][i] == 0) + screen[j][i] = ' '; + else if ((kk = mitem[j][i]) != 0) + screen[j][i] = monstnamelist[kk]; + else if ((kk = item[j][i]) == OWALL) + screen[j][i] = '#'; + else + screen[j][i] = ' '; + + for (i = d_ymin; i < d_ymax; i++) { + j = d_xmin; + while ((screen[j][i] == ' ') && (j < d_xmax)) + j++; + /* was m=0 */ + if (j >= d_xmax) + m = d_xmin; /* don't search backwards if blank + * line */ + else { /* search backwards for end of line */ + m = d_xmax - 1; + while ((screen[m][i] == ' ') && (m > d_xmin)) + --m; + if (j <= m) + cursor(j + 1, i + 1); + else + continue; + } + while (j <= m) { + if (j <= m - 3) { + for (kk = j; kk <= j + 3; kk++) + if (screen[kk][i] != ' ') + kk = 1000; + if (kk < 1000) { + while (screen[j][i] == ' ' && j <= m) + j++; + cursor(j + 1, i + 1); + } + } + lprc(screen[j++][i]); + } + } + // setbold(); /* print out only bold objects now */ + + for (lastx = lasty = 127, i = d_ymin; i < d_ymax; i++) + for (j = d_xmin; j < d_xmax; j++) { + if ((kk = item[j][i]) != 0) + if (kk != OWALL) + if ((know[j][i]) && (mitem[j][i] == 0)) + if (objnamelist[kk] != ' ') { + if (lasty != i + 1 || lastx != j) + cursor(lastx = j + 1, lasty = i + 1); + else + lastx++; + lprc(objnamelist[kk]); + } + } + + // resetbold(); + if (d_flag) { + always = 1; + botside(); + always = 1; + bot_linex(); + } + oldx = 99; + d_xmin = 0, d_xmax = MAXX, d_ymin = 0, d_ymax = MAXY; /* for limited screen + * drawing */ +} + + +/* + showcell(x,y) + + subroutine to display a cell location on the screen + */ +void +showcell(int x, int y) +{ + int i, j, kk, mm; + if (c[BLINDCOUNT]) + // 20150211 bkw: + /* return */; /* see nothing if blind */ + if (c[AWARENESS]) { + minx = x - 3; + maxx = x + 3; + miny = y - 3; + maxy = y + 3; + } else { + minx = x - 1; + maxx = x + 1; + miny = y - 1; + maxy = y + 1; + } + + if (minx < 0) + minx = 0; + if (maxx > MAXX - 1) + maxx = MAXX - 1; + if (miny < 0) + miny = 0; + if (maxy > MAXY - 1) + maxy = MAXY - 1; + + for (j = miny; j <= maxy; j++) + for (mm = minx; mm <= maxx; mm++) + if (know[mm][j] == 0) { + cursor(mm + 1, j + 1); + x = maxx; + while (know[x][j]) + --x; + for (i = mm; i <= x; i++) { + if ((kk = mitem[i][j]) != 0) + lprc(monstnamelist[kk]); + else + switch (kk = item[i][j]) { + case OWALL: + case 0: + case OIVTELETRAP: + case OTRAPARROWIV: + case OIVDARTRAP: + case OIVTRAPDOOR: + lprc(objnamelist[kk]); + break; + + default: + setbold(); + lprc(objnamelist[kk]); + resetbold(); + }; + know[i][j] = 1; + } + mm = maxx; + } +} + +/* + this routine shows only the spot that is given it. the spaces around + these coordinated are not shown + used in godirect() in monster.c for missile weapons display + */ +void +show1cell(int x, int y) +{ + if (c[BLINDCOUNT]) + return; /* see nothing if blind */ + cursor(x + 1, y + 1); + if ((k = mitem[x][y]) != 0) + lprc(monstnamelist[k]); + else + switch (k = item[x][y]) { + case OWALL: + case 0: + case OIVTELETRAP: + case OTRAPARROWIV: + case OIVDARTRAP: + case OIVTRAPDOOR: + lprc(objnamelist[k]); + break; + + default: + setbold(); + lprc(objnamelist[k]); + resetbold(); + }; + know[x][y] |= 1; /* we end up knowing about it */ +} + +/* + showplayer() + + subroutine to show where the player is on the screen + cursor values start from 1 up + */ +void +showplayer(void) +{ +/* 20150211 bkw: put back the player's @. Took the function body + from some old larn source originally posted to USEnet in the + paleozoic era. */ + if(oldx != 99) show1cell( oldx, oldy ); + cursor(playerx+1,playery+1); + lprc('@'); + cursor(playerx+1,playery+1); + oldx = playerx; + oldy = playery; +} + +/* + moveplayer(dir) + + subroutine to move the player from one room to another + returns 0 if can't move in that direction or hit a monster or on an object + else returns 1 + nomove is set to 1 to stop the next move (inadvertent monsters hitting + players when walking into walls) if player walks off screen or into wall + */ +short diroffx[] = {0, 0, 1, 0, -1, 1, -1, 1, -1}; +short diroffy[] = {0, 1, 0, -1, 0, -1, -1, 1, 1}; +int +moveplayer(int dir) + /* from = present room # direction = + * [1-north] [2-east] [3-south] [4-west] + * [5-northeast] [6-northwest] [7-southeast] + * [8-southwest] if direction=0, don't + * move--just show where he is */ +{ + int kk, mm, i, j; + if (c[CONFUSE]) + if (c[LEVEL] < rnd(30)) + dir = rund(9); /* if confused any dir */ + kk = playerx + diroffx[dir]; + mm = playery + diroffy[dir]; + if (kk < 0 || kk >= MAXX || mm < 0 || mm >= MAXY) { + nomove = 1; + return (yrepcount = 0); + } + i = item[kk][mm]; + j = mitem[kk][mm]; + if (i == OWALL && c[WTW] == 0) { + nomove = 1; + return (yrepcount = 0); + } /* hit a wall */ + if (kk == 33 && mm == MAXY - 1 && level == 1) { + newcavelevel(0); + for (kk = 0; kk < MAXX; kk++) + for (mm = 0; mm < MAXY; mm++) + if (item[kk][mm] == OENTRANCE) { + playerx = kk; + playery = mm; + positionplayer(); + drawscreen(); + return (0); + } + } + if (j > 0) { + hitmonster(kk, mm); + return (yrepcount = 0); + } /* hit a monster */ + lastpx = playerx; + lastpy = playery; + playerx = kk; + playery = mm; + if (i && i != OTRAPARROWIV && i != OIVTELETRAP && i != OIVDARTRAP && i != OIVTRAPDOOR) + return (yrepcount = 0); + else + return (1); +} + + +/* + * function to show what magic items have been discovered thus far + * enter with -1 for just spells, anything else will give scrolls & potions + */ +static int lincount, count; +void +seemagic(int arg) +{ + int i, number = 0; + count = lincount = 0; + nosignal = 1; + + if (arg == -1) { /* if display spells while casting one */ + for (number = i = 0; i < SPNUM; i++) + if (spelknow[i]) + number++; + number = (number + 2) / 3 + 4; /* # lines needed to display */ + cl_up(79, number); + cursor(1, 1); + } else { + resetscroll(); + clear(); + } + + lprcat("The magic spells you have discovered thus far:\n\n"); + for (i = 0; i < SPNUM; i++) + if (spelknow[i]) { + lprintf("%s %-20s ", spelcode[i], spelname[i]); + seepage(); + } + if (arg == -1) { + seepage(); + more(); + nosignal = 0; + draws(0, MAXX, 0, number); + return; + } + lincount += 3; + if (count != 0) { + count = 2; + seepage(); + } + lprcat("\nThe magic scrolls you have found to date are:\n\n"); + count = 0; + for (i = 0; i < MAXSCROLL; i++) + if (scrollname[i][0]) + if (scrollname[i][1] != ' ') { + lprintf("%-26s", &scrollname[i][1]); + seepage(); + } + lincount += 3; + if (count != 0) { + count = 2; + seepage(); + } + lprcat("\nThe magic potions you have found to date are:\n\n"); + count = 0; + for (i = 0; i < MAXPOTION; i++) + if (potionname[i][0]) + if (potionname[i][1] != ' ') { + lprintf("%-26s", &potionname[i][1]); + seepage(); + } + if (lincount != 0) + more(); + nosignal = 0; + setscroll(); + drawscreen(); +} + +/* + * subroutine to paginate the seemagic function + */ +static void +seepage(void) +{ + if (++count == 3) { + lincount++; + count = 0; + lprc('\n'); + if (lincount > 17) { + lincount = 0; + more(); + clear(); + } + } +} diff --git a/larn/extern.h b/larn/extern.h new file mode 100644 index 0000000..f0c681f --- /dev/null +++ b/larn/extern.h @@ -0,0 +1,226 @@ +/* $NetBSD: extern.h,v 1.16 2011/08/29 20:30:37 joerg Exp $ */ + +/* + * Copyright (c) 1997 Christos Zoulas. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* action.c */ +void act_remove_gems(int); +void act_sit_throne(int); +void act_drink_fountain(void); +void act_wash_fountain(void); +void act_desecrate_altar(void); +void act_donation_pray(void); +void act_just_pray(void); +void act_ignore_altar(void); +void act_open_chest(int, int); + +/* bill.c */ +__dead void mailbill(void); + +/* config.c */ + +/* create.c */ +void makeplayer(void); +void newcavelevel(int); +void eat(int, int); +int fillmonst(int); + +/* data.c */ + +/* diag.c */ +void diag(void); +int dcount(int); +void diagdrawscreen(void); +int savegame(char *); +void restoregame(char *); + +/* display.c */ +void bottomline(void); +void bottomhp(void); +void bottomspell(void); +void bottomdo(void); +void bot_linex(void); +void bottomgold(void); +void draws(int, int, int, int); +void drawscreen(void); +void showcell(int, int); +void show1cell(int, int); +void showplayer(void); +int moveplayer(int); +void seemagic(int); + +/* fortune.c */ +const char *fortune(void); + +/* global.c */ +void raiselevel(void); +void loselevel(void); +void raiseexperience(long); +void loseexperience(long); +void losehp(int); +void losemhp(int); +void raisehp(int); +void raisemhp(int); +void raisemspells(int); +void losemspells(int); +int makemonst(int); +void positionplayer(void); +void recalc(void); +void quit(void); +void more(void); +int take(int, int); +int drop_object(int); +void enchantarmor(void); +void enchweapon(void); +int pocketfull(void); +int nearbymonst(void); +int stealsomething(void); +int emptyhanded(void); +void creategem(void); +void adjustcvalues(int, int); +int getpassword(void); +int getyn(void); +int packweight(void); +int rnd(int); +int rund(int); + +/* help.c */ +void help(void); +void welcome(void); + +/* io.c */ +void setupvt100(void); +void clearvt100(void); +int ttgetch(void); +void scbr(void); +void sncbr(void); +void newgame(void); +void lprintf(const char *, ...) __printflike(1, 2); +void lprint(long); +void lwrite(char *, int); +long lgetc(void); +long larn_lrint(void); +void lrfill(char *, int); +char *lgetw(void); +char *lgetl(void); +int lcreat(char *); +int lopen(char *); +int lappend(char *); +void lrclose(void); +void lwclose(void); +void lprcat(const char *); +void cursor(int, int); +void cursors(void); +void init_term(void); +void cl_line(int, int); +void cl_up(int, int); +void cl_dn(int, int); +void standout(const char *); +void set_score_output(void); +void lflush(void); +char *tmcapcnv(char *, char *); +void beep(void); + +/* main.c */ +int main(int, char **); +void qshowstr(void); +void show3(int); +void parse2(void); +unsigned long readnum(long); +void szero(char *); + +/* monster.c */ +void createmonster(int); +void createitem(int, int); +void cast(void); +void godirect(int, int, const char *, int, int); +int vxy(int *, int *); +void hitmonster(int, int); +void hitplayer(int, int); +void dropgold(int); +void something(int); +int newobject(int, int *); +void checkloss(int); +int annihilate(void); +int newsphere(int, int, int, int); +int rmsphere(int, int); + +/* moreobj.c */ +void oaltar(void); +void othrone(int); +void odeadthrone(void); +void ochest(void); +void ofountain(void); +void fntchange(int); + +/* movem.c */ +void movemonst(void); + +/* nap.c */ +void nap(int); + +/* object.c */ +void lookforobject(void); +void oteleport(int); +void quaffpotion(int); +void adjusttime(long); +void read_scroll(int); +void readbook(int); +void iopts(void); +void ignore(void); + +/* regen.c */ +void regen(void); + +/* savelev.c */ +void savelevel(void); +void getlevel(void); + +/* scores.c */ +int makeboard(void); +int hashewon(void); +long paytaxes(long); +void showscores(void); +void showallscores(void); +void died(int); +void diedlog(void); +int getplid(char *); + +/* signal.c */ +void sigsetup(void); + +/* store.c */ +void dndstore(void); +void oschool(void); +void obank(void); +void obank2(void); +void ointerest(void); +void otradepost(void); +void olrs(void); + +/* tok.c */ +int yylex(void); +void flushall(void); +void sethard(int); +void readopts(void); diff --git a/larn/fortune.c b/larn/fortune.c new file mode 100644 index 0000000..4eb7f67 --- /dev/null +++ b/larn/fortune.c @@ -0,0 +1,94 @@ +/* $NetBSD: fortune.c,v 1.7 2009/08/12 08:04:05 dholland Exp $ */ + +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)fortune.c 5.5 (Berkeley) 6/10/91"; +#else +__RCSID("$NetBSD: fortune.c,v 1.7 2009/08/12 08:04:05 dholland Exp $"); +#endif +#endif /* not lint */ + +#include <stdlib.h> + +#include "header.h" +#include "extern.h" +/* fortune.c Larn is copyrighted 1986 by Noah Morgan. */ + +/* + * function to return a random fortune from the fortune file + */ + +static const char *flines[] = { + "gem value = gem * 2 ^ perfection", + "sitting down can have unexpected results", + "don't pry into the affairs of others", + "drinking can be hazardous to your health", + "beware of the gusher!", + "some monsters are greedy", + "nymphs have light fingers", + "try kissing a disenchantress!", + "hammers and brains don't mix", + "what does a potion of cure dianthroritis taste like?", + "hit point gain/loss when raising a level depends on constitution", + "healing a mighty wizard can be exhilarating", + "be sure to pay your taxes", + "are Vampires afraid of something?", + "some dragons can fly", + "dos thou strive for perfection?", + "patience is a virtue, unless your daughter dies", + "what does the Eye of Larn see in its guardian?", + "a level 25 player casts like crazy!", + "energy rings affect spell regeneration", + "difficulty affects regeneration", + "control of the pesty spirits is most helpful", + "don't fall into a bottomless pit", + "dexterity allows you to carry more", + "you can get 2 points of WC for the price of one", + "never enter the dungeon naked! the monsters will laugh at you!", + "did someone put itching powder in your armor?", + "you klutz!", + "avoid opening doors. you never know whats on the other side.", + "infinite regeneration ---> temptation", + "the greatest weapon in the game has not the highest Weapon Class", + "you can't buy the most powerful scroll", + "identify things before you use them", + "there's more than one way through a wall" +}; + +#define NFORTUNES 34 + +const char * +fortune(void) +{ + return (flines[random() % NFORTUNES]); +} diff --git a/larn/global.c b/larn/global.c new file mode 100644 index 0000000..013aa6f --- /dev/null +++ b/larn/global.c @@ -0,0 +1,861 @@ +/* $NetBSD: global.c,v 1.14 2012/06/19 05:30:43 dholland Exp $ */ + +/* + * global.c Larn is copyrighted 1986 by Noah Morgan. + * + * raiselevel() subroutine to raise the player one level + * loselevel() subroutine to lower the player by one level + * raiseexperience(x) subroutine to increase experience points + * loseexperience(x) subroutine to lose experience points + * losehp(x) subroutine to remove hit points from the player + * losemhp(x) subroutine to remove max # hit points from the player + * raisehp(x) subroutine to gain hit points + * raisemhp(x) subroutine to gain maximum hit points + * losemspells(x) subroutine to lose maximum spells + * raisemspells(x) subroutine to gain maximum spells + * makemonst(lev) function to return monster number for a randomly + * selected monster + * positionplayer() function to be sure player is not in a wall + * recalc() function to recalculate the armor class of the player + * quit() subroutine to ask if the player really wants to quit + */ +#include <sys/cdefs.h> +#ifndef lint +__RCSID("$NetBSD: global.c,v 1.14 2012/06/19 05:30:43 dholland Exp $"); +#endif /* not lint */ + +#include <string.h> +#include <unistd.h> +#include "header.h" +#include "extern.h" +extern int score[], dropflag; +extern char *what[], *who[]; +extern char winner[]; +extern char sciv[SCORESIZE + 1][26][2]; +extern const char *password; + +/* + raiselevel() + + subroutine to raise the player one level + uses the skill[] array to find level boundarys + uses c[EXPERIENCE] c[LEVEL] + */ +void +raiselevel(void) +{ + if (c[LEVEL] < MAXPLEVEL) + raiseexperience((long) (skill[c[LEVEL]] - c[EXPERIENCE])); +} + +/* + loselevel() + + subroutine to lower the players character level by one + */ +void +loselevel(void) +{ + if (c[LEVEL] > 1) + loseexperience((long) (c[EXPERIENCE] - skill[c[LEVEL] - 1] + 1)); +} + +/* + raiseexperience(x) + + subroutine to increase experience points + */ +void +raiseexperience(long x) +{ + int i, tmp; + i = c[LEVEL]; + c[EXPERIENCE] += x; + while (c[EXPERIENCE] >= skill[c[LEVEL]] && (c[LEVEL] < MAXPLEVEL)) { + tmp = (c[CONSTITUTION] - c[HARDGAME]) >> 1; + c[LEVEL]++; + raisemhp((int) (rnd(3) + rnd((tmp > 0) ? tmp : 1))); + raisemspells((int) rund(3)); + if (c[LEVEL] < 7 - c[HARDGAME]) + raisemhp((int) (c[CONSTITUTION] >> 2)); + } + if (c[LEVEL] != i) { + cursors(); + beep(); + lprintf("\nWelcome to level %ld", (long) c[LEVEL]); /* if we changed levels */ + } + bottomline(); +} + +/* + loseexperience(x) + + subroutine to lose experience points + */ +void +loseexperience(long x) +{ + int i, tmp; + i = c[LEVEL]; + c[EXPERIENCE] -= x; + if (c[EXPERIENCE] < 0) + c[EXPERIENCE] = 0; + while (c[EXPERIENCE] < skill[c[LEVEL] - 1]) { + if (--c[LEVEL] <= 1) + c[LEVEL] = 1; /* down one level */ + tmp = (c[CONSTITUTION] - c[HARDGAME]) >> 1; /* lose hpoints */ + losemhp((int) rnd((tmp > 0) ? tmp : 1)); /* lose hpoints */ + if (c[LEVEL] < 7 - c[HARDGAME]) + losemhp((int) (c[CONSTITUTION] >> 2)); + losemspells((int) rund(3)); /* lose spells */ + } + if (i != c[LEVEL]) { + cursors(); + beep(); + lprintf("\nYou went down to level %ld!", (long) c[LEVEL]); + } + bottomline(); +} + +/* + losehp(x) + losemhp(x) + + subroutine to remove hit points from the player + warning -- will kill player if hp goes to zero + */ +void +losehp(int x) +{ + if ((c[HP] -= x) <= 0) { + beep(); + lprcat("\n"); + nap(3000); + died(lastnum); + } +} + +void +losemhp(int x) +{ + c[HP] -= x; + if (c[HP] < 1) + c[HP] = 1; + c[HPMAX] -= x; + if (c[HPMAX] < 1) + c[HPMAX] = 1; +} + +/* + raisehp(x) + raisemhp(x) + + subroutine to gain maximum hit points + */ +void +raisehp(int x) +{ + if ((c[HP] += x) > c[HPMAX]) + c[HP] = c[HPMAX]; +} + +void +raisemhp(int x) +{ + c[HPMAX] += x; + c[HP] += x; +} + +/* + raisemspells(x) + + subroutine to gain maximum spells + */ +void +raisemspells(int x) +{ + c[SPELLMAX] += x; + c[SPELLS] += x; +} + +/* + losemspells(x) + + subroutine to lose maximum spells + */ +void +losemspells(int x) +{ + if ((c[SPELLMAX] -= x) < 0) + c[SPELLMAX] = 0; + if ((c[SPELLS] -= x) < 0) + c[SPELLS] = 0; +} + +/* + makemonst(lev) + int lev; + + function to return monster number for a randomly selected monster + for the given cave level + */ +int +makemonst(int lev) +{ + int tmp, x; + if (lev < 1) + lev = 1; + if (lev > 12) + lev = 12; + tmp = WATERLORD; + if (lev < 5) + while (tmp == WATERLORD) + tmp = rnd((x = monstlevel[lev - 1]) ? x : 1); + else + while (tmp == WATERLORD) + tmp = rnd((x = monstlevel[lev - 1] - monstlevel[lev - 4]) ? x : 1) + monstlevel[lev - 4]; + + while (monster[tmp].genocided && tmp < MAXMONST) + tmp++; /* genocided? */ + return (tmp); +} + +/* + positionplayer() + + function to be sure player is not in a wall + */ +void +positionplayer(void) +{ + int try; + try = 2; + while ((item[playerx][playery] || mitem[playerx][playery]) && (try)) + if (++playerx >= MAXX - 1) { + playerx = 1; + if (++playery >= MAXY - 1) { + playery = 1; + --try; + } + } + if (try == 0) + lprcat("Failure in positionplayer\n"); +} + +/* + recalc() function to recalculate the armor class of the player + */ +void +recalc(void) +{ + int i, j, k; + c[AC] = c[MOREDEFENSES]; + if (c[WEAR] >= 0) + switch (iven[c[WEAR]]) { + case OSHIELD: + c[AC] += 2 + ivenarg[c[WEAR]]; + break; + case OLEATHER: + c[AC] += 2 + ivenarg[c[WEAR]]; + break; + case OSTUDLEATHER: + c[AC] += 3 + ivenarg[c[WEAR]]; + break; + case ORING: + c[AC] += 5 + ivenarg[c[WEAR]]; + break; + case OCHAIN: + c[AC] += 6 + ivenarg[c[WEAR]]; + break; + case OSPLINT: + c[AC] += 7 + ivenarg[c[WEAR]]; + break; + case OPLATE: + c[AC] += 9 + ivenarg[c[WEAR]]; + break; + case OPLATEARMOR: + c[AC] += 10 + ivenarg[c[WEAR]]; + break; + case OSSPLATE: + c[AC] += 12 + ivenarg[c[WEAR]]; + break; + } + + if (c[SHIELD] >= 0) + if (iven[c[SHIELD]] == OSHIELD) + c[AC] += 2 + ivenarg[c[SHIELD]]; + if (c[WIELD] < 0) + c[WCLASS] = 0; + else { + i = ivenarg[c[WIELD]]; + switch (iven[c[WIELD]]) { + case ODAGGER: + c[WCLASS] = 3 + i; + break; + case OBELT: + c[WCLASS] = 7 + i; + break; + case OSHIELD: + c[WCLASS] = 8 + i; + break; + case OSPEAR: + c[WCLASS] = 10 + i; + break; + case OFLAIL: + c[WCLASS] = 14 + i; + break; + case OBATTLEAXE: + c[WCLASS] = 17 + i; + break; + case OLANCE: + c[WCLASS] = 19 + i; + break; + case OLONGSWORD: + c[WCLASS] = 22 + i; + break; + case O2SWORD: + c[WCLASS] = 26 + i; + break; + case OSWORD: + c[WCLASS] = 32 + i; + break; + case OSWORDofSLASHING: + c[WCLASS] = 30 + i; + break; + case OHAMMER: + c[WCLASS] = 35 + i; + break; + default: + c[WCLASS] = 0; + } + } + c[WCLASS] += c[MOREDAM]; + + /* now for regeneration abilities based on rings */ + c[REGEN] = 1; + c[ENERGY] = 0; + j = 0; + for (k = 25; k > 0; k--) + if (iven[k]) { + j = k; + k = 0; + } + for (i = 0; i <= j; i++) { + switch (iven[i]) { + case OPROTRING: + c[AC] += ivenarg[i] + 1; + break; + case ODAMRING: + c[WCLASS] += ivenarg[i] + 1; + break; + case OBELT: + c[WCLASS] += ((ivenarg[i] << 1)) + 2; + break; + + case OREGENRING: + c[REGEN] += ivenarg[i] + 1; + break; + case ORINGOFEXTRA: + c[REGEN] += 5 * (ivenarg[i] + 1); + break; + case OENERGYRING: + c[ENERGY] += ivenarg[i] + 1; + break; + } + } +} + + +/* + quit() + + subroutine to ask if the player really wants to quit + */ +void +quit(void) +{ + int i; + cursors(); + strcpy(lastmonst, ""); + lprcat("\n\nDo you really want to quit?"); + while (1) { + i = ttgetch(); + if (i == 'y') { + died(300); + return; + } + if ((i == 'n') || (i == '\33')) { + lprcat(" no"); + lflush(); + return; + } + lprcat("\n"); + setbold(); + lprcat("Yes"); + resetbold(); + lprcat(" or "); + setbold(); + lprcat("No"); + resetbold(); + lprcat(" please? Do you want to quit? "); + } +} + +/* + function to ask --more-- then the user must enter a space + */ +void +more(void) +{ + lprcat("\n --- press "); + standout("space"); + lprcat(" to continue --- "); + while (ttgetch() != ' '); +} + +/* + function to put something in the players inventory + returns 0 if success, 1 if a failure + */ +int +take(int theitem, int arg) +{ + int i, limit; + /* cursors(); */ + if ((limit = 15 + (c[LEVEL] >> 1)) > 26) + limit = 26; + for (i = 0; i < limit; i++) + if (iven[i] == 0) { + iven[i] = theitem; + ivenarg[i] = arg; + limit = 0; + switch (theitem) { + case OPROTRING: + case ODAMRING: + case OBELT: + limit = 1; + break; + case ODEXRING: + c[DEXTERITY] += ivenarg[i] + 1; + limit = 1; + break; + case OSTRRING: + c[STREXTRA] += ivenarg[i] + 1; + limit = 1; + break; + case OCLEVERRING: + c[INTELLIGENCE] += ivenarg[i] + 1; + limit = 1; + break; + case OHAMMER: + c[DEXTERITY] += 10; + c[STREXTRA] += 10; + c[INTELLIGENCE] -= 10; + limit = 1; + break; + + case OORBOFDRAGON: + c[SLAYING]++; + break; + case OSPIRITSCARAB: + c[NEGATESPIRIT]++; + break; + case OCUBEofUNDEAD: + c[CUBEofUNDEAD]++; + break; + case ONOTHEFT: + c[NOTHEFT]++; + break; + case OSWORDofSLASHING: + c[DEXTERITY] += 5; + limit = 1; + break; + }; + lprcat("\nYou pick up:"); + srcount = 0; + show3(i); + if (limit) + bottomline(); + return (0); + } + lprcat("\nYou can't carry anything else"); + return (1); +} + +/* + subroutine to drop an object + returns 1 if something there already else 0 + */ +int +drop_object(int k) +{ + int theitem; + if ((k < 0) || (k > 25)) + return (0); + theitem = iven[k]; + cursors(); + if (theitem == 0) { + lprintf("\nYou don't have item %c! ", k + 'a'); + return (1); + } + if (item[playerx][playery]) { + beep(); + lprcat("\nThere's something here already"); + return (1); + } + if (playery == MAXY - 1 && playerx == 33) + return (1); /* not in entrance */ + item[playerx][playery] = theitem; + iarg[playerx][playery] = ivenarg[k]; + srcount = 0; + lprcat("\n You drop:"); + show3(k); /* show what item you dropped */ + know[playerx][playery] = 0; + iven[k] = 0; + if (c[WIELD] == k) + c[WIELD] = -1; + if (c[WEAR] == k) + c[WEAR] = -1; + if (c[SHIELD] == k) + c[SHIELD] = -1; + adjustcvalues(theitem, ivenarg[k]); + dropflag = 1; /* say dropped an item so wont ask to pick it + * up right away */ + return (0); +} + +/* + function to enchant armor player is currently wearing + */ +void +enchantarmor(void) +{ + int tmp; + if (c[WEAR] < 0) { + if (c[SHIELD] < 0) { + cursors(); + beep(); + lprcat("\nYou feel a sense of loss"); + return; + } else { + tmp = iven[c[SHIELD]]; + if (tmp != OSCROLL) + if (tmp != OPOTION) { + ivenarg[c[SHIELD]]++; + bottomline(); + } + } + } + tmp = iven[c[WEAR]]; + if (tmp != OSCROLL) + if (tmp != OPOTION) { + ivenarg[c[WEAR]]++; + bottomline(); + } +} + +/* + function to enchant a weapon presently being wielded + */ +void +enchweapon(void) +{ + int tmp; + if (c[WIELD] < 0) { + cursors(); + beep(); + lprcat("\nYou feel a sense of loss"); + return; + } + tmp = iven[c[WIELD]]; + if (tmp != OSCROLL) + if (tmp != OPOTION) { + ivenarg[c[WIELD]]++; + if (tmp == OCLEVERRING) + c[INTELLIGENCE]++; + else if (tmp == OSTRRING) + c[STREXTRA]++; + else if (tmp == ODEXRING) + c[DEXTERITY]++; + bottomline(); + } +} + +/* + routine to tell if player can carry one more thing + returns 1 if pockets are full, else 0 + */ +int +pocketfull(void) +{ + int i, limit; + if ((limit = 15 + (c[LEVEL] >> 1)) > 26) + limit = 26; + for (i = 0; i < limit; i++) + if (iven[i] == 0) + return (0); + return (1); +} + +/* + function to return 1 if a monster is next to the player else returns 0 + */ +int +nearbymonst(void) +{ + int tmp, tmp2; + for (tmp = playerx - 1; tmp < playerx + 2; tmp++) + for (tmp2 = playery - 1; tmp2 < playery + 2; tmp2++) + if (mitem[tmp][tmp2]) + return (1); /* if monster nearby */ + return (0); +} + +/* + function to steal an item from the players pockets + returns 1 if steals something else returns 0 + */ +int +stealsomething(void) +{ + int i, j; + j = 100; + while (1) { + i = rund(26); + if (iven[i]) + if (c[WEAR] != i) + if (c[WIELD] != i) + if (c[SHIELD] != i) { + srcount = 0; + show3(i); + adjustcvalues(iven[i], ivenarg[i]); + iven[i] = 0; + return (1); + } + if (--j <= 0) + return (0); + } +} + +/* + function to return 1 is player carrys nothing else return 0 + */ +int +emptyhanded(void) +{ + int i; + for (i = 0; i < 26; i++) + if (iven[i]) + if (i != c[WIELD]) + if (i != c[WEAR]) + if (i != c[SHIELD]) + return (0); + return (1); +} + +/* + function to create a gem on a square near the player + */ +void +creategem(void) +{ + int i, j; + switch (rnd(4)) { + case 1: + i = ODIAMOND; + j = 50; + break; + case 2: + i = ORUBY; + j = 40; + break; + case 3: + i = OEMERALD; + j = 30; + break; + default: + i = OSAPPHIRE; + j = 20; + break; + }; + createitem(i, rnd(j) + j / 10); +} + +/* + function to change character levels as needed when dropping an object + that affects these characteristics + */ +void +adjustcvalues(int theitem, int arg) +{ + int flag; + flag = 0; + switch (theitem) { + case ODEXRING: + c[DEXTERITY] -= arg + 1; + flag = 1; + break; + case OSTRRING: + c[STREXTRA] -= arg + 1; + flag = 1; + break; + case OCLEVERRING: + c[INTELLIGENCE] -= arg + 1; + flag = 1; + break; + case OHAMMER: + c[DEXTERITY] -= 10; + c[STREXTRA] -= 10; + c[INTELLIGENCE] += 10; + flag = 1; + break; + case OSWORDofSLASHING: + c[DEXTERITY] -= 5; + flag = 1; + break; + case OORBOFDRAGON: + --c[SLAYING]; + return; + case OSPIRITSCARAB: + --c[NEGATESPIRIT]; + return; + case OCUBEofUNDEAD: + --c[CUBEofUNDEAD]; + return; + case ONOTHEFT: + --c[NOTHEFT]; + return; + case OLANCE: + c[LANCEDEATH] = 0; + return; + case OPOTION: + case OSCROLL: + return; + + default: + flag = 1; + }; + if (flag) + bottomline(); +} + +/* + function to ask user for a password (no echo) + returns 1 if entered correctly, 0 if not + */ +static char gpwbuf[33]; +int +getpassword(void) +{ + int i, j; + char *gpwp; + scbr(); /* system("stty -echo cbreak"); */ + gpwp = gpwbuf; + lprcat("\nEnter Password: "); + lflush(); + i = strlen(password); + for (j = 0; j < i; j++) + *gpwp++ = ttgetch(); + gpwbuf[i] = 0; + sncbr(); /* system("stty echo -cbreak"); */ + if (strcmp(gpwbuf, password) != 0) { + lprcat("\nSorry\n"); + lflush(); + return (0); + } else + return (1); +} + +/* + subroutine to get a yes or no response from the user + returns y or n + */ +int +getyn(void) +{ + int i; + i = 0; + while (i != 'y' && i != 'n' && i != '\33') + i = ttgetch(); + return (i); +} + +/* + function to calculate the pack weight of the player + returns the number of pounds the player is carrying + */ +int +packweight(void) +{ + int i, j, k; + k = c[GOLD] / 1000; + j = 25; + while ((iven[j] == 0) && (j > 0)) + --j; + for (i = 0; i <= j; i++) + switch (iven[i]) { + case 0: + break; + case OSSPLATE: + case OPLATEARMOR: + k += 40; + break; + case OPLATE: + k += 35; + break; + case OHAMMER: + k += 30; + break; + case OSPLINT: + k += 26; + break; + case OSWORDofSLASHING: + case OCHAIN: + case OBATTLEAXE: + case O2SWORD: + k += 23; + break; + case OLONGSWORD: + case OSWORD: + case ORING: + case OFLAIL: + k += 20; + break; + case OLANCE: + case OSTUDLEATHER: + k += 15; + break; + case OLEATHER: + case OSPEAR: + k += 8; + break; + case OORBOFDRAGON: + case OBELT: + k += 4; + break; + case OSHIELD: + k += 7; + break; + case OCHEST: + k += 30 + ivenarg[i]; + break; + default: + k++; + }; + return (k); +} + +#ifndef MACRORND +/* macros to generate random numbers 1<=rnd(N)<=N 0<=rund(N)<=N-1 */ +int +rnd(int x) +{ + return ((((randx = randx * 1103515245 + 12345) >> 7) % (x)) + 1); +} + +int +rund(int x) +{ + return ((((randx = randx * 1103515245 + 12345) >> 7) % (x))); +} +#endif /* MACRORND */ diff --git a/larn/header.h b/larn/header.h new file mode 100644 index 0000000..121a472 --- /dev/null +++ b/larn/header.h @@ -0,0 +1,444 @@ +/* $NetBSD: header.h,v 1.22 2008/08/29 00:37:38 gmcgarry Exp $ */ + +/* header.h Larn is copyrighted 1986 by Noah Morgan. */ + +#include <sys/types.h> + +#define MAXLEVEL 11 +/* max # levels in the dungeon */ +#define MAXVLEVEL 3 +/* max # of levels in the temple of the luran */ +#define MAXX 67 +#define MAXY 17 + +#define SCORESIZE 10 +/* this is the number of people on a scoreboard max */ +#define MAXPLEVEL 100 +/* maximum player level allowed */ +#define MAXMONST 56 +/* maximum # monsters in the dungeon */ +#define SPNUM 38 +/* maximum number of spells in existence */ +#define MAXSCROLL 28 +/* maximum number of scrolls that are possible */ +#define MAXPOTION 35 +/* maximum number of potions that are possible */ +#define TIMELIMIT 30000 +/* the maximum number of moves before the game is called */ +#define TAXRATE 1/20 +/* the tax rate for the LRS */ +#define MAXOBJ 93 +/* the maximum number of objects n < MAXOBJ */ + +/* this is the structure definition of the monster data */ +struct monst { + const char *name; + char level; + short armorclass; + char damage; + char attack; + char defense; + char genocided; + char intelligence; /* monsters intelligence -- used to + * choose movement */ + short gold; + short hitpoints; + unsigned long experience; +}; + +/* this is the structure definition for the items in the dnd store */ +struct _itm { + short price; + u_char obj; + u_char arg; + char qty; +}; + +/* this is the structure that holds the entire dungeon specifications */ +struct cel { + short hitp; /* monster's hit points */ + char mitem; /* the monster ID */ + char item; /* the object's ID */ + short iarg; /* the object's argument */ + char know; /* have we been here before */ +}; + +/* this is the structure for maintaining & moving the spheres of annihilation */ +struct sphere { + struct sphere *p; /* pointer to next structure */ + char x, y, lev; /* location of the sphere */ + char dir; /* direction sphere is going in */ + short lifetime; /* duration of the sphere */ +}; + +/* defines for the character attribute array c[] */ +#define STRENGTH 0 /* characters physical strength not due to + * objects */ +#define INTELLIGENCE 1 +#define WISDOM 2 +#define CONSTITUTION 3 +#define DEXTERITY 4 +#define CHARISMA 5 +#define HPMAX 6 +#define HP 7 +#define GOLD 8 +#define EXPERIENCE 9 +#define LEVEL 10 +#define REGEN 11 +#define WCLASS 12 +#define AC 13 +#define BANKACCOUNT 14 +#define SPELLMAX 15 +#define SPELLS 16 +#define ENERGY 17 +#define ECOUNTER 18 +#define MOREDEFENSES 19 +#define WEAR 20 +#define PROTECTIONTIME 21 +#define WIELD 22 +#define AMULET 23 +#define REGENCOUNTER 24 +#define MOREDAM 25 +#define DEXCOUNT 26 +#define STRCOUNT 27 +#define BLINDCOUNT 28 +#define CAVELEVEL 29 +#define CONFUSE 30 +#define ALTPRO 31 +#define HERO 32 +#define CHARMCOUNT 33 +#define INVISIBILITY 34 +#define CANCELLATION 35 +#define HASTESELF 36 +#define EYEOFLARN 37 +#define AGGRAVATE 38 +#define GLOBE 39 +#define TELEFLAG 40 +#define SLAYING 41 +#define NEGATESPIRIT 42 +#define SCAREMONST 43 +#define AWARENESS 44 +#define HOLDMONST 45 +#define TIMESTOP 46 +#define HASTEMONST 47 +#define CUBEofUNDEAD 48 +#define GIANTSTR 49 +#define FIRERESISTANCE 50 +#define BESSMANN 51 +#define NOTHEFT 52 +#define HARDGAME 53 +#define CPUTIME 54 +#define BYTESIN 55 +#define BYTESOUT 56 +#define MOVESMADE 57 +#define MONSTKILLED 58 +#define SPELLSCAST 59 +#define LANCEDEATH 60 +#define SPIRITPRO 61 +#define UNDEADPRO 62 +#define SHIELD 63 +#define STEALTH 64 +#define ITCHING 65 +#define LAUGHING 66 +#define DRAINSTRENGTH 67 +#define CLUMSINESS 68 +#define INFEEBLEMENT 69 +#define HALFDAM 70 +#define SEEINVISIBLE 71 +#define FILLROOM 72 +#define RANDOMWALK 73 +#define SPHCAST 74 /* nz if an active sphere of annihilation */ +#define WTW 75 /* walk through walls */ +#define STREXTRA 76 /* character strength due to objects or + * enchantments */ +#define TMP 77 /* misc scratch space */ +#define LIFEPROT 78 /* life protection counter */ + +/* defines for the objects in the game */ + +#define OALTAR 1 +#define OTHRONE 2 +#define OORB 3 +#define OPIT 4 +#define OSTAIRSUP 5 +#define OELEVATORUP 6 +#define OFOUNTAIN 7 +#define OSTATUE 8 +#define OTELEPORTER 9 +#define OSCHOOL 10 +#define OMIRROR 11 +#define ODNDSTORE 12 +#define OSTAIRSDOWN 13 +#define OELEVATORDOWN 14 +#define OBANK2 15 +#define OBANK 16 +#define ODEADFOUNTAIN 17 +#define OMAXGOLD 70 +#define OGOLDPILE 18 +#define OOPENDOOR 19 +#define OCLOSEDDOOR 20 +#define OWALL 21 +#define OTRAPARROW 66 +#define OTRAPARROWIV 67 + +#define OLARNEYE 22 + +#define OPLATE 23 +#define OCHAIN 24 +#define OLEATHER 25 +#define ORING 60 +#define OSTUDLEATHER 61 +#define OSPLINT 62 +#define OPLATEARMOR 63 +#define OSSPLATE 64 +#define OSHIELD 68 +#define OELVENCHAIN 92 + +#define OSWORDofSLASHING 26 +#define OHAMMER 27 +#define OSWORD 28 +#define O2SWORD 29 +#define OSPEAR 30 +#define ODAGGER 31 +#define OBATTLEAXE 57 +#define OLONGSWORD 58 +#define OFLAIL 59 +#define OLANCE 65 +#define OVORPAL 90 +#define OSLAYER 91 + +#define ORINGOFEXTRA 32 +#define OREGENRING 33 +#define OPROTRING 34 +#define OENERGYRING 35 +#define ODEXRING 36 +#define OSTRRING 37 +#define OCLEVERRING 38 +#define ODAMRING 39 + +#define OBELT 40 + +#define OSCROLL 41 +#define OPOTION 42 +#define OBOOK 43 +#define OCHEST 44 +#define OAMULET 45 + +#define OORBOFDRAGON 46 +#define OSPIRITSCARAB 47 +#define OCUBEofUNDEAD 48 +#define ONOTHEFT 49 + +#define ODIAMOND 50 +#define ORUBY 51 +#define OEMERALD 52 +#define OSAPPHIRE 53 + +#define OENTRANCE 54 +#define OVOLDOWN 55 +#define OVOLUP 56 +#define OHOME 69 + +#define OKGOLD 71 +#define ODGOLD 72 +#define OIVDARTRAP 73 +#define ODARTRAP 74 +#define OTRAPDOOR 75 +#define OIVTRAPDOOR 76 +#define OTRADEPOST 77 +#define OIVTELETRAP 78 +#define ODEADTHRONE 79 +#define OANNIHILATION 80 /* sphere of annihilation */ +#define OTHRONE2 81 +#define OLRS 82 /* Larn Revenue Service */ +#define OCOOKIE 83 +#define OURN 84 +#define OBRASSLAMP 85 +#define OHANDofFEAR 86 /* hand of fear */ +#define OSPHTAILSMAN 87 /* tailsman of the sphere */ +#define OWWAND 88 /* wand of wonder */ +#define OPSTAFF 89 /* staff of power */ +/* used up to 92 */ + +/* defines for the monsters as objects */ + +#define BAT 1 +#define GNOME 2 +#define HOBGOBLIN 3 +#define JACKAL 4 +#define KOBOLD 5 +#define ORC 6 +#define SNAKE 7 +#define CENTIPEDE 8 +#define JACULI 9 +#define TROGLODYTE 10 +#define ANT 11 +#define EYE 12 +#define LEPRECHAUN 13 +#define NYMPH 14 +#define QUASIT 15 +#define RUSTMONSTER 16 +#define ZOMBIE 17 +#define ASSASSINBUG 18 +#define BUGBEAR 19 +#define HELLHOUND 20 +#define ICELIZARD 21 +#define CENTAUR 22 +#define TROLL 23 +#define YETI 24 +#define WHITEDRAGON 25 +#define ELF 26 +#define CUBE 27 +#define METAMORPH 28 +#define VORTEX 29 +#define ZILLER 30 +#define VIOLETFUNGI 31 +#define WRAITH 32 +#define FORVALAKA 33 +#define LAMANOBE 34 +#define OSEQUIP 35 +#define ROTHE 36 +#define XORN 37 +#define VAMPIRE 38 +#define INVISIBLESTALKER 39 +#define POLTERGEIST 40 +#define DISENCHANTRESS 41 +#define SHAMBLINGMOUND 42 +#define YELLOWMOLD 43 +#define UMBERHULK 44 +#define GNOMEKING 45 +#define MIMIC 46 +#define WATERLORD 47 +#define BRONZEDRAGON 48 +#define GREENDRAGON 49 +#define PURPLEWORM 50 +#define XVART 51 +#define SPIRITNAGA 52 +#define SILVERDRAGON 53 +#define PLATINUMDRAGON 54 +#define GREENURCHIN 55 +#define REDDRAGON 56 +#define DEMONLORD 57 +#define DEMONPRINCE 64 + +#ifndef NULL +#define NULL 0 +#endif +#define BUFBIG 4096 /* size of the output buffer */ +#define MAXIBUF 4096 /* size of the input buffer */ +#define LOGNAMESIZE 40 /* max size of the players name */ +#define PSNAMESIZE 40 /* max size of the process name */ + +#ifndef NODEFS +extern char VERSION, SUBVERSION; +extern u_char beenhere[], boldon, cheat, ckpflag; +extern const char *class[]; +extern u_char course[]; +extern char diagfile[], helpfile[], ckpfile[], larnlevels[], + playerids[], optsfile[1024], psname[], savefilename[], + scorefile[]; +extern u_char *inbuffer; +extern u_char item[MAXX][MAXY], iven[], know[MAXX][MAXY]; +extern const char *levelname[]; +extern char logfile[], loginname[], logname[], lastmonst[]; +extern u_char *lpbuf, *lpend; +extern u_char *lpnt, moved[MAXX][MAXY], mitem[MAXX][MAXY], monstlevel[]; +extern char monstnamelist[], objnamelist[]; +extern u_char nch[], ndgg[], nlpts[], nomove, nosignal, nowelcome; +extern u_char nplt[], nsw[]; +extern const char *objectname[]; +extern const char *potionhide[], *potionname[]; +extern const char *spelcode[], *spelname[], *spelmes[]; +extern char aborted[], spelweird[MAXMONST + 8][SPNUM]; +extern u_char potprob[]; +extern u_char predostuff, restorflag, scprob[]; +extern u_char screen[MAXX][MAXY], sex; +extern const char *speldescript[]; +extern const char *scrollhide[], *scrollname[]; +extern u_char spelknow[]; +extern u_char splev[], stealth[MAXX][MAXY], wizard; +extern short diroffx[], diroffy[], hitflag, hit2flag, hit3flag, hitp[MAXX][MAXY]; +extern short iarg[MAXX][MAXY], ivenarg[], lasthx, lasthy, lastnum, lastpx, + lastpy; +extern short nobeep, oldx, oldy, playerx, playery, level; +extern int enable_scroll, srcount, yrepcount, userid, wisid, + io_outfd, io_infd; +extern gid_t gid, egid; +extern long outstanding_taxes, skill[], gltime, c[], cbak[]; +extern time_t initialtime; +extern unsigned long randx; +extern struct cel *cell; +extern struct monst monster[]; +extern struct sphere *spheres; +extern struct _itm itm[]; +extern int rmst, lasttime; + +/* macro to create scroll #'s with probability of occurrence */ +#define newscroll() (scprob[rund(81)]) +/* macro to return a potion # created with probability of occurrence */ +#define newpotion() (potprob[rund(41)]) +/* macro to return the + points on created leather armor */ +#define newleather() (nlpts[rund(c[HARDGAME]?13:15)]) +/* macro to return the + points on chain armor */ +#define newchain() (nch[rund(10)]) +/* macro to return + points on plate armor */ +#define newplate() (nplt[rund(c[HARDGAME]?4:12)]) +/* macro to return + points on new daggers */ +#define newdagger() (ndgg[rund(13)]) +/* macro to return + points on new swords */ +#define newsword() (nsw[rund(c[HARDGAME]?6:13)]) +/* macro to destroy object at present location */ +#define forget() (item[playerx][playery]=know[playerx][playery]=0) +/* macro to wipe out a monster at a location */ +#define disappear(x,y) (mitem[x][y]=know[x][y]=0) + +#ifdef VT100 +/* macro to turn on bold display for the terminal */ +#define setbold() (lprcat(boldon?"\33[1m":"\33[7m")) +/* macro to turn off bold display for the terminal */ +#define resetbold() (lprcat("\33[m")) +/* macro to setup the scrolling region for the terminal */ +#define setscroll() (lprcat("\33[20;24r")) +/* macro to clear the scrolling region for the terminal */ +#define resetscroll() (lprcat("\33[;24r")) +/* macro to clear the screen and home the cursor */ +#define clear() (lprcat("\33[2J\33[f"), cbak[SPELLS]= -50) +#define cltoeoln() lprcat("\33[K") +#else /* VT100 */ +/* defines below are for use in the termcap mode only */ +#define ST_START 1 +#define ST_END 2 +#define BOLD 3 +#define END_BOLD 4 +#define CLEAR 5 +#define CL_LINE 6 +#define CL_DOWN 14 +#define CURSOR 15 +/* macro to turn on bold display for the terminal */ +#define setbold() (*lpnt++ = ST_START) +/* macro to turn off bold display for the terminal */ +#define resetbold() (*lpnt++ = ST_END) +/* macro to setup the scrolling region for the terminal */ +#define setscroll() enable_scroll=1 +/* macro to clear the scrolling region for the terminal */ +#define resetscroll() enable_scroll=0 +/* macro to clear the screen and home the cursor */ +#define clear() (*lpnt++ =CLEAR, cbak[SPELLS]= -50) +/* macro to clear to end of line */ +#define cltoeoln() (*lpnt++ = CL_LINE) +#endif /* VT100 */ + +/* macro to output one byte to the output buffer */ +#define lprc(ch) ((lpnt>=lpend)?(void)(*lpnt++ = (ch), lflush()):(void)(*lpnt++ = (ch))) + +/* macro to seed the random number generator */ +#define seedrand(x) (randx=x) +#ifdef MACRORND +/* macros to generate random numbers 1<=rnd(N)<=N 0<=rund(N)<=N-1 */ +#define rnd(x) ((((randx=randx*1103515245+12345)>>7)%(x))+1) +#define rund(x) ((((randx=randx*1103515245+12345)>>7)%(x)) ) +#endif /* MACRORND */ +/* macros for miscellaneous data conversion */ +#define min(x,y) (((x)>(y))?(y):(x)) +#define max(x,y) (((x)>(y))?(x):(y)) +#endif /* NODEFS */ diff --git a/larn/help.c b/larn/help.c new file mode 100644 index 0000000..477052c --- /dev/null +++ b/larn/help.c @@ -0,0 +1,129 @@ +/* $NetBSD: help.c,v 1.9 2012/06/19 05:30:43 dholland Exp $ */ + +/* help.c Larn is copyrighted 1986 by Noah Morgan. */ +#include <sys/cdefs.h> +#ifndef lint +__RCSID("$NetBSD: help.c,v 1.9 2012/06/19 05:30:43 dholland Exp $"); +#endif /* not lint */ + +#include <unistd.h> + +#include "header.h" +#include "extern.h" + +static void retcont(void); +static int openhelp(void); + +/* + * help function to display the help info + * + * format of the .larn.help file + * + * 1st character of file: # of pages of help available (ascii digit) + * page (23 lines) for the introductory message (not counted in above) + * pages of help text (23 lines per page) + */ +void +help(void) +{ + int i, j; +#ifndef VT100 + char tmbuf[128]; /* intermediate translation buffer + * when not a VT100 */ +#endif /* VT100 */ + if ((j = openhelp()) < 0) + return; /* open the help file and get # pages */ + for (i = 0; i < 23; i++) + lgetl(); /* skip over intro message */ + for (; j > 0; j--) { + clear(); + for (i = 0; i < 23; i++) +#ifdef VT100 + lprcat(lgetl()); /* print out each line that + * we read in */ +#else /* VT100 */ + { + tmcapcnv(tmbuf, lgetl()); + lprcat(tmbuf); + } /* intercept \33's */ +#endif /* VT100 */ + if (j > 1) { + lprcat(" ---- Press "); + standout("return"); + lprcat(" to exit, "); + standout("space"); + lprcat(" for more help ---- "); + i = 0; + while ((i != ' ') && (i != '\n') && (i != '\33')) + i = ttgetch(); + if ((i == '\n') || (i == '\33')) { + lrclose(); + setscroll(); + drawscreen(); + return; + } + } + } + lrclose(); + retcont(); + drawscreen(); +} + +/* + * function to display the welcome message and background + */ +void +welcome(void) +{ + int i; +#ifndef VT100 + char tmbuf[128]; /* intermediate translation buffer + * when not a VT100 */ +#endif /* VT100 */ + if (openhelp() < 0) + return; /* open the help file */ + clear(); + for (i = 0; i < 23; i++) +#ifdef VT100 + lprcat(lgetl());/* print out each line that we read in */ +#else /* VT100 */ + { + tmcapcnv(tmbuf, lgetl()); + lprcat(tmbuf); + } /* intercept \33's */ +#endif /* VT100 */ + lrclose(); + retcont(); /* press return to continue */ +} + +/* + * function to say press return to continue and reset scroll when done + */ +static void +retcont(void) +{ + cursor(1, 24); + lprcat("Press "); + standout("return"); + lprcat(" to continue: "); + while (ttgetch() != '\n'); + setscroll(); +} + +/* + * routine to open the help file and return the first character - '0' + */ +static int +openhelp(void) +{ + if (lopen(helpfile) < 0) { + lprintf("Can't open help file \"%s\" ", helpfile); + lflush(); + sleep(4); + drawscreen(); + setscroll(); + return (-1); + } + resetscroll(); + return (lgetc() - '0'); +} diff --git a/larn/io.c b/larn/io.c new file mode 100644 index 0000000..245ce8a --- /dev/null +++ b/larn/io.c @@ -0,0 +1,991 @@ +/* $NetBSD: io.c,v 1.27 2012/06/19 05:30:43 dholland Exp $ */ + +/* + * io.c Larn is copyrighted 1986 by Noah Morgan. + * + * Below are the functions in this file: + * + * setupvt100() Subroutine to set up terminal in correct mode for game + * clearvt100() Subroutine to clean up terminal when the game is over + * ttgetch() Routine to read in one character from the terminal + * scbr() Function to set cbreak -echo for the terminal + * sncbr() Function to set -cbreak echo for the terminal + * newgame() Subroutine to save the initial time and seed rnd() + * + * FILE OUTPUT ROUTINES + * + * lprintf(format,args . . .) printf to the output buffer lprint(integer) + * end binary integer to output buffer lwrite(buf,len) + * rite a buffer to the output buffer lprcat(str) + * ent string to output buffer + * + * FILE OUTPUT MACROS (in header.h) + * + * lprc(character) put the character into the output + * buffer + * + * FILE INPUT ROUTINES + * + * long lgetc() read one character from input buffer + * long larn_lrint() read one integer from input buffer + * lrfill(address,number) put input bytes into a buffer char + * *lgetw() get a whitespace ended word from + * input char *lgetl() get a \n or EOF ended line + * from input + * + * FILE OPEN / CLOSE ROUTINES + * + * lcreat(filename) create a new file for write + * lopen(filename) open a file for read + * lappend(filename) open for append to an existing file + * lrclose() close the input file + * lwclose() close output file lflush() + * lush the output buffer + * + * Other Routines + * + * cursor(x,y) position cursor at [x,y] + * cursors() position cursor at [1,24] + * (saves memory) cl_line(x,y) Clear line at [1,y] and leave + * cursor at [x,y] cl_up(x,y) Clear screen + * from [x,1] to current line. cl_dn(x,y) + * lear screen from [1,y] to end of display. standout(str) + * rint the string in standout mode. set_score_output() + * alled when output should be literally printed. * ttputch(ch) + * rint one character in decoded output buffer. * flush_buf() + * lush buffer with decoded output. * init_term() + * erminal initialization -- setup termcap info * char *tmcapcnv(sd,ss) + * outine to convert VT100 \33's to termcap format beep() + * e to emit a beep if enabled (see no-beep in .larnopts) + * + * Note: ** entries are available only in termcap mode. + */ +#include <sys/cdefs.h> +#ifndef lint +__RCSID("$NetBSD: io.c,v 1.27 2012/06/19 05:30:43 dholland Exp $"); +#endif /* not lint */ + +#include "header.h" +#include "extern.h" +#include <string.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <term.h> +#include <fcntl.h> +#include <errno.h> +#include <ctype.h> + +#ifdef TERMIO +#include <termio.h> +#define sgttyb termio +#define stty(_a,_b) ioctl(_a,TCSETA,_b) +#define gtty(_a,_b) ioctl(_a,TCGETA,_b) +#endif +#ifdef TERMIOS +#include <termios.h> +#define sgttyb termios +#define stty(_a,_b) tcsetattr(_a,TCSADRAIN,_b) +#define gtty(_a,_b) tcgetattr(_a,_b) +#endif + +#if defined(TERMIO) || defined(TERMIOS) +static int rawflg = 0; +static char saveeof, saveeol; +#define doraw(_a) \ + if(!rawflg) { \ + ++rawflg; \ + saveeof = _a.c_cc[VMIN]; \ + saveeol = _a.c_cc[VTIME]; \ + } \ + _a.c_cc[VMIN] = 1; \ + _a.c_cc[VTIME] = 1; \ + _a.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL) +#define unraw(_a) \ + _a.c_cc[VMIN] = saveeof; \ + _a.c_cc[VTIME] = saveeol; \ + _a.c_lflag |= ICANON|ECHO|ECHOE|ECHOK|ECHONL + +#else /* not TERMIO or TERMIOS */ + +#ifndef BSD +#define CBREAK RAW /* V7 has no CBREAK */ +#endif + +#define doraw(_a) (_a.sg_flags |= CBREAK,_a.sg_flags &= ~ECHO) +#define unraw(_a) (_a.sg_flags &= ~CBREAK,_a.sg_flags |= ECHO) +#include <sgtty.h> +#endif /* not TERMIO or TERMIOS */ + +#ifndef NOVARARGS /* if we have varargs */ +#include <stdarg.h> +#else /* NOVARARGS */ /* if we don't have varargs */ +typedef char *va_list; +#define va_dcl int va_alist; +#define va_start(plist) plist = (char *) &va_alist +#define va_end(plist) +#define va_arg(plist,mode) ((mode *)(plist += sizeof(mode)))[-1] +#endif /* NOVARARGS */ + +static int ttputch(int ch); +static void flush_buf(void); + +#define LINBUFSIZE 128 /* size of the lgetw() and lgetl() buffer */ +int io_outfd; /* output file numbers */ +int io_infd; /* input file numbers */ +static struct sgttyb ttx;/* storage for the tty modes */ +static int ipoint = MAXIBUF, iepoint = MAXIBUF; /* input buffering + * pointers */ +static char lgetwbuf[LINBUFSIZE]; /* get line (word) buffer */ + +/* + * setupvt100() Subroutine to set up terminal in correct mode for game + * + * Attributes off, clear screen, set scrolling region, set tty mode + */ +void +setupvt100(void) +{ + clear(); + setscroll(); + scbr(); /* system("stty cbreak -echo"); */ +} + +/* + * clearvt100() Subroutine to clean up terminal when the game is over + * + * Attributes off, clear screen, unset scrolling region, restore tty mode + */ +void +clearvt100(void) +{ + resetscroll(); + clear(); + sncbr(); /* system("stty -cbreak echo"); */ +} + +/* + * ttgetch() Routine to read in one character from the terminal + */ +int +ttgetch(void) +{ + char byt; +#ifdef EXTRA + c[BYTESIN]++; +#endif + lflush(); /* be sure output buffer is flushed */ + read(0, &byt, 1); /* get byte from terminal */ + return (byt); +} + +/* + * scbr() Function to set cbreak -echo for the terminal + * + * like: system("stty cbreak -echo") + */ +void +scbr(void) +{ + gtty(0, &ttx); + doraw(ttx); + stty(0, &ttx); +} + +/* + * sncbr() Function to set -cbreak echo for the terminal + * + * like: system("stty -cbreak echo") + */ +void +sncbr(void) +{ + gtty(0, &ttx); + unraw(ttx); + stty(0, &ttx); +} + +/* + * newgame() Subroutine to save the initial time and seed rnd() + */ +void +newgame(void) +{ + long *p, *pe; + for (p = c, pe = c + 100; p < pe; *p++ = 0); + time(&initialtime); + seedrand(initialtime); + srandom(initialtime); + lcreat((char *) 0); /* open buffering for output to terminal */ +} + +/* + * lprintf(format,args . . .) printf to the output buffer + * char *format; + * ??? args . . . + * + * Enter with the format string in "format", as per printf() usage + * and any needed arguments following it + * Note: lprintf() only supports %s, %c and %d, with width modifier and left + * or right justification. + * No correct checking for output buffer overflow is done, but flushes + * are done beforehand if needed. + * Returns nothing of value. + */ +void +lprintf(const char *fmt, ...) +{ + va_list ap; + char buf[BUFBIG/2]; + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + + if (lpnt >= lpend) + lflush(); + + lprcat(buf); +} + +/* + * lprint(long-integer) send binary integer to output buffer + * long integer; + * + * +---------+---------+---------+---------+ + * | high | | | low | + * | order | | | order | + * | byte | | | byte | + * +---------+---------+---------+---------+ + * 31 --- 24 23 --- 16 15 --- 8 7 --- 0 + * + * The save order is low order first, to high order (4 bytes total) + * and is written to be system independent. + * No checking for output buffer overflow is done, but flushes if needed! + * Returns nothing of value. + */ +void +lprint(long x) +{ + if (lpnt >= lpend) + lflush(); + *lpnt++ = 255 & x; + *lpnt++ = 255 & (x >> 8); + *lpnt++ = 255 & (x >> 16); + *lpnt++ = 255 & (x >> 24); +} + +/* + * lwrite(buf,len) write a buffer to the output buffer + * char *buf; + * int len; + * + * Enter with the address and number of bytes to write out + * Returns nothing of value + */ +void +lwrite(char *buf, int len) +{ + char *s; + u_char *t; + int num2; + + if (len > 399) { /* don't copy data if can just write it */ +#ifdef EXTRA + c[BYTESOUT] += len; +#endif + +#ifndef VT100 + for (s = buf; len > 0; --len) + lprc(*s++); +#else /* VT100 */ + lflush(); + write(io_outfd, buf, len); +#endif /* VT100 */ + } else + while (len) { + if (lpnt >= lpend) + lflush(); /* if buffer is full flush it */ + num2 = lpbuf + BUFBIG - lpnt; /* # bytes left in + * output buffer */ + if (num2 > len) + num2 = len; + t = lpnt; + len -= num2; + while (num2--) + *t++ = *buf++; /* copy in the bytes */ + lpnt = t; + } +} + +/* + * long lgetc() Read one character from input buffer + * + * Returns 0 if EOF, otherwise the character + */ +long +lgetc(void) +{ + int i; + if (ipoint != iepoint) + return (inbuffer[ipoint++]); + if (iepoint != MAXIBUF) + return (0); + if ((i = read(io_infd, inbuffer, MAXIBUF)) <= 0) { + if (i != 0) + write(1, "error reading from input file\n", 30); + iepoint = ipoint = 0; + return (0); + } + ipoint = 1; + iepoint = i; + return (*inbuffer); +} + +/* + * long lrint() Read one integer from input buffer + * + * +---------+---------+---------+---------+ + * | high | | | low | + * | order | | | order | + * | byte | | | byte | + * +---------+---------+---------+---------+ + * 31 --- 24 23 --- 16 15 --- 8 7 --- 0 + * + * The save order is low order first, to high order (4 bytes total) + * Returns the int read + */ +long +larn_lrint(void) +{ + unsigned long i; + i = 255 & lgetc(); + i |= (255 & lgetc()) << 8; + i |= (255 & lgetc()) << 16; + i |= (255 & lgetc()) << 24; + return (i); +} + +/* + * lrfill(address,number) put input bytes into a buffer + * char *address; + * int number; + * + * Reads "number" bytes into the buffer pointed to by "address". + * Returns nothing of value + */ +void +lrfill(char *adr, int num) +{ + u_char *pnt; + int num2; + + while (num) { + if (iepoint == ipoint) { + if (num > 5) { /* fast way */ + if (read(io_infd, adr, num) != num) + write(2, "error reading from input file\n", 30); + num = 0; + } else { + *adr++ = lgetc(); + --num; + } + } else { + num2 = iepoint - ipoint; /* # of bytes left in + * the buffer */ + if (num2 > num) + num2 = num; + pnt = inbuffer + ipoint; + num -= num2; + ipoint += num2; + while (num2--) + *adr++ = *pnt++; + } + } +} + +/* + * char *lgetw() Get a whitespace ended word from input + * + * Returns pointer to a buffer that contains word. If EOF, returns a NULL + */ +char * +lgetw(void) +{ + char *lgp, cc; + int n = LINBUFSIZE, quote = 0; + lgp = lgetwbuf; + do + cc = lgetc(); + while ((cc <= 32) && (cc > '\0')); /* eat whitespace */ + for (;; --n, cc = lgetc()) { + if ((cc == '\0') && (lgp == lgetwbuf)) + return (NULL); /* EOF */ + if ((n <= 1) || ((cc <= 32) && (quote == 0))) { + *lgp = '\0'; + return (lgetwbuf); + } + if (cc != '"') + *lgp++ = cc; + else + quote ^= 1; + } +} + +/* + * char *lgetl() Function to read in a line ended by newline or EOF + * + * Returns pointer to a buffer that contains the line. If EOF, returns NULL + */ +char * +lgetl(void) +{ + int i = LINBUFSIZE, ch; + char *str = lgetwbuf; + for (;; --i) { + if ((*str++ = ch = lgetc()) == '\0') { + if (str == lgetwbuf + 1) + return (NULL); /* EOF */ + ot: *str = '\0'; + return (lgetwbuf); /* line ended by EOF */ + } + if ((ch == '\n') || (i <= 1)) + goto ot;/* line ended by \n */ + } +} + +/* + * lcreat(filename) Create a new file for write + * char *filename; + * + * lcreat((char*)0); means to the terminal + * Returns -1 if error, otherwise the file descriptor opened. + */ +int +lcreat(char *str) +{ + lflush(); + lpnt = lpbuf; + lpend = lpbuf + BUFBIG; + if (str == NULL) + return (io_outfd = 1); + if ((io_outfd = creat(str, 0644)) < 0) { + io_outfd = 1; + lprintf("error creating file <%s>: %s\n", str, + strerror(errno)); + lflush(); + return (-1); + } + return (io_outfd); +} + +/* + * lopen(filename) Open a file for read + * char *filename; + * + * lopen(0) means from the terminal + * Returns -1 if error, otherwise the file descriptor opened. + */ +int +lopen(char *str) +{ + ipoint = iepoint = MAXIBUF; + if (str == NULL) + return (io_infd = 0); + if ((io_infd = open(str, O_RDONLY)) < 0) { + lwclose(); + io_outfd = 1; + lpnt = lpbuf; + return (-1); + } + return (io_infd); +} + +/* + * lappend(filename) Open for append to an existing file + * char *filename; + * + * lappend(0) means to the terminal + * Returns -1 if error, otherwise the file descriptor opened. + */ +int +lappend(char *str) +{ + lpnt = lpbuf; + lpend = lpbuf + BUFBIG; + if (str == NULL) + return (io_outfd = 1); + if ((io_outfd = open(str, 2)) < 0) { + io_outfd = 1; + return (-1); + } + lseek(io_outfd, 0, SEEK_END); /* seek to end of file */ + return (io_outfd); +} + +/* + * lrclose() close the input file + * + * Returns nothing of value. + */ +void +lrclose(void) +{ + if (io_infd > 0) { + close(io_infd); + io_infd = 0; + } +} + +/* + * lwclose() close output file flushing if needed + * + * Returns nothing of value. + */ +void +lwclose(void) +{ + lflush(); + if (io_outfd > 2) { + close(io_outfd); + io_outfd = 1; + } +} + +/* + * lprcat(string) append a string to the output buffer + * avoids calls to lprintf (time consuming) + */ +void +lprcat(const char *str) +{ + u_char *str2; + if (lpnt >= lpend) + lflush(); + str2 = lpnt; + while ((*str2++ = *str++) != '\0') + continue; + lpnt = str2 - 1; +} + +#ifdef VT100 +/* + * cursor(x,y) Subroutine to set the cursor position + * + * x and y are the cursor coordinates, and lpbuff is the output buffer where + * escape sequence will be placed. + */ +static char *y_num[] = { +"\33[", "\33[", "\33[2", "\33[3", "\33[4", "\33[5", "\33[6", +"\33[7", "\33[8", "\33[9", "\33[10", "\33[11", "\33[12", "\33[13", "\33[14", +"\33[15", "\33[16", "\33[17", "\33[18", "\33[19", "\33[20", "\33[21", "\33[22", +"\33[23", "\33[24"}; + +static char *x_num[] = { +"H", "H", ";2H", ";3H", ";4H", ";5H", ";6H", ";7H", ";8H", ";9H", +";10H", ";11H", ";12H", ";13H", ";14H", ";15H", ";16H", ";17H", ";18H", ";19H", +";20H", ";21H", ";22H", ";23H", ";24H", ";25H", ";26H", ";27H", ";28H", ";29H", +";30H", ";31H", ";32H", ";33H", ";34H", ";35H", ";36H", ";37H", ";38H", ";39H", +";40H", ";41H", ";42H", ";43H", ";44H", ";45H", ";46H", ";47H", ";48H", ";49H", +";50H", ";51H", ";52H", ";53H", ";54H", ";55H", ";56H", ";57H", ";58H", ";59H", +";60H", ";61H", ";62H", ";63H", ";64H", ";65H", ";66H", ";67H", ";68H", ";69H", +";70H", ";71H", ";72H", ";73H", ";74H", ";75H", ";76H", ";77H", ";78H", ";79H", +";80H"}; + +void +cursor(x, y) + int x, y; +{ + char *p; + if (lpnt >= lpend) + lflush(); + + p = y_num[y]; /* get the string to print */ + while (*p) + *lpnt++ = *p++; /* print the string */ + + p = x_num[x]; /* get the string to print */ + while (*p) + *lpnt++ = *p++; /* print the string */ +} +#else /* VT100 */ +/* + * cursor(x,y) Put cursor at specified coordinates staring at [1,1] (termcap) + */ +void +cursor(int x, int y) +{ + if (lpnt >= lpend) + lflush(); + + *lpnt++ = CURSOR; + *lpnt++ = x; + *lpnt++ = y; +} +#endif /* VT100 */ + +/* + * Routine to position cursor at beginning of 24th line + */ +void +cursors(void) +{ + cursor(1, 24); +} + +#ifndef VT100 +/* + * Warning: ringing the bell is control code 7. Don't use in defines. + * Don't change the order of these defines. + * Also used in helpfiles. Codes used in helpfiles should be \E[1 to \E[7 with + * obvious meanings. + */ + +static char *outbuf = 0; /* translated output buffer */ +/* + * init_term() Terminal initialization -- setup termcap info + */ +void +init_term(void) +{ + setupterm(NULL, 0, NULL); /* will exit if invalid term */ + if (!cursor_address) { + fprintf(stderr, "term does not have cursor_address.\n"); + exit(1); + } + if (!clr_eol) { + fprintf(stderr, "term does not have clr_eol.\n"); + exit(1); + } + if (!clear_screen) { + fprintf(stderr, "term does not have clear_screen.\n"); + exit(1); + } + if ((outbuf = malloc(BUFBIG + 16)) == 0) { /* get memory for + * decoded output buffer */ + fprintf(stderr, "Error malloc'ing memory for decoded output buffer\n"); + died(-285); /* malloc() failure */ + } + +} +#endif /* VT100 */ + +/* + * cl_line(x,y) Clear the whole line indicated by 'y' and leave cursor at [x,y] + */ +void +cl_line(int x, int y) +{ +#ifdef VT100 + cursor(x, y); + lprcat("\33[2K"); +#else /* VT100 */ + cursor(1, y); + *lpnt++ = CL_LINE; + cursor(x, y); +#endif /* VT100 */ +} + +/* + * cl_up(x,y) Clear screen from [x,1] to current position. Leave cursor at [x,y] + */ +void +cl_up(int x, int y) +{ +#ifdef VT100 + cursor(x, y); + lprcat("\33[1J\33[2K"); +#else /* VT100 */ + int i; + cursor(1, 1); + for (i = 1; i <= y; i++) { + *lpnt++ = CL_LINE; + *lpnt++ = '\n'; + } + cursor(x, y); +#endif /* VT100 */ +} + +/* + * cl_dn(x,y) Clear screen from [1,y] to end of display. Leave cursor at [x,y] + */ +void +cl_dn(int x, int y) +{ +#ifdef VT100 + cursor(x, y); + lprcat("\33[J\33[2K"); +#else /* VT100 */ + int i; + cursor(1, y); + if (!clr_eos) { + *lpnt++ = CL_LINE; + for (i = y; i <= 24; i++) { + *lpnt++ = CL_LINE; + if (i != 24) + *lpnt++ = '\n'; + } + cursor(x, y); + } else + *lpnt++ = CL_DOWN; + cursor(x, y); +#endif /* VT100 */ +} + +/* + * standout(str) Print the argument string in inverse video (standout mode). + */ +void +standout(const char *str) +{ +#ifdef VT100 + setbold(); + while (*str) + *lpnt++ = *str++; + resetbold(); +#else /* VT100 */ + *lpnt++ = ST_START; + while (*str) + *lpnt++ = *str++; + *lpnt++ = ST_END; +#endif /* VT100 */ +} + +/* + * set_score_output() Called when output should be literally printed. + */ +void +set_score_output(void) +{ + enable_scroll = -1; +} + +/* + * lflush() Flush the output buffer + * + * Returns nothing of value. + * for termcap version: Flush output in output buffer according to output + * status as indicated by `enable_scroll' + */ +#ifndef VT100 +static int scrline = 18; /* line # for wraparound instead of scrolling + * if no DL */ +void +lflush(void) +{ + int lpoint; + u_char *str; + static int curx = 0; + static int cury = 0; + + if ((lpoint = lpnt - lpbuf) > 0) { +#ifdef EXTRA + c[BYTESOUT] += lpoint; +#endif + if (enable_scroll <= -1) { + flush_buf(); + if (write(io_outfd, lpbuf, lpoint) != lpoint) + write(2, "error writing to output file\n", 29); + lpnt = lpbuf; /* point back to beginning of buffer */ + return; + } + for (str = lpbuf; str < lpnt; str++) { + if (*str >= 32) { + ttputch(*str); + curx++; + } else + switch (*str) { + case CLEAR: + tputs(clear_screen, 0, ttputch); + curx = cury = 0; + break; + + case CL_LINE: + tputs(clr_eol, 0, ttputch); + break; + + case CL_DOWN: + tputs(clr_eos, 0, ttputch); + break; + + case ST_START: + tputs(enter_standout_mode, 0, ttputch); + break; + + case ST_END: + tputs(exit_standout_mode, 0, ttputch); + break; + + case CURSOR: + curx = *++str - 1; + cury = *++str - 1; + tputs(tiparm(cursor_address, + cury, curx), 0, ttputch); + break; + + case '\n': + if ((cury == 23) && enable_scroll) { + if (!delete_line || + !insert_line) + { /* wraparound or scroll? */ + if (++scrline > 23) + scrline = 19; + + if (++scrline > 23) + scrline = 19; + tputs(tiparm( + cursor_address, + scrline, 0), + 0, ttputch); + tputs(clr_eol, 0, + ttputch); + + if (--scrline < 19) + scrline = 23; + tputs(tiparm( + cursor_address, + scrline, 0), + 0, ttputch); + tputs(clr_eol, 0, + ttputch); + } else { + tputs(tiparm( + cursor_address, + 19, 0), + 0, ttputch); + tputs(delete_line, 0, + ttputch); + tputs(tiparm( + cursor_address, + 23, 0), + 0, ttputch); + /* + * tputs (AL, 0, + * ttputch); + */ + } + } else { + ttputch('\n'); + cury++; + } + curx = 0; + break; + + default: + ttputch(*str); + curx++; + }; + } + } + lpnt = lpbuf; + flush_buf(); /* flush real output buffer now */ +} +#else /* VT100 */ +/* + * lflush() flush the output buffer + * + * Returns nothing of value. + */ +void +lflush() +{ + int lpoint; + if ((lpoint = lpnt - lpbuf) > 0) { +#ifdef EXTRA + c[BYTESOUT] += lpoint; +#endif + if (write(io_outfd, lpbuf, lpoint) != lpoint) + write(2, "error writing to output file\n", 29); + } + lpnt = lpbuf; /* point back to beginning of buffer */ +} +#endif /* VT100 */ + +#ifndef VT100 +static int vindex = 0; +/* + * ttputch(ch) Print one character in decoded output buffer. + */ +static int +ttputch(int ch) +{ + outbuf[vindex++] = ch; + if (vindex >= BUFBIG) + flush_buf(); + return (0); +} + +/* + * flush_buf() Flush buffer with decoded output. + */ +static void +flush_buf(void) +{ + if (vindex) + write(io_outfd, outbuf, vindex); + vindex = 0; +} + +/* + * char *tmcapcnv(sd,ss) Routine to convert VT100 escapes to termcap + * format + * Processes only the \33[#m sequence (converts . files for termcap use + */ +char * +tmcapcnv(char *sd, char *ss) +{ + int tmstate = 0; /* 0=normal, 1=\33 2=[ 3=# */ + char tmdigit = 0; /* the # in \33[#m */ + while (*ss) { + switch (tmstate) { + case 0: + if (*ss == '\33') { + tmstate++; + break; + } + ign: *sd++ = *ss; + ign2: tmstate = 0; + break; + case 1: + if (*ss != '[') + goto ign; + tmstate++; + break; + case 2: + if (isdigit((u_char)*ss)) { + tmdigit = *ss - '0'; + tmstate++; + break; + } + if (*ss == 'm') { + *sd++ = ST_END; + goto ign2; + } + goto ign; + case 3: + if (*ss == 'm') { + if (tmdigit) + *sd++ = ST_START; + else + *sd++ = ST_END; + goto ign2; + } + default: + goto ign; + }; + ss++; + } + *sd = 0; /* NULL terminator */ + return (sd); +} +#endif /* VT100 */ + +/* + * beep() Routine to emit a beep if enabled (see no-beep in .larnopts) + */ +void +beep(void) +{ + if (!nobeep) + *lpnt++ = '\7'; +} diff --git a/larn/larn.6 b/larn/larn.6 new file mode 100644 index 0000000..7fadc6b --- /dev/null +++ b/larn/larn.6 @@ -0,0 +1,157 @@ +.\" $NetBSD: larn.6,v 1.14 2010/04/24 01:13:37 dholland Exp $ +.\" +.\" Copyright (c) 1990 The Regents of the University of California. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)larn.6 5.5 (Berkeley) 12/30/93 +.\" +.Dd April 23, 2010 +.Dt LARN 6 +.Os +.Sh NAME +.Nm larn +.Nd exploring the caverns of Larn +.Sh SYNOPSIS +.Nm larn +.Op Fl chilns +.\".Op Fl H Ar number +.Op Fl o Ar optsfile +.Op Fl ## +.Op ++ +.Sh DESCRIPTION +.Nm +is a fantasy games in which your child has contracted +a strange disease, and none of your home remedies seem to have any effect. +You set out to find a remedy in a limited +amount of time, and to collect gold along the way of course! +.Pp +The options are: +.Pp +.Bl -tag -width flag +.It Fl c +Clear the high scores file. +.\" .It Fl H +.\" Set the difficulty (hardness) level. +.It Fl h +Show the command line options. +.It Fl i +Show the high scores, including inventory data. +.It Fl l +Show the log of all games. +.It Fl n +Suppress the welcome message and start the game immediately. +.It Fl o +.\" Ar optsfile +Read the specified options file instead of +.Pa ~/.larnopts . +.It Fl s +Show the high scores. +.It Fl ## +Set the difficulty (hardness) level. +.It ++ +Restore game from the checkpoint (auto-save) file. +.El +.Sh COMMANDS +These are the movement commands: +.Bl -column " print program version" " give present pack weight" +.It h move to the left H run left . stay here +.It j move down J run down Z teleport yourself +.It k move up K run up c cast a spell +.It l move to the right L run right r read a scroll +.It y move northwest Y run northwest q quaff a potion +.It u move northeast U run northeast W wear armor +.It b move southwest B run southwest T take off armor +.It n move southeast N run southeast w wield a weapon +.It ^ identify a trap g give present pack weight P give tax status +.It d drop an item i inventory your pockets Q quit the game +.It v print program version S save the game D list all items found +.It ? this help screen A create diagnostic file e eat something +.It (wizards only) +.El +.Sh OPTIONS FILE +The file +.Pa ~/.larnopts +may be used to set a few options for +.Nm . +A sequence of words terminated by whitespace is used to specify options. +.Pp +.Bl -tag -width "savefile: xxsave-file-namexx" -compact +.It Sy Word +.Sy Meaning +.Pp +.It bold-objects +Select bold display of objects. +.It inverse-objects +Select inverse video display of objects. +.It no-introduction +Do not display intro message. +.It enable-checkpointing +Turn on periodic checkpointing. +.It no-beep +Disable beeping of the terminal. +.It male +Choose your sex to be a man. +.It female +Choose your sex to be a woman. +.It name: Dq your name +Choose your playing name. +.It monster: Dq monst name +Choose a name for a monster. +.It savefile: Dq save-file-name +Define what the savegame filename will be. +.El +.Pp +Your name and monster names must be enclosed in double quotation marks and may +be up to 34 characters long. +Longer names are truncated. +Anything enclosed in quotation marks is considered one word, and must be +separated from other words by whitespace. +.Sh SPECIAL NOTES +When +.Sy dropping gold , +if you type '*' as your amount, all your gold gets dropped. +In general, typing in '*' means all of what you are interested in. +This is true when visiting the bank, or when contributing at altars. +.Pp +You can get out of the store, trading post, school, or home by hitting +.Aq Sy esc . +.Pp +When casting a spell, if you need a list of spells you can cast, type +.Ic D +as the first letter of your spell. +The available list of spells will be shown, +after which you may enter the spell code. +This only works on the 1st letter of the spell you are casting. +.Sh FILES +.Bl -tag -width "/var/games/larn.scores" -compact +.It Pa /var/games/larn.scores +Score file. +.It Pa ~/.larnopts +Options file. +.El +.Sh AUTHORS +.An Noah Morgan diff --git a/larn/main.c b/larn/main.c new file mode 100644 index 0000000..4a5af8f --- /dev/null +++ b/larn/main.c @@ -0,0 +1,1341 @@ +/* $NetBSD: main.c,v 1.25 2012/06/19 05:30:43 dholland Exp $ */ + +/* main.c */ +#include <sys/cdefs.h> +#ifndef lint +__RCSID("$NetBSD: main.c,v 1.25 2012/06/19 05:30:43 dholland Exp $"); +#endif /* not lint */ + +#include <sys/types.h> +#include <stdio.h> +#include <pwd.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include "header.h" +#include "extern.h" + +static void showstr(void); +static void t_setup(int); +static void t_endup(int); +static void showwear(void); +static void showwield(void); +static void showread(void); +static void showeat(void); +static void showquaff(void); +static void show1(int, const char *[]); +static void randmonst(void); +static void parse(void); +static void run(int); +static void wield(void); +static void ydhi(int); +static void ycwi(int); +static void wear(void); +static void dropobj(void); +static void readscr(void); +static void eatcookie(void); +static void quaff(void); +static int whatitem(const char *); + +static char copyright[] = "\nLarn is copyrighted 1986 by Noah Morgan.\n"; +int srcount = 0; /* line counter for showstr() */ +int dropflag = 0; /* if 1 then don't lookforobject() next round */ +int rmst = 80; /* random monster creation counter */ +int userid; /* the players login user id number */ +gid_t gid, egid; /* used for security */ +u_char nowelcome = 0, nomove = 0; /* if (nomove) then don't + * count next iteration as a + * move */ +static char viewflag = 0; +/* + * if viewflag then we have done a 99 stay here and don't showcell in the + * main loop + */ +u_char restorflag = 0; /* 1 means restore has been done */ +static char cmdhelp[] = "\ +Cmd line format: larn [-slicnh] [-o<optsfile>] [-##] [++]\n\ + -s show the scoreboard\n\ + -l show the logfile (wizard id only)\n\ + -i show scoreboard with inventories of dead characters\n\ + -c create new scoreboard (wizard id only)\n\ + -n suppress welcome message on starting game\n\ + -## specify level of difficulty (example: -5)\n\ + -h print this help text\n\ + ++ restore game from checkpoint file\n\ + -o<optsfile> specify .larnopts filename to be used instead of \"~/.larnopts\"\n\ +"; +#ifdef VT100 +/* +20150211 bkw: assume any term type is vt100-compatible +static char *termtypes[] = {"vt100", "vt101", "vt102", "vt103", "vt125", + "vt131", "vt140", "vt180", "vt220", "vt240", "vt241", "vt320", "vt340", +"vt341"}; +*/ +#endif /* VT100 */ +/* + ************ + MAIN PROGRAM + ************ + */ +int +main(int argc, char **argv) +{ + int i; + int hard; + const char *ptr = 0; + struct passwd *pwe; + + i = 0; + egid = getegid(); + gid = getgid(); + setegid(gid); /* give up "games" if we have it */ + /* + * first task is to identify the player + */ +#ifndef VT100 + init_term(); /* setup the terminal (find out what type) + * for termcap */ +#endif /* VT100 */ + /* try to get login name */ + if (((ptr = getlogin()) == 0) || (*ptr == 0)) { + /* can we get it from /etc/passwd? */ + if ((pwe = getpwuid(getuid())) != NULL) + ptr = pwe->pw_name; + else if ((ptr = getenv("USER")) == 0) + if ((ptr = getenv("LOGNAME")) == 0) { + noone: write(2, "Can't find your logname. Who Are You?\n", 39); + exit(1); + } + } + if (ptr == 0) + goto noone; + if (strlen(ptr) == 0) + goto noone; + /* + * second task is to prepare the pathnames the player will need + */ + strcpy(loginname, ptr); /* save loginname of the user for logging + * purposes */ + strcpy(logname, ptr); /* this will be overwritten with the players + * name */ + if ((ptr = getenv("HOME")) == NULL) + ptr = "."; + strcpy(savefilename, ptr); + strcat(savefilename, "/Larn.sav"); /* save file name in home + * directory */ + snprintf(optsfile, sizeof(optsfile), "%s/.larnopts", ptr); + /* the .larnopts filename */ + + /* + * now malloc the memory for the dungeon + */ + cell = (struct cel *) malloc(sizeof(struct cel) * (MAXLEVEL + MAXVLEVEL) * MAXX * MAXY); + if (cell == 0) + died(-285); /* malloc failure */ + lpbuf = malloc((5 * BUFBIG) >> 2); /* output buffer */ + inbuffer = malloc((5 * MAXIBUF) >> 2); /* output buffer */ + if ((lpbuf == 0) || (inbuffer == 0)) + died(-285); /* malloc() failure */ + + lcreat((char *) 0); + newgame(); /* set the initial clock */ + hard = -1; + +#ifdef VT100 + /* + * check terminal type to avoid users who have not vt100 type terminals + */ + /* + // 20150211 bkw: assume any term type is vt100-compatible + ttype = getenv("TERM"); + for (j = 1, i = 0; i < sizeof(termtypes) / sizeof(char *); i++) + if (strcmp(ttype, termtypes[i]) == 0) { + j = 0; + break; + } + if (j) { + lprcat("Sorry, Larn needs a VT100 family terminal for all its features.\n"); + lflush(); + exit(1); + } + */ +#endif /* VT100 */ + + /* + * now make scoreboard if it is not there (don't clear) + */ + if (access(scorefile, 0) == -1) /* not there */ + makeboard(); + + /* + * now process the command line arguments + */ + for (i = 1; i < argc; i++) { + if (argv[i][0] == '-') + switch (argv[i][1]) { + case 's': + showscores(); + exit(0); /* show scoreboard */ + + case 'l': /* show log file */ + diedlog(); + exit(0); + + case 'i': + showallscores(); + exit(0); /* show all scoreboard */ + + case 'c': /* anyone with password can create + * scoreboard */ + lprcat("Preparing to initialize the scoreboard.\n"); + if (getpassword() != 0) { /* make new scoreboard */ + makeboard(); + lprc('\n'); + showscores(); + } + exit(0); + + case 'n': /* no welcome msg */ + nowelcome = 1; + argv[i][0] = 0; + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': /* for hardness */ + sscanf(&argv[i][1], "%d", &hard); + break; + + case 'h': /* print out command line arguments */ + write(1, cmdhelp, sizeof(cmdhelp)); + exit(0); + + case 'o': /* specify a .larnopts filename */ + strncpy(optsfile, argv[i] + 2, 127); + break; + + default: + printf("Unknown option <%s>\n", argv[i]); + exit(1); + }; + + if (argv[i][0] == '+') { + clear(); + restorflag = 1; + if (argv[i][1] == '+') { + hitflag = 1; + restoregame(ckpfile); /* restore checkpointed + * game */ + } + i = argc; + } + } + + readopts(); /* read the options file if there is one */ + + +#ifdef UIDSCORE + userid = geteuid(); /* obtain the user's effective id number */ +#else /* UIDSCORE */ + userid = getplid(logname); /* obtain the players id number */ +#endif /* UIDSCORE */ + if (userid < 0) { + write(2, "Can't obtain playerid\n", 22); + exit(1); + } +#ifdef HIDEBYLINK + /* + * this section of code causes the program to look like something else to ps + */ + if (strcmp(psname, argv[0])) { /* if a different process name only */ + if ((i = access(psname, 1)) < 0) { /* link not there */ + if (link(argv[0], psname) >= 0) { + argv[0] = psname; + execv(psname, argv); + } + } else + unlink(psname); + } + for (i = 1; i < argc; i++) { + szero(argv[i]); /* zero the argument to avoid ps snooping */ + } +#endif /* HIDEBYLINK */ + + if (access(savefilename, 0) == 0) { /* restore game if need to */ + clear(); + restorflag = 1; + hitflag = 1; + restoregame(savefilename); /* restore last game */ + } + sigsetup(); /* trap all needed signals */ + sethard(hard); /* set up the desired difficulty */ + setupvt100(); /* setup the terminal special mode */ + if (c[HP] == 0) { /* create new game */ + makeplayer(); /* make the character that will play */ + newcavelevel(0);/* make the dungeon */ + predostuff = 1; /* tell signals that we are in the welcome + * screen */ + if (nowelcome == 0) + welcome(); /* welcome the player to the game */ + } + drawscreen(); /* show the initial dungeon */ + predostuff = 2; /* tell the trap functions that they must do + * a showplayer() from here on */ +#if 0 + nice(1); /* games should be run niced */ +#endif + yrepcount = hit2flag = 0; + while (1) { + if (dropflag == 0) + lookforobject(); /* see if there is an object + * here */ + else + dropflag = 0; /* don't show it just dropped an item */ + if (hitflag == 0) { + if (c[HASTEMONST]) + movemonst(); + movemonst(); + } /* move the monsters */ + if (viewflag == 0) + showcell(playerx, playery); + else + viewflag = 0; /* show stuff around player */ + if (hit3flag) + flushall(); + hitflag = hit3flag = 0; + nomove = 1; + bot_linex(); /* update bottom line */ + while (nomove) { + if (hit3flag) + flushall(); + nomove = 0; + parse(); + } /* get commands and make moves */ + regen(); /* regenerate hp and spells */ + if (c[TIMESTOP] == 0) + if (--rmst <= 0) { + rmst = 120 - (level << 2); + fillmonst(makemonst(level)); + } + } +} + + +/* + showstr() + + show character's inventory + */ +static void +showstr(void) +{ + int i, number; + for (number = 3, i = 0; i < 26; i++) + if (iven[i]) + number++; /* count items in inventory */ + t_setup(number); + qshowstr(); + t_endup(number); +} + +void +qshowstr(void) +{ + int i, j, k, sigsav; + srcount = 0; + sigsav = nosignal; + nosignal = 1; /* don't allow ^c etc */ + if (c[GOLD]) { + lprintf(".) %ld gold pieces", (long) c[GOLD]); + srcount++; + } + for (k = 26; k >= 0; k--) + if (iven[k]) { + for (i = 22; i < 84; i++) + for (j = 0; j <= k; j++) + if (i == iven[j]) + show3(j); + k = 0; + } + lprintf("\nElapsed time is %ld. You have %ld mobuls left", (long) ((gltime + 99) / 100 + 1), (long) ((TIMELIMIT - gltime) / 100)); + more(); + nosignal = sigsav; +} + +/* + * subroutine to clear screen depending on # lines to display + */ +static void +t_setup(int count) +{ + if (count < 20) { /* how do we clear the screen? */ + cl_up(79, count); + cursor(1, 1); + } else { + resetscroll(); + clear(); + } +} + +/* + * subroutine to restore normal display screen depending on t_setup() + */ +static void +t_endup(int count) +{ + if (count < 18) /* how did we clear the screen? */ + draws(0, MAXX, 0, (count > MAXY) ? MAXY : count); + else { + drawscreen(); + setscroll(); + } +} + +/* + function to show the things player is wearing only + */ +static void +showwear(void) +{ + int i, j, sigsav, count; + sigsav = nosignal; + nosignal = 1; /* don't allow ^c etc */ + srcount = 0; + + for (count = 2, j = 0; j <= 26; j++) /* count number of items we + * will display */ + if ((i = iven[j]) != 0) + switch (i) { + case OLEATHER: + case OPLATE: + case OCHAIN: + case ORING: + case OSTUDLEATHER: + case OSPLINT: + case OPLATEARMOR: + case OSSPLATE: + case OSHIELD: + count++; + }; + + t_setup(count); + + for (i = 22; i < 84; i++) + for (j = 0; j <= 26; j++) + if (i == iven[j]) + switch (i) { + case OLEATHER: + case OPLATE: + case OCHAIN: + case ORING: + case OSTUDLEATHER: + case OSPLINT: + case OPLATEARMOR: + case OSSPLATE: + case OSHIELD: + show3(j); + }; + more(); + nosignal = sigsav; + t_endup(count); +} + +/* + function to show the things player can wield only + */ +static void +showwield(void) +{ + int i, j, sigsav, count; + sigsav = nosignal; + nosignal = 1; /* don't allow ^c etc */ + srcount = 0; + + for (count = 2, j = 0; j <= 26; j++) /* count how many items */ + if ((i = iven[j]) != 0) + switch (i) { + case ODIAMOND: + case ORUBY: + case OEMERALD: + case OSAPPHIRE: + case OBOOK: + case OCHEST: + case OLARNEYE: + case ONOTHEFT: + case OSPIRITSCARAB: + case OCUBEofUNDEAD: + case OPOTION: + case OSCROLL: + break; + default: + count++; + }; + + t_setup(count); + + for (i = 22; i < 84; i++) + for (j = 0; j <= 26; j++) + if (i == iven[j]) + switch (i) { + case ODIAMOND: + case ORUBY: + case OEMERALD: + case OSAPPHIRE: + case OBOOK: + case OCHEST: + case OLARNEYE: + case ONOTHEFT: + case OSPIRITSCARAB: + case OCUBEofUNDEAD: + case OPOTION: + case OSCROLL: + break; + default: + show3(j); + }; + more(); + nosignal = sigsav; + t_endup(count); +} + +/* + * function to show the things player can read only + */ +static void +showread(void) +{ + int i, j, sigsav, count; + sigsav = nosignal; + nosignal = 1; /* don't allow ^c etc */ + srcount = 0; + + for (count = 2, j = 0; j <= 26; j++) + switch (iven[j]) { + case OBOOK: + case OSCROLL: + count++; + }; + t_setup(count); + + for (i = 22; i < 84; i++) + for (j = 0; j <= 26; j++) + if (i == iven[j]) + switch (i) { + case OBOOK: + case OSCROLL: + show3(j); + }; + more(); + nosignal = sigsav; + t_endup(count); +} + +/* + * function to show the things player can eat only + */ +static void +showeat(void) +{ + int i, j, sigsav, count; + sigsav = nosignal; + nosignal = 1; /* don't allow ^c etc */ + srcount = 0; + + for (count = 2, j = 0; j <= 26; j++) + switch (iven[j]) { + case OCOOKIE: + count++; + }; + t_setup(count); + + for (i = 22; i < 84; i++) + for (j = 0; j <= 26; j++) + if (i == iven[j]) + switch (i) { + case OCOOKIE: + show3(j); + }; + more(); + nosignal = sigsav; + t_endup(count); +} + +/* + function to show the things player can quaff only + */ +static void +showquaff(void) +{ + int i, j, sigsav, count; + sigsav = nosignal; + nosignal = 1; /* don't allow ^c etc */ + srcount = 0; + + for (count = 2, j = 0; j <= 26; j++) + switch (iven[j]) { + case OPOTION: + count++; + }; + t_setup(count); + + for (i = 22; i < 84; i++) + for (j = 0; j <= 26; j++) + if (i == iven[j]) + switch (i) { + case OPOTION: + show3(j); + }; + more(); + nosignal = sigsav; + t_endup(count); +} + +static void +show1(int idx, const char *str2[]) +{ + lprintf("\n%c) %s", idx + 'a', objectname[iven[idx]]); + if (str2 != 0 && str2[ivenarg[idx]][0] != 0) + lprintf(" of%s", str2[ivenarg[idx]]); +} + +void +show3(int indx) +{ + switch (iven[indx]) { + case OPOTION: + show1(indx, potionname); + break; + case OSCROLL: + show1(indx, scrollname); + break; + + case OLARNEYE: + case OBOOK: + case OSPIRITSCARAB: + case ODIAMOND: + case ORUBY: + case OCUBEofUNDEAD: + case OEMERALD: + case OCHEST: + case OCOOKIE: + case OSAPPHIRE: + case ONOTHEFT: + show1(indx, NULL); + break; + + default: + lprintf("\n%c) %s", indx + 'a', objectname[iven[indx]]); + if (ivenarg[indx] > 0) + lprintf(" + %ld", (long) ivenarg[indx]); + else if (ivenarg[indx] < 0) + lprintf(" %ld", (long) ivenarg[indx]); + break; + } + if (c[WIELD] == indx) + lprcat(" (weapon in hand)"); + if ((c[WEAR] == indx) || (c[SHIELD] == indx)) + lprcat(" (being worn)"); + if (++srcount >= 22) { + srcount = 0; + more(); + clear(); + } +} + +/* + subroutine to randomly create monsters if needed + */ +static void +randmonst(void) +{ + if (c[TIMESTOP]) + return; /* don't make monsters if time is stopped */ + if (--rmst <= 0) { + rmst = 120 - (level << 2); + fillmonst(makemonst(level)); + } +} + + + +/* + parse() + + get and execute a command + */ +static void +parse(void) +{ + int i, j, k, flag; + while (1) { + k = yylex(); + switch (k) { /* get the token from the input and switch on + * it */ + case 'h': + moveplayer(4); + return; /* west */ + case 'H': + run(4); + return; /* west */ + case 'l': + moveplayer(2); + return; /* east */ + case 'L': + run(2); + return; /* east */ + case 'j': + moveplayer(1); + return; /* south */ + case 'J': + run(1); + return; /* south */ + case 'k': + moveplayer(3); + return; /* north */ + case 'K': + run(3); + return; /* north */ + case 'u': + moveplayer(5); + return; /* northeast */ + case 'U': + run(5); + return; /* northeast */ + case 'y': + moveplayer(6); + return; /* northwest */ + case 'Y': + run(6); + return; /* northwest */ + case 'n': + moveplayer(7); + return; /* southeast */ + case 'N': + run(7); + return; /* southeast */ + case 'b': + moveplayer(8); + return; /* southwest */ + case 'B': + run(8); + return; /* southwest */ + + case '.': + if (yrepcount) + viewflag = 1; + return; /* stay here */ + + case 'w': + yrepcount = 0; + wield(); + return; /* wield a weapon */ + + case 'W': + yrepcount = 0; + wear(); + return; /* wear armor */ + + case 'r': + yrepcount = 0; + if (c[BLINDCOUNT]) { + cursors(); + lprcat("\nYou can't read anything when you're blind!"); + } else if (c[TIMESTOP] == 0) + readscr(); + return; /* to read a scroll */ + + case 'q': + yrepcount = 0; + if (c[TIMESTOP] == 0) + quaff(); + return; /* quaff a potion */ + + case 'd': + yrepcount = 0; + if (c[TIMESTOP] == 0) + dropobj(); + return; /* to drop an object */ + + case 'c': + yrepcount = 0; + cast(); + return; /* cast a spell */ + + case 'i': + yrepcount = 0; + nomove = 1; + showstr(); + return; /* status */ + + case 'e': + yrepcount = 0; + if (c[TIMESTOP] == 0) + eatcookie(); + return; /* to eat a fortune cookie */ + + case 'D': + yrepcount = 0; + seemagic(0); + nomove = 1; + return; /* list spells and scrolls */ + + case '?': + yrepcount = 0; + help(); + nomove = 1; + return; /* give the help screen */ + + case 'S': + clear(); + lprcat("Saving . . ."); + lflush(); + savegame(savefilename); + wizard = 1; + died(-257); /* save the game - doesn't return */ + + case 'Z': + yrepcount = 0; + if (c[LEVEL] > 9) { + oteleport(1); + return; + } + cursors(); + lprcat("\nAs yet, you don't have enough experience to use teleportation"); + return; /* teleport yourself */ + + case '^': /* identify traps */ + flag = yrepcount = 0; + cursors(); + lprc('\n'); + for (j = playery - 1; j < playery + 2; j++) { + if (j < 0) + j = 0; + if (j >= MAXY) + break; + for (i = playerx - 1; i < playerx + 2; i++) { + if (i < 0) + i = 0; + if (i >= MAXX) + break; + switch (item[i][j]) { + case OTRAPDOOR: + case ODARTRAP: + case OTRAPARROW: + case OTELEPORTER: + lprcat("\nIt's "); + lprcat(objectname[item[i][j]]); + flag++; + }; + } + } + if (flag == 0) + lprcat("\nNo traps are visible"); + return; + +#if WIZID + case '_': /* this is the fudge player password for + * wizard mode */ + yrepcount = 0; + cursors(); + nomove = 1; + if (userid != wisid) { + lprcat("Sorry, you are not empowered to be a wizard.\n"); + scbr(); /* system("stty -echo cbreak"); */ + lflush(); + return; + } + if (getpassword() == 0) { + scbr(); /* system("stty -echo cbreak"); */ + return; + } + wizard = 1; + scbr(); /* system("stty -echo cbreak"); */ + for (i = 0; i < 6; i++) + c[i] = 70; + iven[0] = iven[1] = 0; + take(OPROTRING, 50); + take(OLANCE, 25); + c[WIELD] = 1; + c[LANCEDEATH] = 1; + c[WEAR] = c[SHIELD] = -1; + raiseexperience(6000000L); + c[AWARENESS] += 25000; + { + int i, j; + for (i = 0; i < MAXY; i++) + for (j = 0; j < MAXX; j++) + know[j][i] = 1; + for (i = 0; i < SPNUM; i++) + spelknow[i] = 1; + for (i = 0; i < MAXSCROLL; i++) + scrollname[i] = scrollhide[i]; + for (i = 0; i < MAXPOTION; i++) + potionname[i] = potionhide[i]; + } + for (i = 0; i < MAXSCROLL; i++) + if (strlen(scrollname[i]) > 2) { /* no null items */ + item[i][0] = OSCROLL; + iarg[i][0] = i; + } + for (i = MAXX - 1; i > MAXX - 1 - MAXPOTION; i--) + if (strlen(potionname[i - MAXX + MAXPOTION]) > 2) { /* no null items */ + item[i][0] = OPOTION; + iarg[i][0] = i - MAXX + MAXPOTION; + } + for (i = 1; i < MAXY; i++) { + item[0][i] = i; + iarg[0][i] = 0; + } + for (i = MAXY; i < MAXY + MAXX; i++) { + item[i - MAXY][MAXY - 1] = i; + iarg[i - MAXY][MAXY - 1] = 0; + } + for (i = MAXX + MAXY; i < MAXX + MAXY + MAXY; i++) { + item[MAXX - 1][i - MAXX - MAXY] = i; + iarg[MAXX - 1][i - MAXX - MAXY] = 0; + } + c[GOLD] += 25000; + drawscreen(); + return; +#endif + + case 'T': + yrepcount = 0; + cursors(); + if (c[SHIELD] != -1) { + c[SHIELD] = -1; + lprcat("\nYour shield is off"); + bottomline(); + } else if (c[WEAR] != -1) { + c[WEAR] = -1; + lprcat("\nYour armor is off"); + bottomline(); + } else + lprcat("\nYou aren't wearing anything"); + return; + + case 'g': + cursors(); + lprintf("\nThe stuff you are carrying presently weighs %ld pounds", (long) packweight()); + case ' ': + yrepcount = 0; + nomove = 1; + return; + + case 'v': + yrepcount = 0; + cursors(); + lprintf("\nCaverns of Larn, Version %ld.%ld, Diff=%ld", + (long) VERSION, (long) SUBVERSION, + (long) c[HARDGAME]); + if (wizard) + lprcat(" Wizard"); + nomove = 1; + if (cheat) + lprcat(" Cheater"); + lprcat(copyright); + return; + + case 'Q': + yrepcount = 0; + quit(); + nomove = 1; + return; /* quit */ + + case 'L' - 64: + yrepcount = 0; + drawscreen(); + nomove = 1; + return; /* look */ + +#if WIZID +#ifdef EXTRA + case 'A': + yrepcount = 0; + nomove = 1; + if (wizard) { + diag(); + return; + } /* create diagnostic file */ + return; +#endif +#endif + case 'P': + cursors(); + if (outstanding_taxes > 0) + lprintf("\nYou presently owe %ld gp in taxes.", + (long) outstanding_taxes); + else + lprcat("\nYou do not owe any taxes."); + return; + }; + } +} + +void +parse2(void) +{ + if (c[HASTEMONST]) + movemonst(); + movemonst(); /* move the monsters */ + randmonst(); + regen(); +} + +static void +run(int dir) +{ + int i; + i = 1; + while (i) { + i = moveplayer(dir); + if (i > 0) { + if (c[HASTEMONST]) + movemonst(); + movemonst(); + randmonst(); + regen(); + } + if (hitflag) + i = 0; + if (i != 0) + showcell(playerx, playery); + } +} + +/* + function to wield a weapon + */ +static void +wield(void) +{ + int i; + while (1) { + if ((i = whatitem("wield")) == '\33') + return; + if (i != '.') { + if (i == '*') + showwield(); + else if (iven[i - 'a'] == 0) { + ydhi(i); + return; + } else if (iven[i - 'a'] == OPOTION) { + ycwi(i); + return; + } else if (iven[i - 'a'] == OSCROLL) { + ycwi(i); + return; + } else if ((c[SHIELD] != -1) && (iven[i - 'a'] == O2SWORD)) { + lprcat("\nBut one arm is busy with your shield!"); + return; + } else { + c[WIELD] = i - 'a'; + if (iven[i - 'a'] == OLANCE) + c[LANCEDEATH] = 1; + else + c[LANCEDEATH] = 0; + bottomline(); + return; + } + } + } +} + +/* + common routine to say you don't have an item + */ +static void +ydhi(int x) +{ + cursors(); + lprintf("\nYou don't have item %c!", x); +} +static void +ycwi(int x) +{ + cursors(); + lprintf("\nYou can't wield item %c!", x); +} + +/* + function to wear armor + */ +static void +wear(void) +{ + int i; + while (1) { + if ((i = whatitem("wear")) == '\33') + return; + if (i != '.') { + if (i == '*') + showwear(); + else + switch (iven[i - 'a']) { + case 0: + ydhi(i); + return; + case OLEATHER: + case OCHAIN: + case OPLATE: + case OSTUDLEATHER: + case ORING: + case OSPLINT: + case OPLATEARMOR: + case OSSPLATE: + if (c[WEAR] != -1) { + lprcat("\nYou're already wearing some armor"); + return; + } + c[WEAR] = i - 'a'; + bottomline(); + return; + case OSHIELD: + if (c[SHIELD] != -1) { + lprcat("\nYou are already wearing a shield"); + return; + } + if (iven[c[WIELD]] == O2SWORD) { + lprcat("\nYour hands are busy with the two handed sword!"); + return; + } + c[SHIELD] = i - 'a'; + bottomline(); + return; + default: + lprcat("\nYou can't wear that!"); + }; + } + } +} + +/* + function to drop an object + */ +static void +dropobj(void) +{ + int i; + unsigned char *p; + long amt; + p = &item[playerx][playery]; + while (1) { + if ((i = whatitem("drop")) == '\33') + return; + if (i == '*') + showstr(); + else { + if (i == '.') { /* drop some gold */ + if (*p) { + lprcat("\nThere's something here already!"); + return; + } + lprcat("\n\n"); + cl_dn(1, 23); + lprcat("How much gold do you drop? "); + if ((amt = readnum((long) c[GOLD])) == 0) + return; + if (amt > c[GOLD]) { + lprcat("\nYou don't have that much!"); + return; + } + if (amt <= 32767) { + *p = OGOLDPILE; + i = amt; + } else if (amt <= 327670L) { + *p = ODGOLD; + i = amt / 10; + amt = 10 * i; + } else if (amt <= 3276700L) { + *p = OMAXGOLD; + i = amt / 100; + amt = 100 * i; + } else if (amt <= 32767000L) { + *p = OKGOLD; + i = amt / 1000; + amt = 1000 * i; + } else { + *p = OKGOLD; + i = 32767; + amt = 32767000L; + } + c[GOLD] -= amt; + lprintf("You drop %ld gold pieces", (long)amt); + iarg[playerx][playery] = i; + bottomgold(); + know[playerx][playery] = 0; + dropflag = 1; + return; + } + drop_object(i - 'a'); + return; + } + } +} + +/* + * readscr() Subroutine to read a scroll one is carrying + */ +static void +readscr(void) +{ + int i; + while (1) { + if ((i = whatitem("read")) == '\33') + return; + if (i != '.') { + if (i == '*') + showread(); + else { + if (iven[i - 'a'] == OSCROLL) { + read_scroll(ivenarg[i - 'a']); + iven[i - 'a'] = 0; + return; + } + if (iven[i - 'a'] == OBOOK) { + readbook(ivenarg[i - 'a']); + iven[i - 'a'] = 0; + return; + } + if (iven[i - 'a'] == 0) { + ydhi(i); + return; + } + lprcat("\nThere's nothing on it to read"); + return; + } + } + } +} + +/* + * subroutine to eat a cookie one is carrying + */ +static void +eatcookie(void) +{ + const char *p; + int i; + + while (1) { + if ((i = whatitem("eat")) == '\33') + return; + if (i != '.') { + if (i == '*') + showeat(); + else { + if (iven[i - 'a'] == OCOOKIE) { + lprcat("\nThe cookie was delicious."); + iven[i - 'a'] = 0; + if (!c[BLINDCOUNT]) { + if ((p = fortune()) != NULL) { + lprcat(" Inside you find a scrap of paper that says:\n"); + lprcat(p); + } + } + return; + } + if (iven[i - 'a'] == 0) { + ydhi(i); + return; + } + lprcat("\nYou can't eat that!"); + return; + } + } + } +} + +/* + * subroutine to quaff a potion one is carrying + */ +static void +quaff(void) +{ + int i; + while (1) { + if ((i = whatitem("quaff")) == '\33') + return; + if (i != '.') { + if (i == '*') + showquaff(); + else { + if (iven[i - 'a'] == OPOTION) { + quaffpotion(ivenarg[i - 'a']); + iven[i - 'a'] = 0; + return; + } + if (iven[i - 'a'] == 0) { + ydhi(i); + return; + } + lprcat("\nYou wouldn't want to quaff that, would you? "); + return; + } + } + } +} + +/* + function to ask what player wants to do + */ +static int +whatitem(const char *str) +{ + int i; + cursors(); + lprintf("\nWhat do you want to %s [* for all] ? ", str); + i = 0; + while (i > 'z' || (i < 'a' && i != '*' && i != '\33' && i != '.')) + i = ttgetch(); + if (i == '\33') + lprcat(" aborted"); + return (i); +} + +/* + subroutine to get a number from the player + and allow * to mean return amt, else return the number entered + */ +unsigned long +readnum(long mx) +{ + int i; + unsigned long amt = 0; + sncbr(); + if ((i = ttgetch()) == '*') + amt = mx; /* allow him to say * for all gold */ + else + while (i != '\n') { + if (i == '\033') { + scbr(); + lprcat(" aborted"); + return (0); + } + if ((i <= '9') && (i >= '0') && (amt < 99999999)) + amt = amt * 10 + i - '0'; + i = ttgetch(); + } + scbr(); + return (amt); +} + +#ifdef HIDEBYLINK +/* + * routine to zero every byte in a string + */ +void +szero(str) + char *str; +{ + while (*str) + *str++ = 0; +} +#endif /* HIDEBYLINK */ diff --git a/larn/monster.c b/larn/monster.c new file mode 100644 index 0000000..35a329e --- /dev/null +++ b/larn/monster.c @@ -0,0 +1,1911 @@ +/* $NetBSD: monster.c,v 1.18 2012/06/19 05:30:43 dholland Exp $ */ + +/* + * monster.c Larn is copyrighted 1986 by Noah Morgan. + * + * This file contains the following functions: + * ---------------------------------------------------------------------------- + * + * createmonster(monstno) Function to create a monster next to the player + * int monstno; + * + * int cgood(x,y,itm,monst)Function to check location for emptiness + * int x,y,itm,monst; + * + * createitem(it,arg) Routine to place an item next to the player + * int it,arg; + * + * cast() Subroutine called by parse to cast a spell for the user + * + * speldamage(x) Function to perform spell functions cast by the player + * int x; + * + * loseint() Routine to decrement your int (intelligence) if > 3 + * + * isconfuse() Routine to check to see if player is confused + * + * nospell(x,monst)Routine to return 1 if a spell doesn't affect a monster + * int x,monst; + * + * fullhit(xx) Function to return full damage against a monst (aka web) + * int xx; + * + * direct(spnum,dam,str,arg)Routine to direct spell damage 1 square in 1 dir + * int spnum,dam,arg; + * char *str; + * + * godirect(spnum,dam,str,delay,cshow) Function to perform missile attacks + * int spnum,dam,delay; + * char *str,cshow; + * + * ifblind(x,y)Routine to put "monster" or the monster name into lastmosnt + * int x,y; + * + * tdirect(spnum) Routine to teleport away a monster + * int spnum; + * + * omnidirect(sp,dam,str) Routine to damage all monsters 1 square from player + * int sp,dam; + * char *str; + * + * dirsub(x,y) Routine to ask for direction, then modify x,y for it + * int *x,*y; + * + * vxy(x,y) Routine to verify/fix (*x,*y) for being within bounds + * int *x,*y; + * + * dirpoly(spnum) Routine to ask for a direction and polymorph a monst + * int spnum; + * + * hitmonster(x,y) Function to hit a monster at the designated coordinates + * int x,y; + * + * hitm(x,y,amt) Function to just hit a monster at a given coordinates + * int x,y,amt; + * + * hitplayer(x,y) Function for the monster to hit the player from (x,y) + * int x,y; + * + * dropsomething(monst) Function to create an object when a monster dies + * int monst; + * + * dropgold(amount) Function to drop some gold around player + * int amount; + * + * something(level) Function to create a random item around player + * int level; + * + * newobject(lev,i) Routine to return a randomly selected new object + * int lev,*i; + * + * spattack(atckno,xx,yy) Function to process special attacks from monsters + * int atckno,xx,yy; + * + * checkloss(x) Routine to subtract hp from user and flag bottomline display + * int x; + * + * annihilate() Routine to annihilate monsters around player, playerx,playery + * + * newsphere(x,y,dir,lifetime) Function to create a new sphere of annihilation + * int x,y,dir,lifetime; + * + * rmsphere(x,y) Function to delete a sphere of annihilation from list + * int x,y; + * + * sphboom(x,y) Function to perform the effects of a sphere detonation + * int x,y; + * + * genmonst() Function to ask for monster and genocide from game + * + */ +#include <sys/cdefs.h> +#ifndef lint +__RCSID("$NetBSD: monster.c,v 1.18 2012/06/19 05:30:43 dholland Exp $"); +#endif /* not lint */ + +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include "header.h" +#include "extern.h" + +struct isave { /* used for altar reality */ + char type; /* 0=item, 1=monster */ + char id; /* item number or monster number */ + short arg; /* the type of item or hitpoints of monster */ +}; + +static int cgood(int, int, int, int); +static void speldamage(int); +static void loseint(void); +static int isconfuse(void); +static int nospell(int, int); +static int fullhit(int); +static void direct(int, int, const char *, int); +static void ifblind(int, int); +static void tdirect(int); +static void omnidirect(int, int, const char *); +static int dirsub(int *, int *); +static void dirpoly(int); +static int hitm(int, int, int); +static void dropsomething(int); +static int spattack(int, int, int); +static void sphboom(int, int); +static void genmonst(void); + +/* + * createmonster(monstno) Function to create a monster next to the player + * int monstno; + * + * Enter with the monster number (1 to MAXMONST+8) + * Returns no value. + */ +void +createmonster(int mon) +{ + int x, y, k, i; + if (mon < 1 || mon > MAXMONST + 8) { /* check for monster number + * out of bounds */ + beep(); + lprintf("\ncan't createmonst(%ld)\n", (long) mon); + nap(3000); + return; + } + while (monster[mon].genocided && mon < MAXMONST) + mon++; /* genocided? */ + for (k = rnd(8), i = -8; i < 0; i++, k++) { /* choose direction, + * then try all */ + if (k > 8) + k = 1; /* wraparound the diroff arrays */ + x = playerx + diroffx[k]; + y = playery + diroffy[k]; + if (cgood(x, y, 0, 1)) { /* if we can create here */ + mitem[x][y] = mon; + hitp[x][y] = monster[mon].hitpoints; + stealth[x][y] = know[x][y] = 0; + switch (mon) { + case ROTHE: + case POLTERGEIST: + case VAMPIRE: + stealth[x][y] = 1; + }; + return; + } + } +} + +/* + * int cgood(x,y,itm,monst) Function to check location for emptiness + * int x,y,itm,monst; + * + * Routine to return TRUE if a location does not have itm or monst there + * returns FALSE (0) otherwise + * Enter with itm or monst TRUE or FALSE if checking it + * Example: if itm==TRUE check for no item at this location + * if monst==TRUE check for no monster at this location + * This routine will return FALSE if at a wall or the dungeon exit on level 1 + */ +static int +cgood(int x, int y, int theitem, int monst) +{ +#define itm __lose + if ((y >= 0) && (y <= MAXY - 1) && (x >= 0) && (x <= MAXX - 1)) + /* within bounds? */ + if (item[x][y] != OWALL) /* can't make anything on walls */ + /* is it free of items? */ + if (theitem == 0 || (item[x][y] == 0)) + /* is it free of monsters? */ + if (monst == 0 || (mitem[x][y] == 0)) + if ((level != 1) || (x != 33) || + (y != MAXY - 1)) + /* not exit to level 1 */ + return (1); + return (0); +} + +/* + * createitem(it,arg) Routine to place an item next to the player + * int it,arg; + * + * Enter with the item number and its argument (iven[], ivenarg[]) + * Returns no value, thus we don't know about createitem() failures. + */ +void +createitem(int it, int arg) +{ + int x, y, k, i; + if (it >= MAXOBJ) + return; /* no such object */ + for (k = rnd(8), i = -8; i < 0; i++, k++) { /* choose direction, + * then try all */ + if (k > 8) + k = 1; /* wraparound the diroff arrays */ + x = playerx + diroffx[k]; + y = playery + diroffy[k]; + if (cgood(x, y, 1, 0)) { /* if we can create here */ + item[x][y] = it; + know[x][y] = 0; + iarg[x][y] = arg; + return; + } + } +} + +/* + * cast() Subroutine called by parse to cast a spell for the user + * + * No arguments and no return value. + */ +static char eys[] = "\nEnter your spell: "; +void +cast(void) +{ + int i, j, a, b, d; + cursors(); + if (c[SPELLS] <= 0) { + lprcat("\nYou don't have any spells!"); + return; + } + lprcat(eys); + --c[SPELLS]; + while ((a = ttgetch()) == 'D') { + seemagic(-1); + cursors(); + lprcat(eys); + } + if (a == '\33') + goto over; /* to escape casting a spell */ + if ((b = ttgetch()) == '\33') + goto over; /* to escape casting a spell */ + if ((d = ttgetch()) == '\33') { +over: lprcat(aborted); + c[SPELLS]++; + return; + } /* to escape casting a spell */ +#ifdef EXTRA + c[SPELLSCAST]++; +#endif + for (lprc('\n'), j = -1, i = 0; i < SPNUM; i++) /* seq search for his + * spell, hash? */ + if ((spelcode[i][0] == a) && (spelcode[i][1] == b) && (spelcode[i][2] == d)) + if (spelknow[i]) { + speldamage(i); + j = 1; + i = SPNUM; + } + if (j == -1) + lprcat(" Nothing Happened "); + bottomline(); +} + +/* + * speldamage(x) Function to perform spell functions cast by the player + * int x; + * + * Enter with the spell number, returns no value. + * Please insure that there are 2 spaces before all messages here + */ +static void +speldamage(int x) +{ + int i, j, clev; + int xl, xh, yl, yh; + u_char *p, *kn, *pm; + + if (x >= SPNUM) + return; /* no such spell */ + if (c[TIMESTOP]) { + lprcat(" It didn't seem to work"); + return; + } /* not if time stopped */ + clev = c[LEVEL]; + if ((rnd(23) == 7) || (rnd(18) > c[INTELLIGENCE])) { + lprcat(" It didn't work!"); + return; + } + if (clev * 3 + 2 < x) { + lprcat(" Nothing happens. You seem inexperienced at this"); + return; + } + switch (x) { + /* ----- LEVEL 1 SPELLS ----- */ + + case 0: + if (c[PROTECTIONTIME] == 0) + c[MOREDEFENSES] += 2; /* protection field +2 */ + c[PROTECTIONTIME] += 250; + return; + + case 1: + i = rnd(((clev + 1) << 1)) + clev + 3; + godirect(x, i, (clev >= 2) ? " Your missiles hit the %s" : " Your missile hit the %s", 100, '+'); /* magic missile */ + + return; + + case 2: + if (c[DEXCOUNT] == 0) + c[DEXTERITY] += 3; /* dexterity */ + c[DEXCOUNT] += 400; + return; + + case 3: /* sleep */ + i = rnd(3) + 1; + direct(x, fullhit(i), + " While the %s slept, you smashed it %ld times", i); + return; + + case 4: /* charm monster */ + c[CHARMCOUNT] += c[CHARISMA] << 1; + return; + + case 5: + godirect(x, rnd(10) + 15 + clev, " The sound damages the %s", 70, '@'); /* sonic spear */ + return; + + /* ----- LEVEL 2 SPELLS ----- */ + + case 6: /* web */ + i = rnd(3) + 2; + direct(x, fullhit(i), + " While the %s is entangled, you hit %ld times", i); + return; + + case 7: + if (c[STRCOUNT] == 0) + c[STREXTRA] += 3; /* strength */ + c[STRCOUNT] += 150 + rnd(100); + return; + + case 8: + yl = playery - 5; /* enlightenment */ + yh = playery + 6; + xl = playerx - 15; + xh = playerx + 16; + vxy(&xl, &yl); + vxy(&xh, &yh); /* check bounds */ + for (i = yl; i <= yh; i++) /* enlightenment */ + for (j = xl; j <= xh; j++) + know[j][i] = 1; + draws(xl, xh + 1, yl, yh + 1); + return; + + case 9: + raisehp(20 + (clev << 1)); + return; /* healing */ + + case 10: + c[BLINDCOUNT] = 0; + return; /* cure blindness */ + + case 11: + createmonster(makemonst(level + 1) + 8); + return; + + case 12: + if (rnd(11) + 7 <= c[WISDOM]) + direct(x, rnd(20) + 20 + clev, " The %s believed!", 0); + else + lprcat(" It didn't believe the illusions!"); + return; + + case 13: /* if he has the amulet of invisibility then + * add more time */ + for (j = i = 0; i < 26; i++) + if (iven[i] == OAMULET) + j += 1 + ivenarg[i]; + c[INVISIBILITY] += (j << 7) + 12; + return; + + /* ----- LEVEL 3 SPELLS ----- */ + + case 14: + godirect(x, rnd(25 + clev) + 25 + clev, " The fireball hits the %s", 40, '*'); + return; /* fireball */ + + case 15: + godirect(x, rnd(25) + 20 + clev, " Your cone of cold strikes the %s", 60, 'O'); /* cold */ + return; + + case 16: + dirpoly(x); + return; /* polymorph */ + + case 17: + c[CANCELLATION] += 5 + clev; + return; /* cancellation */ + + case 18: + c[HASTESELF] += 7 + clev; + return; /* haste self */ + + case 19: + omnidirect(x, 30 + rnd(10), " The %s gasps for air"); /* cloud kill */ + return; + + case 20: + xh = min(playerx + 1, MAXX - 2); + yh = min(playery + 1, MAXY - 2); + for (i = max(playerx - 1, 1); i <= xh; i++) /* vaporize rock */ + for (j = max(playery - 1, 1); j <= yh; j++) { + kn = &know[i][j]; + pm = &mitem[i][j]; + switch (*(p = &item[i][j])) { + case OWALL: + if (level < MAXLEVEL + MAXVLEVEL - 1) + *p = *kn = 0; + break; + + case OSTATUE: + if (c[HARDGAME] < 3) { + *p = OBOOK; + iarg[i][j] = level; + *kn = 0; + } + break; + + case OTHRONE: + *pm = GNOMEKING; + *kn = 0; + *p = OTHRONE2; + hitp[i][j] = monster[GNOMEKING].hitpoints; + break; + + case OALTAR: + *pm = DEMONPRINCE; + *kn = 0; + hitp[i][j] = monster[DEMONPRINCE].hitpoints; + break; + }; + switch (*pm) { + case XORN: + ifblind(i, j); + hitm(i, j, 200); + break; /* Xorn takes damage from vpr */ + } + } + return; + + /* ----- LEVEL 4 SPELLS ----- */ + + case 21: + direct(x, 100 + clev, " The %s shrivels up", 0); /* dehydration */ + return; + + case 22: + godirect(x, rnd(25) + 20 + (clev << 1), " A lightning bolt hits the %s", 1, '~'); /* lightning */ + return; + + case 23: + i = min(c[HP] - 1, c[HPMAX] / 2); /* drain life */ + direct(x, i + i, "", 0); + c[HP] -= i; + return; + + case 24: + if (c[GLOBE] == 0) + c[MOREDEFENSES] += 10; + c[GLOBE] += 200; + loseint(); /* globe of invulnerability */ + return; + + case 25: + omnidirect(x, 32 + clev, " The %s struggles for air in your flood!"); /* flood */ + return; + + case 26: + if (rnd(151) == 63) { + beep(); + lprcat("\nYour heart stopped!\n"); + nap(4000); + died(270); + return; + } + if (c[WISDOM] > rnd(10) + 10) + direct(x, 2000, " The %s's heart stopped", 0); /* finger of death */ + else + lprcat(" It didn't work"); + return; + + /* ----- LEVEL 5 SPELLS ----- */ + + case 27: + c[SCAREMONST] += rnd(10) + clev; + return; /* scare monster */ + + case 28: + c[HOLDMONST] += rnd(10) + clev; + return; /* hold monster */ + + case 29: + c[TIMESTOP] += rnd(20) + (clev << 1); + return; /* time stop */ + + case 30: + tdirect(x); + return; /* teleport away */ + + case 31: + omnidirect(x, 35 + rnd(10) + clev, " The %s cringes from the flame"); /* magic fire */ + return; + + /* ----- LEVEL 6 SPELLS ----- */ + + case 32: + if ((rnd(23) == 5) && (wizard == 0)) { /* sphere of + * annihilation */ + beep(); + lprcat("\nYou have been enveloped by the zone of nothingness!\n"); + nap(4000); + died(258); + return; + } + xl = playerx; + yl = playery; + loseint(); + i = dirsub(&xl, &yl); /* get direction of sphere */ + newsphere(xl, yl, i, rnd(20) + 11); /* make a sphere */ + return; + + case 33: + genmonst(); + spelknow[33] = 0; /* genocide */ + loseint(); + return; + + case 34: /* summon demon */ + if (rnd(100) > 30) { + direct(x, 150, " The demon strikes at the %s", 0); + return; + } + if (rnd(100) > 15) { + lprcat(" Nothing seems to have happened"); + return; + } + lprcat(" The demon turned on you and vanished!"); + beep(); + i = rnd(40) + 30; + lastnum = 277; + losehp(i); /* must say killed by a demon */ + return; + + case 35: /* walk through walls */ + c[WTW] += rnd(10) + 5; + return; + + case 36: /* alter reality */ + { + struct isave *save; /* pointer to item save + * structure */ + int sc; + sc = 0; /* # items saved */ + save = (struct isave *) malloc(sizeof(struct isave) * MAXX * MAXY * 2); + for (j = 0; j < MAXY; j++) + for (i = 0; i < MAXX; i++) { /* save all items and + * monsters */ + xl = item[i][j]; + if (xl && xl != OWALL && xl != OANNIHILATION) { + save[sc].type = 0; + save[sc].id = item[i][j]; + save[sc++].arg = iarg[i][j]; + } + if (mitem[i][j]) { + save[sc].type = 1; + save[sc].id = mitem[i][j]; + save[sc++].arg = hitp[i][j]; + } + item[i][j] = OWALL; + mitem[i][j] = 0; + if (wizard) + know[i][j] = 1; + else + know[i][j] = 0; + } + eat(1, 1); + if (level == 1) + item[33][MAXY - 1] = 0; + for (j = rnd(MAXY - 2), i = 1; i < MAXX - 1; i++) + item[i][j] = 0; + while (sc > 0) { /* put objects back in level */ + --sc; + if (save[sc].type == 0) { + int trys; + for (trys = 100, i = j = 1; --trys > 0 && item[i][j]; i = rnd(MAXX - 1), j = rnd(MAXY - 1)); + if (trys) { + item[i][j] = save[sc].id; + iarg[i][j] = save[sc].arg; + } + } else { /* put monsters back in */ + int trys; + for (trys = 100, i = j = 1; --trys > 0 && (item[i][j] == OWALL || mitem[i][j]); i = rnd(MAXX - 1), j = rnd(MAXY - 1)); + if (trys) { + mitem[i][j] = save[sc].id; + hitp[i][j] = save[sc].arg; + } + } + } + loseint(); + draws(0, MAXX, 0, MAXY); + if (wizard == 0) + spelknow[36] = 0; + free((char *) save); + positionplayer(); + return; + } + + case 37: /* permanence */ + adjusttime(-99999L); + spelknow[37] = 0; /* forget */ + loseint(); + return; + + default: + lprintf(" spell %ld not available!", (long) x); + beep(); + return; + }; +} + +/* + * loseint() Routine to subtract 1 from your int (intelligence) if > 3 + * + * No arguments and no return value + */ +static void +loseint(void) +{ + if (--c[INTELLIGENCE] < 3) + c[INTELLIGENCE] = 3; +} + +/* + * isconfuse() Routine to check to see if player is confused + * + * This routine prints out a message saying "You can't aim your magic!" + * returns 0 if not confused, non-zero (time remaining confused) if confused + */ +static int +isconfuse(void) +{ + if (c[CONFUSE]) { + lprcat(" You can't aim your magic!"); + beep(); + } + return (c[CONFUSE]); +} + +/* + * nospell(x,monst) Routine to return 1 if a spell doesn't affect a monster + * int x,monst; + * + * Subroutine to return 1 if the spell can't affect the monster + * otherwise returns 0 + * Enter with the spell number in x, and the monster number in monst. + */ +static int +nospell(int x, int monst) +{ + int tmp; + if (x >= SPNUM || monst >= MAXMONST + 8 || monst < 0 || x < 0) + return (0); /* bad spell or monst */ + if ((tmp = spelweird[monst - 1][x]) == 0) + return (0); + cursors(); + lprc('\n'); + lprintf(spelmes[tmp], monster[monst].name); + return (1); +} + +/* + * fullhit(xx) Function to return full damage against a monster (aka web) + * int xx; + * + * Function to return hp damage to monster due to a number of full hits + * Enter with the number of full hits being done + */ +static int +fullhit(int xx) +{ + int i; + if (xx < 0 || xx > 20) + return (0); /* fullhits are out of range */ + if (c[LANCEDEATH]) + return (10000); /* lance of death */ + i = xx * ((c[WCLASS] >> 1) + c[STRENGTH] + c[STREXTRA] - c[HARDGAME] - 12 + c[MOREDAM]); + return ((i >= 1) ? i : xx); +} + +/* + * direct(spnum,dam,str,arg) Routine to direct spell damage 1 square in 1 dir + * int spnum,dam,arg; + * char *str; + * + * Routine to ask for a direction to a spell and then hit the monster + * Enter with the spell number in spnum, the damage to be done in dam, + * lprintf format string in str, and lprintf's argument in arg. + * Returns no value. + */ +static void +direct(int spnum, int dam, const char *str, int arg) +{ + int x, y; + int m; + if (spnum < 0 || spnum >= SPNUM || str == 0) + return; /* bad arguments */ + if (isconfuse()) + return; + dirsub(&x, &y); + m = mitem[x][y]; + if (item[x][y] == OMIRROR) { + if (spnum == 3) { /* sleep */ + lprcat("You fall asleep! "); + beep(); + fool: + arg += 2; + while (arg-- > 0) { + parse2(); + nap(1000); + } + return; + } else if (spnum == 6) { /* web */ + lprcat("You get stuck in your own web! "); + beep(); + goto fool; + } else { + lastnum = 278; + lprintf(str, "spell caster (that's you)", (long) arg); + beep(); + losehp(dam); + return; + } + } + if (m == 0) { + lprcat(" There wasn't anything there!"); + return; + } + ifblind(x, y); + if (nospell(spnum, m)) { + lasthx = x; + lasthy = y; + return; + } + lprintf(str, lastmonst, (long) arg); + hitm(x, y, dam); +} + +/* + * godirect(spnum,dam,str,delay,cshow) Function to perform missile attacks + * int spnum,dam,delay; + * char *str,cshow; + * + * Function to hit in a direction from a missile weapon and have it keep + * on going in that direction until its power is exhausted + * Enter with the spell number in spnum, the power of the weapon in hp, + * lprintf format string in str, the # of milliseconds to delay between + * locations in delay, and the character to represent the weapon in cshow. + * Returns no value. + */ +void +godirect(int spnum, int dam, const char *str, int delay, int cshow_i) +{ + u_char *p; + int x, y, m; + int dx, dy; + char cshow; + + /* truncate to char width in case it matters */ + cshow = (char)cshow_i; + + if (spnum < 0 || spnum >= SPNUM || str == 0 || delay < 0) + return; /* bad args */ + if (isconfuse()) + return; + dirsub(&dx, &dy); + x = dx; + y = dy; + dx = x - playerx; + dy = y - playery; + x = playerx; + y = playery; + while (dam > 0) { + x += dx; + y += dy; + if ((x > MAXX - 1) || (y > MAXY - 1) || (x < 0) || (y < 0)) { + dam = 0; + break; /* out of bounds */ + } + if ((x == playerx) && (y == playery)) { /* if energy hits player */ + cursors(); + lprcat("\nYou are hit by your own magic!"); + beep(); + lastnum = 278; + losehp(dam); + return; + } + if (c[BLINDCOUNT] == 0) { /* if not blind show effect */ + cursor(x + 1, y + 1); + lprc(cshow); + nap(delay); + show1cell(x, y); + } + if ((m = mitem[x][y])) { /* is there a monster there? */ + ifblind(x, y); + if (nospell(spnum, m)) { + lasthx = x; + lasthy = y; + return; + } + cursors(); + lprc('\n'); + lprintf(str, lastmonst); + dam -= hitm(x, y, dam); + show1cell(x, y); + nap(1000); + x -= dx; + y -= dy; + } else + switch (*(p = &item[x][y])) { + case OWALL: + cursors(); + lprc('\n'); + lprintf(str, "wall"); + if (dam >= 50 + c[HARDGAME]) /* enough damage? */ + if (level < MAXLEVEL + MAXVLEVEL - 1) /* not on V3 */ + if ((x < MAXX - 1) && (y < MAXY - 1) && (x) && (y)) { + lprcat(" The wall crumbles"); + god3: *p = 0; + god: know[x][y] = 0; + show1cell(x, y); + } + god2: dam = 0; + break; + + case OCLOSEDDOOR: + cursors(); + lprc('\n'); + lprintf(str, "door"); + if (dam >= 40) { + lprcat(" The door is blasted apart"); + goto god3; + } + goto god2; + + case OSTATUE: + cursors(); + lprc('\n'); + lprintf(str, "statue"); + if (c[HARDGAME] < 3) + if (dam > 44) { + lprcat(" The statue crumbles"); + *p = OBOOK; + iarg[x][y] = level; + goto god; + } + goto god2; + + case OTHRONE: + cursors(); + lprc('\n'); + lprintf(str, "throne"); + if (dam > 39) { + mitem[x][y] = GNOMEKING; + hitp[x][y] = monster[GNOMEKING].hitpoints; + *p = OTHRONE2; + goto god; + } + goto god2; + + case OMIRROR: + dx *= -1; + dy *= -1; + break; + }; + dam -= 3 + (c[HARDGAME] >> 1); + } +} + +/* + * ifblind(x,y) Routine to put "monster" or the monster name into lastmosnt + * int x,y; + * + * Subroutine to copy the word "monster" into lastmonst if the player is blind + * Enter with the coordinates (x,y) of the monster + * Returns no value. + */ +static void +ifblind(int x, int y) +{ + const char *p; + + vxy(&x, &y); /* verify correct x,y coordinates */ + if (c[BLINDCOUNT]) { + lastnum = 279; + p = "monster"; + } else { + lastnum = mitem[x][y]; + p = monster[lastnum].name; + } + strcpy(lastmonst, p); +} + +/* + * tdirect(spnum) Routine to teleport away a monster + * int spnum; + * + * Routine to ask for a direction to a spell and then teleport away monster + * Enter with the spell number that wants to teleport away + * Returns no value. + */ +static void +tdirect(int spnum) +{ + int x, y; + int m; + if (spnum < 0 || spnum >= SPNUM) + return; /* bad args */ + if (isconfuse()) + return; + dirsub(&x, &y); + if ((m = mitem[x][y]) == 0) { + lprcat(" There wasn't anything there!"); + return; + } + ifblind(x, y); + if (nospell(spnum, m)) { + lasthx = x; + lasthy = y; + return; + } + fillmonst(m); + mitem[x][y] = know[x][y] = 0; +} + +/* + * omnidirect(sp,dam,str) Routine to damage all monsters 1 square from player + * int sp,dam; + * char *str; + * + * Routine to cast a spell and then hit the monster in all directions + * Enter with the spell number in sp, the damage done to wach square in dam, + * and the lprintf string to identify the spell in str. + * Returns no value. + */ +static void +omnidirect(int spnum, int dam, const char *str) +{ + int x, y, m; + + if (spnum < 0 || spnum >= SPNUM || str == 0) + return; /* bad args */ + for (x = playerx - 1; x < playerx + 2; x++) + for (y = playery - 1; y < playery + 2; y++) { + if ((m = mitem[x][y]) != 0) { + if (nospell(spnum, m) == 0) { + ifblind(x, y); + cursors(); + lprc('\n'); + lprintf(str, lastmonst); + hitm(x, y, dam); + nap(800); + } else { + lasthx = x; + lasthy = y; + } + } + } +} + +/* + * static dirsub(x,y) Routine to ask for direction, then modify x,y for it + * int *x,*y; + * + * Function to ask for a direction and modify an x,y for that direction + * Enter with the origination coordinates in (x,y). + * Returns index into diroffx[] (0-8). + */ +static int +dirsub(int *x, int *y) +{ + int i; + lprcat("\nIn What Direction? "); + for (i = 0;;) + switch (ttgetch()) { + case 'b': + i++; + case 'n': + i++; + case 'y': + i++; + case 'u': + i++; + case 'h': + i++; + case 'k': + i++; + case 'l': + i++; + case 'j': + i++; + goto out; + }; +out: + *x = playerx + diroffx[i]; + *y = playery + diroffy[i]; + vxy(x, y); + return (i); +} + +/* + * vxy(x,y) Routine to verify/fix coordinates for being within bounds + * int *x,*y; + * + * Function to verify x & y are within the bounds for a level + * If *x or *y is not within the absolute bounds for a level, fix them so that + * they are on the level. + * Returns TRUE if it was out of bounds, and the *x & *y in the calling + * routine are affected. + */ +int +vxy(int *x, int *y) +{ + int flag = 0; + if (*x < 0) { + *x = 0; + flag++; + } + if (*y < 0) { + *y = 0; + flag++; + } + if (*x >= MAXX) { + *x = MAXX - 1; + flag++; + } + if (*y >= MAXY) { + *y = MAXY - 1; + flag++; + } + return (flag); +} + +/* + * dirpoly(spnum) Routine to ask for a direction and polymorph a monst + * int spnum; + * + * Subroutine to polymorph a monster and ask for the direction its in + * Enter with the spell number in spmun. + * Returns no value. + */ +static void +dirpoly(int spnum) +{ + int x, y, m; + if (spnum < 0 || spnum >= SPNUM) + return; /* bad args */ + if (isconfuse()) + return; /* if he is confused, he can't aim his magic */ + dirsub(&x, &y); + if (mitem[x][y] == 0) { + lprcat(" There wasn't anything there!"); + return; + } + ifblind(x, y); + if (nospell(spnum, mitem[x][y])) { + lasthx = x; + lasthy = y; + return; + } + while (monster[m = mitem[x][y] = rnd(MAXMONST + 7)].genocided); + hitp[x][y] = monster[m].hitpoints; + show1cell(x, y); /* show the new monster */ +} + +/* + * hitmonster(x,y) Function to hit a monster at the designated coordinates + * int x,y; + * + * This routine is used for a bash & slash type attack on a monster + * Enter with the coordinates of the monster in (x,y). + * Returns no value. + */ +void +hitmonster(int x, int y) +{ + int tmp, monst, damag = 0, flag; + if (c[TIMESTOP]) + return; /* not if time stopped */ + vxy(&x, &y); /* verify coordinates are within range */ + if ((monst = mitem[x][y]) == 0) + return; + hit3flag = 1; + ifblind(x, y); + tmp = monster[monst].armorclass + c[LEVEL] + c[DEXTERITY] + + c[WCLASS] / 4 - 12; + cursors(); + /* need at least random chance to hit */ + if ((rnd(20) < tmp - c[HARDGAME]) || (rnd(71) < 5)) { + lprcat("\nYou hit"); + flag = 1; + damag = fullhit(1); + if (damag < 9999) + damag = rnd(damag) + 1; + } else { + lprcat("\nYou missed"); + flag = 0; + } + lprcat(" the "); + lprcat(lastmonst); + if (flag) /* if the monster was hit */ + if ((monst == RUSTMONSTER) || (monst == DISENCHANTRESS) || (monst == CUBE)) + if (c[WIELD] > 0) + if (ivenarg[c[WIELD]] > -10) { + lprintf("\nYour weapon is dulled by the %s", lastmonst); + beep(); + --ivenarg[c[WIELD]]; + } + if (flag) + hitm(x, y, damag); + if (monst == VAMPIRE) + if (hitp[x][y] < 25) { + mitem[x][y] = BAT; + know[x][y] = 0; + } +} + +/* + * hitm(x,y,amt) Function to just hit a monster at a given coordinates + * int x,y,amt; + * + * Returns the number of hitpoints the monster absorbed + * This routine is used to specifically damage a monster at a location (x,y) + * Called by hitmonster(x,y) + */ +static int +hitm(int x, int y, int amt) +{ + int monst; + int hpoints, amt2; + vxy(&x, &y); /* verify coordinates are within range */ + amt2 = amt; /* save initial damage so we can return it */ + monst = mitem[x][y]; + if (c[HALFDAM]) + amt >>= 1; /* if half damage curse adjust damage points */ + if (amt <= 0) + amt2 = amt = 1; + lasthx = x; + lasthy = y; + stealth[x][y] = 1; /* make sure hitting monst breaks stealth + * condition */ + c[HOLDMONST] = 0; /* hit a monster breaks hold monster spell */ + switch (monst) { /* if a dragon and orb(s) of dragon slaying */ + case WHITEDRAGON: + case REDDRAGON: + case GREENDRAGON: + case BRONZEDRAGON: + case PLATINUMDRAGON: + case SILVERDRAGON: + amt *= 1 + (c[SLAYING] << 1); + break; + } + /* invincible monster fix is here */ + if (hitp[x][y] > monster[monst].hitpoints) + hitp[x][y] = monster[monst].hitpoints; + if ((hpoints = hitp[x][y]) <= amt) { +#ifdef EXTRA + c[MONSTKILLED]++; +#endif + lprintf("\nThe %s died!", lastmonst); + raiseexperience((long) monster[monst].experience); + amt = monster[monst].gold; + if (amt > 0) + dropgold(rnd(amt) + amt); + dropsomething(monst); + disappear(x, y); + bottomline(); + return (hpoints); + } + hitp[x][y] = hpoints - amt; + return (amt2); +} + +/* + * hitplayer(x,y) Function for the monster to hit the player from (x,y) + * int x,y; + * + * Function for the monster to hit the player with monster at location x,y + * Returns nothing of value. + */ +void +hitplayer(int x, int y) +{ + int dam, tmp, mster, bias; + vxy(&x, &y); /* verify coordinates are within range */ + lastnum = mster = mitem[x][y]; + /* + * spirit nagas and poltergeists do nothing if scarab of negate + * spirit + */ + if (c[NEGATESPIRIT] || c[SPIRITPRO]) + if ((mster == POLTERGEIST) || (mster == SPIRITNAGA)) + return; + /* if undead and cube of undead control */ + if (c[CUBEofUNDEAD] || c[UNDEADPRO]) + if ((mster == VAMPIRE) || (mster == WRAITH) || (mster == ZOMBIE)) + return; + if ((know[x][y] & 1) == 0) { + know[x][y] = 1; + show1cell(x, y); + } + bias = (c[HARDGAME]) + 1; + hitflag = hit2flag = hit3flag = 1; + yrepcount = 0; + cursors(); + ifblind(x, y); + if (c[INVISIBILITY]) + if (rnd(33) < 20) { + lprintf("\nThe %s misses wildly", lastmonst); + return; + } + if (c[CHARMCOUNT]) + if (rnd(30) + 5 * monster[mster].level - c[CHARISMA] < 30) { + lprintf("\nThe %s is awestruck at your magnificence!", lastmonst); + return; + } + if (mster == BAT) + dam = 1; + else { + dam = monster[mster].damage; + dam += rnd((int) ((dam < 1) ? 1 : dam)) + monster[mster].level; + } + tmp = 0; + if (monster[mster].attack > 0) + if (((dam + bias + 8) > c[AC]) || (rnd((int) ((c[AC] > 0) ? c[AC] : 1)) == 1)) { + if (spattack(monster[mster].attack, x, y)) { + flushall(); + return; + } + tmp = 1; + bias -= 2; + cursors(); + } + if (((dam + bias) > c[AC]) || (rnd((int) ((c[AC] > 0) ? c[AC] : 1)) == 1)) { + lprintf("\n The %s hit you ", lastmonst); + tmp = 1; + if ((dam -= c[AC]) < 0) + dam = 0; + if (dam > 0) { + losehp(dam); + bottomhp(); + flushall(); + } + } + if (tmp == 0) + lprintf("\n The %s missed ", lastmonst); +} + +/* + * dropsomething(monst) Function to create an object when a monster dies + * int monst; + * + * Function to create an object near the player when certain monsters are killed + * Enter with the monster number + * Returns nothing of value. + */ +static void +dropsomething(int monst) +{ + switch (monst) { + case ORC: + case NYMPH: + case ELF: + case TROGLODYTE: + case TROLL: + case ROTHE: + case VIOLETFUNGI: + case PLATINUMDRAGON: + case GNOMEKING: + case REDDRAGON: + something(level); + return; + + case LEPRECHAUN: + if (rnd(101) >= 75) + creategem(); + if (rnd(5) == 1) + dropsomething(LEPRECHAUN); + return; + } +} + +/* + * dropgold(amount) Function to drop some gold around player + * int amount; + * + * Enter with the number of gold pieces to drop + * Returns nothing of value. + */ +void +dropgold(int amount) +{ + if (amount > 250) + createitem(OMAXGOLD, amount / 100); + else + createitem(OGOLDPILE, amount); +} + +/* + * something(level) Function to create a random item around player + * int level; + * + * Function to create an item from a designed probability around player + * Enter with the cave level on which something is to be dropped + * Returns nothing of value. + */ +void +something(int cavelevel) +{ + int j; + int i; + if (cavelevel < 0 || cavelevel > MAXLEVEL + MAXVLEVEL) + return; /* correct level? */ + if (rnd(101) < 8) + something(cavelevel); /* possibly more than one item */ + j = newobject(cavelevel, &i); + createitem(j, i); +} + +/* + * newobject(lev,i) Routine to return a randomly selected new object + * int lev,*i; + * + * Routine to return a randomly selected object to be created + * Returns the object number created, and sets *i for its argument + * Enter with the cave level and a pointer to the items arg + */ +static char nobjtab[] = { + 0, OSCROLL, OSCROLL, OSCROLL, OSCROLL, OPOTION, OPOTION, + OPOTION, OPOTION, OGOLDPILE, OGOLDPILE, OGOLDPILE, OGOLDPILE, + OBOOK, OBOOK, OBOOK, OBOOK, ODAGGER, ODAGGER, ODAGGER, + OLEATHER, OLEATHER, OLEATHER, OREGENRING, OPROTRING, + OENERGYRING, ODEXRING, OSTRRING, OSPEAR, OBELT, ORING, + OSTUDLEATHER, OSHIELD, OFLAIL, OCHAIN, O2SWORD, OPLATE, + OLONGSWORD}; + +int +newobject(int lev, int *i) +{ + int tmp = 32, j; + if (level < 0 || level > MAXLEVEL + MAXVLEVEL) + return (0); /* correct level? */ + if (lev > 6) + tmp = 37; + else if (lev > 4) + tmp = 35; + j = nobjtab[tmp = rnd(tmp)]; /* the object type */ + switch (tmp) { + case 1: + case 2: + case 3: + case 4: + *i = newscroll(); + break; + case 5: + case 6: + case 7: + case 8: + *i = newpotion(); + break; + case 9: + case 10: + case 11: + case 12: + *i = rnd((lev + 1) * 10) + lev * 10 + 10; + break; + case 13: + case 14: + case 15: + case 16: + *i = lev; + break; + case 17: + case 18: + case 19: + if (!(*i = newdagger())) + return (0); + break; + case 20: + case 21: + case 22: + if (!(*i = newleather())) + return (0); + break; + case 23: + case 32: + case 35: + *i = rund(lev / 3 + 1); + break; + case 24: + case 26: + *i = rnd(lev / 4 + 1); + break; + case 25: + *i = rund(lev / 4 + 1); + break; + case 27: + *i = rnd(lev / 2 + 1); + break; + case 30: + case 33: + *i = rund(lev / 2 + 1); + break; + case 28: + *i = rund(lev / 3 + 1); + if (*i == 0) + return (0); + break; + case 29: + case 31: + *i = rund(lev / 2 + 1); + if (*i == 0) + return (0); + break; + case 34: + *i = newchain(); + break; + case 36: + *i = newplate(); + break; + case 37: + *i = newsword(); + break; + } + return (j); +} + +/* + * spattack(atckno,xx,yy) Function to process special attacks from monsters + * int atckno,xx,yy; + * + * Enter with the special attack number, and the coordinates (xx,yy) + * of the monster that is special attacking + * Returns 1 if must do a show1cell(xx,yy) upon return, 0 otherwise + * + * atckno monster effect + * --------------------------------------------------- + * 0 none + * 1 rust monster eat armor + * 2 hell hound breathe light fire + * 3 dragon breathe fire + * 4 giant centipede weakening sing + * 5 white dragon cold breath + * 6 wraith drain level + * 7 waterlord water gusher + * 8 leprechaun steal gold + * 9 disenchantress disenchant weapon or armor + * 10 ice lizard hits with barbed tail + * 11 umber hulk confusion + * 12 spirit naga cast spells taken from special attacks + * 13 platinum dragon psionics + * 14 nymph steal objects + * 15 bugbear bite + * 16 osequip bite + * + * char rustarm[ARMORTYPES][2]; + * special array for maximum rust damage to armor from rustmonster + * format is: { armor type , minimum attribute + */ +#define ARMORTYPES 6 +static char rustarm[ARMORTYPES][2] = { + { OSTUDLEATHER, -2 }, + { ORING, -4 }, + { OCHAIN, -5 }, + { OSPLINT, -6 }, + { OPLATE, -8 }, + { OPLATEARMOR, -9} +}; +static char spsel[] = {1, 2, 3, 5, 6, 8, 9, 11, 13, 14}; +static int +spattack(int x, int xx, int yy) +{ + int i, j = 0, k, m; + const char *p = NULL; + + if (c[CANCELLATION]) + return (0); + vxy(&xx, &yy); /* verify x & y coordinates */ + switch (x) { + case 1: /* rust your armor, j=1 when rusting has occurred */ + m = k = c[WEAR]; + if ((i = c[SHIELD]) != -1) { + if (--ivenarg[i] < -1) + ivenarg[i] = -1; + else + j = 1; + } + if ((j == 0) && (k != -1)) { + m = iven[k]; + for (i = 0; i < ARMORTYPES; i++) + /* find his armor in table */ + if (m == rustarm[i][0]) { + if (--ivenarg[k] < rustarm[i][1]) + ivenarg[k] = rustarm[i][1]; + else + j = 1; + break; + } + } + if (j == 0) /* if rusting did not occur */ + switch (m) { + case OLEATHER: + p = "\nThe %s hit you -- You're lucky you have leather on"; + break; + case OSSPLATE: + p = "\nThe %s hit you -- You're fortunate to have stainless steel armor!"; + break; + } + else { + beep(); + p = "\nThe %s hit you -- your armor feels weaker"; + } + break; + + case 2: + i = rnd(15) + 8 - c[AC]; +spout: p = "\nThe %s breathes fire at you!"; + if (c[FIRERESISTANCE]) + p = "\nThe %s's flame doesn't faze you!"; + else +spout2: if (p) { + lprintf(p, lastmonst); + beep(); + } + checkloss(i); + return (0); + + case 3: + i = rnd(20) + 25 - c[AC]; + goto spout; + + case 4: + if (c[STRENGTH] > 3) { + p = "\nThe %s stung you! You feel weaker"; + beep(); + --c[STRENGTH]; + } else + p = "\nThe %s stung you!"; + break; + + case 5: + p = "\nThe %s blasts you with his cold breath"; + i = rnd(15) + 18 - c[AC]; + goto spout2; + + case 6: + lprintf("\nThe %s drains you of your life energy!", lastmonst); + loselevel(); + beep(); + return (0); + + case 7: + p = "\nThe %s got you with a gusher!"; + i = rnd(15) + 25 - c[AC]; + goto spout2; + + case 8: + if (c[NOTHEFT]) + return (0); /* he has a device of no theft */ + if (c[GOLD]) { + p = "\nThe %s hit you -- Your purse feels lighter"; + if (c[GOLD] > 32767) + c[GOLD] >>= 1; + else + c[GOLD] -= rnd((int) (1 + (c[GOLD] >> 1))); + if (c[GOLD] < 0) + c[GOLD] = 0; + } else + p = "\nThe %s couldn't find any gold to steal"; + lprintf(p, lastmonst); + disappear(xx, yy); + beep(); + bottomgold(); + return (1); + + case 9: + for (j = 50;;) {/* disenchant */ + i = rund(26); + m = iven[i]; /* randomly select item */ + if (m > 0 && ivenarg[i] > 0 && m != OSCROLL && m != OPOTION) { + if ((ivenarg[i] -= 3) < 0) + ivenarg[i] = 0; + lprintf("\nThe %s hits you -- you feel a sense of loss", lastmonst); + srcount = 0; + beep(); + show3(i); + bottomline(); + return (0); + } + if (--j <= 0) { + p = "\nThe %s nearly misses"; + break; + } + break; + } + break; + + case 10: + p = "\nThe %s hit you with his barbed tail"; + i = rnd(25) - c[AC]; + goto spout2; + + case 11: + p = "\nThe %s has confused you"; + beep(); + c[CONFUSE] += 10 + rnd(10); + break; + + case 12: /* performs any number of other special + * attacks */ + return (spattack(spsel[rund(10)], xx, yy)); + + case 13: + p = "\nThe %s flattens you with his psionics!"; + i = rnd(15) + 30 - c[AC]; + goto spout2; + + case 14: + if (c[NOTHEFT]) + return (0); /* he has device of no theft */ + if (emptyhanded() == 1) { + p = "\nThe %s couldn't find anything to steal"; + break; + } + lprintf("\nThe %s picks your pocket and takes:", lastmonst); + beep(); + if (stealsomething() == 0) + lprcat(" nothing"); + disappear(xx, yy); + bottomline(); + return (1); + + case 15: + i = rnd(10) + 5 - c[AC]; +spout3: p = "\nThe %s bit you!"; + goto spout2; + + case 16: + i = rnd(15) + 10 - c[AC]; + goto spout3; + }; + if (p) { + lprintf(p, lastmonst); + bottomline(); + } + return (0); +} + +/* + * checkloss(x) Routine to subtract hp from user and flag bottomline display + * int x; + * + * Routine to subtract hitpoints from the user and flag the bottomline display + * Enter with the number of hit points to lose + * Note: if x > c[HP] this routine could kill the player! + */ +void +checkloss(int x) +{ + if (x > 0) { + losehp(x); + bottomhp(); + } +} + +/* + * annihilate() Routine to annihilate all monsters around player (playerx,playery) + * + * Gives player experience, but no dropped objects + * Returns the experience gained from all monsters killed + */ +int +annihilate(void) +{ + int i, j; + long k; + u_char *p; + for (k = 0, i = playerx - 1; i <= playerx + 1; i++) + for (j = playery - 1; j <= playery + 1; j++) + if (!vxy(&i, &j)) { /* if not out of bounds */ + if (*(p = &mitem[i][j])) { /* if a monster there */ + if (*p < DEMONLORD + 2) { + k += monster[*p].experience; + *p = know[i][j] = 0; + } else { + lprintf("\nThe %s barely escapes being annihilated!", monster[*p].name); + hitp[i][j] = (hitp[i][j] >> 1) + 1; /* lose half hit points */ + } + } + } + if (k > 0) { + lprcat("\nYou hear loud screams of agony!"); + raiseexperience((long) k); + } + return (k); +} + +/* + * newsphere(x,y,dir,lifetime) Function to create a new sphere of annihilation + * int x,y,dir,lifetime; + * + * Enter with the coordinates of the sphere in x,y + * the direction (0-8 diroffx format) in dir, and the lifespan of the + * sphere in lifetime (in turns) + * Returns the number of spheres currently in existence + */ +int +newsphere(int x, int y, int dir, int life) +{ + int m; + struct sphere *sp; + if (((sp = (struct sphere *) malloc(sizeof(struct sphere)))) == 0) + return (c[SPHCAST]); /* can't malloc, therefore failure */ + if (dir >= 9) + dir = 0; /* no movement if direction not found */ + if (level == 0) + vxy(&x, &y); /* don't go out of bounds */ + else { + if (x < 1) + x = 1; + if (x >= MAXX - 1) + x = MAXX - 2; + if (y < 1) + y = 1; + if (y >= MAXY - 1) + y = MAXY - 2; + } + if ((m = mitem[x][y]) >= DEMONLORD + 4) { /* demons dispel spheres */ + know[x][y] = 1; + show1cell(x, y);/* show the demon (ha ha) */ + cursors(); + lprintf("\nThe %s dispels the sphere!", monster[m].name); + beep(); + rmsphere(x, y); /* remove any spheres that are here */ + free(sp); + return (c[SPHCAST]); + } + if (m == DISENCHANTRESS) { /* disenchantress cancels spheres */ + cursors(); + lprintf("\nThe %s causes cancellation of the sphere!", monster[m].name); + beep(); +boom: sphboom(x, y); /* blow up stuff around sphere */ + rmsphere(x, y); /* remove any spheres that are here */ + free(sp); + return (c[SPHCAST]); + } + if (c[CANCELLATION]) { /* cancellation cancels spheres */ + cursors(); + lprcat("\nAs the cancellation takes effect, you hear a great earth shaking blast!"); + beep(); + goto boom; + } + if (item[x][y] == OANNIHILATION) { /* collision of spheres + * detonates spheres */ + cursors(); + lprcat("\nTwo spheres of annihilation collide! You hear a great earth shaking blast!"); + beep(); + rmsphere(x, y); + goto boom; + } + if (playerx == x && playery == y) { /* collision of sphere and + * player! */ + cursors(); + lprcat("\nYou have been enveloped by the zone of nothingness!\n"); + beep(); + rmsphere(x, y); /* remove any spheres that are here */ + nap(4000); + died(258); + } + item[x][y] = OANNIHILATION; + mitem[x][y] = 0; + know[x][y] = 1; + show1cell(x, y); /* show the new sphere */ + sp->x = x; + sp->y = y; + sp->lev = level; + sp->dir = dir; + sp->lifetime = life; + sp->p = 0; + if (spheres == 0) + spheres = sp; /* if first node in the sphere list */ + else { /* add sphere to beginning of linked list */ + sp->p = spheres; + spheres = sp; + } + return (++c[SPHCAST]); /* one more sphere in the world */ +} + +/* + * rmsphere(x,y) Function to delete a sphere of annihilation from list + * int x,y; + * + * Enter with the coordinates of the sphere (on current level) + * Returns the number of spheres currently in existence + */ +int +rmsphere(int x, int y) +{ + struct sphere *sp, *sp2 = 0; + for (sp = spheres; sp; sp2 = sp, sp = sp->p) + if (level == sp->lev) /* is sphere on this level? */ + if ((x == sp->x) && (y == sp->y)) { /* locate sphere at this + * location */ + item[x][y] = mitem[x][y] = 0; + know[x][y] = 1; + show1cell(x, y); /* show the now missing + * sphere */ + --c[SPHCAST]; + if (sp == spheres) { + sp2 = sp; + spheres = sp->p; + free((char *) sp2); + } else { + if (sp2) + sp2->p = sp->p; + free((char *) sp); + } + break; + } + return (c[SPHCAST]); /* return number of spheres in the world */ +} + +/* + * sphboom(x,y) Function to perform the effects of a sphere detonation + * int x,y; + * + * Enter with the coordinates of the blast, Returns no value + */ +static void +sphboom(int x, int y) +{ + int i, j; + if (c[HOLDMONST]) + c[HOLDMONST] = 1; + if (c[CANCELLATION]) + c[CANCELLATION] = 1; + for (j = max(1, x - 2); j < min(x + 3, MAXX - 1); j++) + for (i = max(1, y - 2); i < min(y + 3, MAXY - 1); i++) { + item[j][i] = mitem[j][i] = 0; + show1cell(j, i); + if (playerx == j && playery == i) { + cursors(); + beep(); + lprcat("\nYou were too close to the sphere!"); + nap(3000); + died(283); /* player killed in explosion */ + } + } +} + +/* + * genmonst() Function to ask for monster and genocide from game + * + * This is done by setting a flag in the monster[] structure + */ +static void +genmonst(void) +{ + int i, j; + cursors(); + lprcat("\nGenocide what monster? "); + for (i = 0; (!isalpha(i)) && (i != ' '); i = ttgetch()); + lprc(i); + for (j = 0; j < MAXMONST; j++) /* search for the monster type */ + if (monstnamelist[j] == i) { /* have we found it? */ + monster[j].genocided = 1; /* genocided from game */ + lprintf(" There will be no more %s's", monster[j].name); + /* now wipe out monsters on this level */ + newcavelevel(level); + draws(0, MAXX, 0, MAXY); + bot_linex(); + return; + } + lprcat(" You sense failure!"); +} diff --git a/larn/moreobj.c b/larn/moreobj.c new file mode 100644 index 0000000..d36e6e1 --- /dev/null +++ b/larn/moreobj.c @@ -0,0 +1,296 @@ +/* $NetBSD: moreobj.c,v 1.12 2012/06/19 05:30:43 dholland Exp $ */ + +/* + * moreobj.c Larn is copyrighted 1986 by Noah Morgan. + * + * Routines in this file: + * + * oaltar() othrone() ochest() ofountain() + */ +#include <sys/cdefs.h> +#ifndef lint +__RCSID("$NetBSD: moreobj.c,v 1.12 2012/06/19 05:30:43 dholland Exp $"); +#endif /* not lint */ +#include <stdlib.h> +#include <unistd.h> +#include "header.h" +#include "extern.h" + +static void fch(int, long *); + +/* + * subroutine to process an altar object + */ +void +oaltar(void) +{ + + lprcat("\nDo you (p) pray (d) desecrate"); + iopts(); + while (1) { + while (1) + switch (ttgetch()) { + case 'p': + lprcat(" pray\nDo you (m) give money or (j) just pray? "); + while (1) + switch (ttgetch()) { + case 'j': + act_just_pray(); + return; + + case 'm': + act_donation_pray(); + return; + + case '\33': + return; + }; + + case 'd': + lprcat(" desecrate"); + act_desecrate_altar(); + return; + + case 'i': + case '\33': + ignore(); + act_ignore_altar(); + return; + }; + } +} + +/* + subroutine to process a throne object + */ +void +othrone(int arg) +{ + + lprcat("\nDo you (p) pry off jewels, (s) sit down"); + iopts(); + while (1) { + while (1) + switch (ttgetch()) { + case 'p': + lprcat(" pry off"); + act_remove_gems(arg); + return; + + case 's': + lprcat(" sit down"); + act_sit_throne(arg); + return; + + case 'i': + case '\33': + ignore(); + return; + }; + } +} + +void +odeadthrone(void) +{ + int k; + + lprcat("\nDo you (s) sit down"); + iopts(); + while (1) { + while (1) + switch (ttgetch()) { + case 's': + lprcat(" sit down"); + k = rnd(101); + if (k < 35) { + lprcat("\nZaaaappp! You've been teleported!\n"); + beep(); + oteleport(0); + } else + lprcat("\nnothing happens"); + return; + + case 'i': + case '\33': + ignore(); + return; + }; + } +} + +/* + subroutine to process a throne object + */ +void +ochest(void) +{ + + lprcat("\nDo you (t) take it, (o) try to open it"); + iopts(); + while (1) { + while (1) + switch (ttgetch()) { + case 'o': + lprcat(" open it"); + act_open_chest(playerx, playery); + return; + + case 't': + lprcat(" take"); + if (take(OCHEST, iarg[playerx][playery]) == 0) + item[playerx][playery] = know[playerx][playery] = 0; + return; + + case 'i': + case '\33': + ignore(); + return; + }; + } +} + +/* + process a fountain object + */ +void +ofountain(void) +{ + + cursors(); + lprcat("\nDo you (d) drink, (w) wash yourself"); + iopts(); + while (1) + switch (ttgetch()) { + case 'd': + lprcat("drink"); + act_drink_fountain(); + return; + + case '\33': + case 'i': + ignore(); + return; + + case 'w': + lprcat("wash yourself"); + act_wash_fountain(); + return; + } +} + +/* + *** + FCH + *** + + subroutine to process an up/down of a character attribute for ofountain + */ +static void +fch(int how, long *x) +{ + if (how < 0) { + lprcat(" went down by one!"); + --(*x); + } else { + lprcat(" went up by one!"); + (*x)++; + } + bottomline(); +} + +/* + a subroutine to raise or lower character levels + if x > 0 they are raised if x < 0 they are lowered + */ +void +fntchange(int how) +{ + long j; + lprc('\n'); + switch (rnd(9)) { + case 1: + lprcat("Your strength"); + fch(how, &c[0]); + break; + case 2: + lprcat("Your intelligence"); + fch(how, &c[1]); + break; + case 3: + lprcat("Your wisdom"); + fch(how, &c[2]); + break; + case 4: + lprcat("Your constitution"); + fch(how, &c[3]); + break; + case 5: + lprcat("Your dexterity"); + fch(how, &c[4]); + break; + case 6: + lprcat("Your charm"); + fch(how, &c[5]); + break; + case 7: + j = rnd(level + 1); + if (how < 0) { + lprintf("You lose %ld hit point", (long) j); + if (j > 1) + lprcat("s!"); + else + lprc('!'); + losemhp((int) j); + } else { + lprintf("You gain %ld hit point", (long) j); + if (j > 1) + lprcat("s!"); + else + lprc('!'); + raisemhp((int) j); + } + bottomline(); + break; + + case 8: + j = rnd(level + 1); + if (how > 0) { + lprintf("You just gained %ld spell", (long) j); + raisemspells((int) j); + if (j > 1) + lprcat("s!"); + else + lprc('!'); + } else { + lprintf("You just lost %ld spell", (long) j); + losemspells((int) j); + if (j > 1) + lprcat("s!"); + else + lprc('!'); + } + bottomline(); + break; + + case 9: + j = 5 * rnd((level + 1) * (level + 1)); + if (how < 0) { + lprintf("You just lost %ld experience point", (long) j); + if (j > 1) + lprcat("s!"); + else + lprc('!'); + loseexperience((long) j); + } else { + lprintf("You just gained %ld experience point", (long) j); + if (j > 1) + lprcat("s!"); + else + lprc('!'); + raiseexperience((long) j); + } + break; + } + cursors(); +} diff --git a/larn/movem.c b/larn/movem.c new file mode 100644 index 0000000..b9745fc --- /dev/null +++ b/larn/movem.c @@ -0,0 +1,447 @@ +/* $NetBSD: movem.c,v 1.9 2012/06/19 05:30:43 dholland Exp $ */ + +/* + * movem.c (move monster) Larn is copyrighted 1986 by Noah Morgan. + * + * Here are the functions in this file: + * + * movemonst() Routine to move the monsters toward the player + * movemt(x,y) Function to move a monster at (x,y) -- must determine where + * mmove(x,y,xd,yd) Function to actually perform the monster movement + * movsphere() Function to look for and move spheres of annihilation + */ +#include <sys/cdefs.h> +#ifndef lint +__RCSID("$NetBSD: movem.c,v 1.9 2012/06/19 05:30:43 dholland Exp $"); +#endif /* not lint */ + +#include "header.h" +#include "extern.h" + +static void movemt(int, int); +static void mmove(int, int, int, int); +static void movsphere(void); + +/* + * movemonst() Routine to move the monsters toward the player + * + * This routine has the responsibility to determine which monsters are to + * move, and call movemt() to do the move. + * Returns no value. + */ +static short w1[9], w1x[9], w1y[9]; +static int tmp1, tmp2, tmp3, tmp4, distance; +void +movemonst(void) +{ + int i, j; + if (c[TIMESTOP]) + return; /* no action if time is stopped */ + if (c[HASTESELF]) + if ((c[HASTESELF] & 1) == 0) + return; + if (spheres) + movsphere(); /* move the spheres of annihilation if any */ + if (c[HOLDMONST]) + return; /* no action if monsters are held */ + + if (c[AGGRAVATE]) { /* determine window of monsters to move */ + tmp1 = playery - 5; + tmp2 = playery + 6; + tmp3 = playerx - 10; + tmp4 = playerx + 11; + distance = 40; /* depth of intelligent monster movement */ + } else { + tmp1 = playery - 3; + tmp2 = playery + 4; + tmp3 = playerx - 5; + tmp4 = playerx + 6; + distance = 17; /* depth of intelligent monster movement */ + } + + if (level == 0) { /* if on outside level monsters can move in + * perimeter */ + if (tmp1 < 0) + tmp1 = 0; + if (tmp2 > MAXY) + tmp2 = MAXY; + if (tmp3 < 0) + tmp3 = 0; + if (tmp4 > MAXX) + tmp4 = MAXX; + } else { /* if in a dungeon monsters can't be on the + * perimeter (wall there) */ + if (tmp1 < 1) + tmp1 = 1; + if (tmp2 > MAXY - 1) + tmp2 = MAXY - 1; + if (tmp3 < 1) + tmp3 = 1; + if (tmp4 > MAXX - 1) + tmp4 = MAXX - 1; + } + + for (j = tmp1; j < tmp2; j++) /* now reset monster moved flags */ + for (i = tmp3; i < tmp4; i++) + moved[i][j] = 0; + moved[lasthx][lasthy] = 0; + + if (c[AGGRAVATE] || !c[STEALTH]) { /* who gets moved? split for + * efficiency */ + for (j = tmp1; j < tmp2; j++) /* look thru all locations in + * window */ + for (i = tmp3; i < tmp4; i++) + if (mitem[i][j]) /* if there is a monster + * to move */ + if (moved[i][j] == 0) /* if it has not already + * been moved */ + movemt(i, j); /* go and move the + * monster */ + } else { /* not aggravated and not stealth */ + for (j = tmp1; j < tmp2; j++) /* look thru all locations in + * window */ + for (i = tmp3; i < tmp4; i++) + if (mitem[i][j]) /* if there is a monster + * to move */ + if (moved[i][j] == 0) /* if it has not already + * been moved */ + if (stealth[i][j]) /* if it is asleep due + * to stealth */ + movemt(i, j); /* go and move the + * monster */ + } + + if (mitem[lasthx][lasthy]) { /* now move monster last hit by + * player if not already moved */ + if (moved[lasthx][lasthy] == 0) { /* if it has not already + * been moved */ + movemt(lasthx, lasthy); + lasthx = w1x[0]; + lasthy = w1y[0]; + } + } +} + +/* + * movemt(x,y) Function to move a monster at (x,y) -- must determine where + * int x,y; + * + * This routine is responsible for determining where one monster at (x,y) will + * move to. Enter with the monsters coordinates in (x,y). + * Returns no value. + */ +static int tmpitem, xl, xh, yl, yh; +static void +movemt(int i, int j) +{ + int k, m, z, tmp, xtmp, ytmp, monst; + switch (monst = mitem[i][j]) { /* for half speed monsters */ + case TROGLODYTE: + case HOBGOBLIN: + case METAMORPH: + case XVART: + case INVISIBLESTALKER: + case ICELIZARD: + if ((gltime & 1) == 1) + return; + }; + + if (c[SCAREMONST]) { /* choose destination randomly if scared */ + if ((xl = i + rnd(3) - 2) < 0) + xl = 0; + if (xl >= MAXX) + xl = MAXX - 1; + if ((yl = j + rnd(3) - 2) < 0) + yl = 0; + if (yl >= MAXY) + yl = MAXY - 1; + if ((tmp = item[xl][yl]) != OWALL) + if (mitem[xl][yl] == 0) + if ((mitem[i][j] != VAMPIRE) || (tmpitem != OMIRROR)) + if (tmp != OCLOSEDDOOR) + mmove(i, j, xl, yl); + return; + } + if (monster[monst].intelligence > 10 - c[HARDGAME]) { /* if smart monster */ + /* intelligent movement here -- first setup screen array */ + xl = tmp3 - 2; + yl = tmp1 - 2; + xh = tmp4 + 2; + yh = tmp2 + 2; + vxy(&xl, &yl); + vxy(&xh, &yh); + for (k = yl; k < yh; k++) + for (m = xl; m < xh; m++) { + switch (item[m][k]) { + case OWALL: + case OPIT: + case OTRAPARROW: + case ODARTRAP: + case OCLOSEDDOOR: + case OTRAPDOOR: + case OTELEPORTER: + smm: screen[m][k] = 127; + break; + case OMIRROR: + if (mitem[m][k] == VAMPIRE) + goto smm; + default: + screen[m][k] = 0; + break; + }; + } + screen[playerx][playery] = 1; + + /* + * now perform proximity ripple from playerx,playery to + * monster + */ + xl = tmp3 - 1; + yl = tmp1 - 1; + xh = tmp4 + 1; + yh = tmp2 + 1; + vxy(&xl, &yl); + vxy(&xh, &yh); + for (tmp = 1; tmp < distance; tmp++) /* only up to 20 squares + * away */ + for (k = yl; k < yh; k++) + for (m = xl; m < xh; m++) + if (screen[m][k] == tmp) /* if find proximity n + * advance it */ + for (z = 1; z < 9; z++) { /* go around in a circle */ + if (screen[xtmp = m + diroffx[z]][ytmp = k + diroffy[z]] == 0) + screen[xtmp][ytmp] = tmp + 1; + if (xtmp == i && ytmp == j) + goto out; + } + +out: if (tmp < distance) /* did find connectivity */ + /* now select lowest value around playerx,playery */ + for (z = 1; z < 9; z++) /* go around in a circle */ + if (screen[xl = i + diroffx[z]][yl = j + diroffy[z]] == tmp) + if (!mitem[xl][yl]) { + mmove(i, j, w1x[0] = xl, w1y[0] = yl); + return; + } + } + /* dumb monsters move here */ + xl = i - 1; + yl = j - 1; + xh = i + 2; + yh = j + 2; + if (i < playerx) + xl++; + else if (i > playerx) + --xh; + if (j < playery) + yl++; + else if (j > playery) + --yh; + for (k = 0; k < 9; k++) + w1[k] = 10000; + + for (k = xl; k < xh; k++) + for (m = yl; m < yh; m++) { /* for each square compute + * distance to player */ + tmp = k - i + 4 + 3 * (m - j); + tmpitem = item[k][m]; + if (tmpitem != OWALL || (k == playerx && m == playery)) + if (mitem[k][m] == 0) + if ((mitem[i][j] != VAMPIRE) || (tmpitem != OMIRROR)) + if (tmpitem != OCLOSEDDOOR) { + w1[tmp] = (playerx - k) * (playerx - k) + (playery - m) * (playery - m); + w1x[tmp] = k; + w1y[tmp] = m; + } + } + + tmp = 0; + for (k = 1; k < 9; k++) + if (w1[tmp] > w1[k]) + tmp = k; + + if (w1[tmp] < 10000) + if ((i != w1x[tmp]) || (j != w1y[tmp])) + mmove(i, j, w1x[tmp], w1y[tmp]); +} + +/* + * mmove(x,y,xd,yd) Function to actually perform the monster movement + * int x,y,xd,yd; + * + * Enter with the from coordinates in (x,y) and the destination coordinates + * in (xd,yd). + */ +static void +mmove(int aa, int bb, int cc, int dd) +{ + int tmp, i, flag; + const char *who = NULL; + + flag = 0; /* set to 1 if monster hit by arrow trap */ + if ((cc == playerx) && (dd == playery)) { + hitplayer(aa, bb); + moved[aa][bb] = 1; + return; + } + i = item[cc][dd]; + if ((i == OPIT) || (i == OTRAPDOOR)) + switch (mitem[aa][bb]) { + case SPIRITNAGA: + case PLATINUMDRAGON: + case WRAITH: + case VAMPIRE: + case SILVERDRAGON: + case POLTERGEIST: + case DEMONLORD: + case DEMONLORD + 1: + case DEMONLORD + 2: + case DEMONLORD + 3: + case DEMONLORD + 4: + case DEMONLORD + 5: + case DEMONLORD + 6: + case DEMONPRINCE: + break; + + default: + mitem[aa][bb] = 0; /* fell in a pit or trapdoor */ + }; + tmp = mitem[cc][dd] = mitem[aa][bb]; + if (i == OANNIHILATION) { + if (tmp >= DEMONLORD + 3) { /* demons dispel spheres */ + cursors(); + lprintf("\nThe %s dispels the sphere!", monster[tmp].name); + rmsphere(cc, dd); /* delete the sphere */ + } else + i = tmp = mitem[cc][dd] = 0; + } + stealth[cc][dd] = 1; + if ((hitp[cc][dd] = hitp[aa][bb]) < 0) + hitp[cc][dd] = 1; + mitem[aa][bb] = 0; + moved[cc][dd] = 1; + if (tmp == LEPRECHAUN) + switch (i) { + case OGOLDPILE: + case OMAXGOLD: + case OKGOLD: + case ODGOLD: + case ODIAMOND: + case ORUBY: + case OEMERALD: + case OSAPPHIRE: + item[cc][dd] = 0; /* leprechaun takes gold */ + }; + + if (tmp == TROLL) /* if a troll regenerate him */ + if ((gltime & 1) == 0) + if (monster[tmp].hitpoints > hitp[cc][dd]) + hitp[cc][dd]++; + + if (i == OTRAPARROW) { /* arrow hits monster */ + who = "An arrow"; + if ((hitp[cc][dd] -= rnd(10) + level) <= 0) { + mitem[cc][dd] = 0; + flag = 2; + } else + flag = 1; + } + if (i == ODARTRAP) { /* dart hits monster */ + who = "A dart"; + if ((hitp[cc][dd] -= rnd(6)) <= 0) { + mitem[cc][dd] = 0; + flag = 2; + } else + flag = 1; + } + if (i == OTELEPORTER) { /* monster hits teleport trap */ + flag = 3; + fillmonst(mitem[cc][dd]); + mitem[cc][dd] = 0; + } + if (c[BLINDCOUNT]) + return; /* if blind don't show where monsters are */ + if (know[cc][dd] & 1) { + if (flag) + cursors(); + switch (flag) { + case 1: + lprintf("\n%s hits the %s", who, monster[tmp].name); + beep(); + break; + case 2: + lprintf("\n%s hits and kills the %s", + who, monster[tmp].name); + beep(); + break; + case 3: + lprintf("\nThe %s gets teleported", monster[tmp].name); + beep(); + break; + } + } + /* + * if (yrepcount>1) { know[aa][bb] &= 2; know[cc][dd] &= 2; return; + * } + */ + if (know[aa][bb] & 1) + show1cell(aa, bb); + if (know[cc][dd] & 1) + show1cell(cc, dd); +} + +/* + * movsphere() Function to look for and move spheres of annihilation + * + * This function works on the sphere linked list, first duplicating the list + * (the act of moving changes the list), then processing each sphere in order + * to move it. They eat anything in their way, including stairs, volcanic + * shafts, potions, etc, except for upper level demons, who can dispel + * spheres. + * No value is returned. + */ +#define SPHMAX 20 /* maximum number of spheres movsphere can + * handle */ +static void +movsphere(void) +{ + int x, y, dir, len; + struct sphere *sp, *sp2; + struct sphere sph[SPHMAX]; + + /* first duplicate sphere list */ + for (sp = 0, x = 0, sp2 = spheres; sp2; sp2 = sp2->p) /* look through sphere + * list */ + if (sp2->lev == level) { /* only if this level */ + sph[x] = *sp2; + sph[x++].p = 0; /* copy the struct */ + if (x > 1) + sph[x - 2].p = &sph[x - 1]; /* link pointers */ + } + if (x) + sp = sph; /* if any spheres, point to them */ + else + return; /* no spheres */ + + for (sp = sph; sp; sp = sp->p) { /* look through sphere list */ + x = sp->x; + y = sp->y; + if (item[x][y] != OANNIHILATION) + continue; /* not really there */ + if (--(sp->lifetime) < 0) { /* has sphere run out of gas? */ + rmsphere(x, y); /* delete sphere */ + continue; + } + switch (rnd((int) max(7, c[INTELLIGENCE] >> 1))) { /* time to move the + * sphere */ + case 1: + case 2: /* change direction to a random one */ + sp->dir = rnd(8); + default: /* move in normal direction */ + dir = sp->dir; + len = sp->lifetime; + rmsphere(x, y); + newsphere(x + diroffx[dir], y + diroffy[dir], dir, len); + }; + } +} diff --git a/larn/nap.c b/larn/nap.c new file mode 100644 index 0000000..1bd11ea --- /dev/null +++ b/larn/nap.c @@ -0,0 +1,23 @@ +/* $NetBSD: nap.c,v 1.6 2012/06/19 05:30:43 dholland Exp $ */ + +/* nap.c Larn is copyrighted 1986 by Noah Morgan. */ +#include <sys/cdefs.h> +#ifndef lint +__RCSID("$NetBSD: nap.c,v 1.6 2012/06/19 05:30:43 dholland Exp $"); +#endif /* not lint */ + +#include <unistd.h> +#include "header.h" +#include "extern.h" + +/* + * routine to take a nap for n milliseconds + */ +void +nap(int x) +{ + if (x <= 0) + return; /* eliminate chance for infinite loop */ + lflush(); + usleep(x * 1000); +} diff --git a/larn/object.c b/larn/object.c new file mode 100644 index 0000000..a8e1449 --- /dev/null +++ b/larn/object.c @@ -0,0 +1,1336 @@ +/* $NetBSD: object.c,v 1.16 2012/06/19 05:30:43 dholland Exp $ */ + +/* object.c Larn is copyrighted 1986 by Noah Morgan. */ + +#include <sys/cdefs.h> +#ifndef lint +__RCSID("$NetBSD: object.c,v 1.16 2012/06/19 05:30:43 dholland Exp $"); +#endif /* not lint */ +#include "header.h" +#include "extern.h" + +static void finditem(int); +static void ostairs(int); +static void opotion(int); +static void oscroll(int); +static void oorb(void); +static void opit(void); +static void obottomless(void); +static void oelevator(int); +static void ostatue(void); +static void omirror(void); +static void obook(void); +static void ocookie(void); +static void ogold(int); +static void ohome(void); + +/* + lookforobject + + subroutine to look for an object and give the player his options + if an object was found. + */ +void +lookforobject(void) +{ + int i, j; + if (c[TIMESTOP]) + return; /* can't find objects if time is stopped */ + i = item[playerx][playery]; + if (i == 0) + return; + showcell(playerx, playery); + cursors(); + yrepcount = 0; + switch (i) { + case OGOLDPILE: + case OMAXGOLD: + case OKGOLD: + case ODGOLD: + lprcat("\n\nYou have found some gold!"); + ogold(i); + break; + + case OPOTION: + lprcat("\n\nYou have found a magic potion"); + i = iarg[playerx][playery]; + if (potionname[i][0] != 0) + lprintf(" of%s", potionname[i]); + opotion(i); + break; + + case OSCROLL: + lprcat("\n\nYou have found a magic scroll"); + i = iarg[playerx][playery]; + if (scrollname[i][0] != 0) + lprintf(" of%s", scrollname[i]); + oscroll(i); + break; + + case OALTAR: + if (nearbymonst()) + return; + lprcat("\n\nThere is a Holy Altar here!"); + oaltar(); + break; + + case OBOOK: + lprcat("\n\nYou have found a book."); + obook(); + break; + + case OCOOKIE: + lprcat("\n\nYou have found a fortune cookie."); + ocookie(); + break; + + case OTHRONE: + if (nearbymonst()) + return; + lprintf("\n\nThere is %s here!", objectname[i]); + othrone(0); + break; + + case OTHRONE2: + if (nearbymonst()) + return; + lprintf("\n\nThere is %s here!", objectname[i]); + othrone(1); + break; + + case ODEADTHRONE: + lprintf("\n\nThere is %s here!", objectname[i]); + odeadthrone(); + break; + + case OORB: + lprcat("\n\nYou have found the Orb!!!!!"); + oorb(); + break; + + case OPIT: + lprcat("\n\nYou're standing at the top of a pit."); + opit(); + break; + + case OSTAIRSUP: + lprcat("\n\nThere is a circular staircase here"); + ostairs(1); /* up */ + break; + + case OELEVATORUP: + lprcat("\n\nYou feel heavy for a moment, but the feeling disappears"); + oelevator(1); /* up */ + break; + + case OFOUNTAIN: + if (nearbymonst()) + return; + lprcat("\n\nThere is a fountain here"); + ofountain(); + break; + + case OSTATUE: + if (nearbymonst()) + return; + lprcat("\n\nYou are standing in front of a statue"); + ostatue(); + break; + + case OCHEST: + lprcat("\n\nThere is a chest here"); + ochest(); + break; + + case OIVTELETRAP: + if (rnd(11) < 6) + return; + item[playerx][playery] = OTELEPORTER; + know[playerx][playery] = 1; + + case OTELEPORTER: + lprcat("\nZaaaappp! You've been teleported!\n"); + beep(); + nap(3000); + oteleport(0); + break; + + case OSCHOOL: + if (nearbymonst()) + return; + lprcat("\n\nYou have found the College of Larn."); + lprcat("\nDo you (g) go inside, or (i) stay here? "); + i = 0; + while ((i != 'g') && (i != 'i') && (i != '\33')) + i = ttgetch(); + if (i == 'g') { + oschool(); /* the college of larn */ + } else + lprcat(" stay here"); + break; + + case OMIRROR: + if (nearbymonst()) + return; + lprcat("\n\nThere is a mirror here"); + omirror(); + break; + + case OBANK2: + case OBANK: + if (nearbymonst()) + return; + if (i == OBANK) + lprcat("\n\nYou have found the bank of Larn."); + else + lprcat("\n\nYou have found a branch office of the bank of Larn."); + lprcat("\nDo you (g) go inside, or (i) stay here? "); + j = 0; + while ((j != 'g') && (j != 'i') && (j != '\33')) + j = ttgetch(); + if (j == 'g') { + if (i == OBANK) + obank(); + else + obank2(); /* the bank of larn */ + } else + lprcat(" stay here"); + break; + + case ODEADFOUNTAIN: + if (nearbymonst()) + return; + lprcat("\n\nThere is a dead fountain here"); + break; + + case ODNDSTORE: + if (nearbymonst()) + return; + lprcat("\n\nThere is a DND store here."); + lprcat("\nDo you (g) go inside, or (i) stay here? "); + i = 0; + while ((i != 'g') && (i != 'i') && (i != '\33')) + i = ttgetch(); + if (i == 'g') + dndstore(); /* the dnd adventurers store */ + else + lprcat(" stay here"); + break; + + case OSTAIRSDOWN: + lprcat("\n\nThere is a circular staircase here"); + ostairs(-1); /* down */ + break; + + case OELEVATORDOWN: + lprcat("\n\nYou feel light for a moment, but the feeling disappears"); + oelevator(-1); /* down */ + break; + + case OOPENDOOR: + lprintf("\n\nYou have found %s", objectname[i]); + lprcat("\nDo you (c) close it"); + iopts(); + i = 0; + while ((i != 'c') && (i != 'i') && (i != '\33')) + i = ttgetch(); + if ((i == '\33') || (i == 'i')) { + ignore(); + break; + } + lprcat("close"); + forget(); + item[playerx][playery] = OCLOSEDDOOR; + iarg[playerx][playery] = 0; + playerx = lastpx; + playery = lastpy; + break; + + case OCLOSEDDOOR: + lprintf("\n\nYou have found %s", objectname[i]); + lprcat("\nDo you (o) try to open it"); + iopts(); + i = 0; + while ((i != 'o') && (i != 'i') && (i != '\33')) + i = ttgetch(); + if ((i == '\33') || (i == 'i')) { + ignore(); + playerx = lastpx; + playery = lastpy; + break; + } else { + lprcat("open"); + if (rnd(11) < 7) { + switch (iarg[playerx][playery]) { + case 6: + c[AGGRAVATE] += rnd(400); + break; + + case 7: + lprcat("\nYou are jolted by an electric shock "); + lastnum = 274; + losehp(rnd(20)); + bottomline(); + break; + + case 8: + loselevel(); + break; + + case 9: + lprcat("\nYou suddenly feel weaker "); + if (c[STRENGTH] > 3) + c[STRENGTH]--; + bottomline(); + break; + + default: + break; + } + playerx = lastpx; + playery = lastpy; + } else { + forget(); + item[playerx][playery] = OOPENDOOR; + } + } + break; + + case OENTRANCE: + lprcat("\nYou have found "); + lprcat(objectname[OENTRANCE]); + lprcat("\nDo you (g) go inside"); + iopts(); + i = 0; + while ((i != 'g') && (i != 'i') && (i != '\33')) + i = ttgetch(); + if (i == 'g') { + newcavelevel(1); + playerx = 33; + playery = MAXY - 2; + item[33][MAXY - 1] = know[33][MAXY - 1] = mitem[33][MAXY - 1] = 0; + draws(0, MAXX, 0, MAXY); + bot_linex(); + return; + } else + ignore(); + break; + + case OVOLDOWN: + lprcat("\nYou have found "); + lprcat(objectname[OVOLDOWN]); + lprcat("\nDo you (c) climb down"); + iopts(); + i = 0; + while ((i != 'c') && (i != 'i') && (i != '\33')) + i = ttgetch(); + if ((i == '\33') || (i == 'i')) { + ignore(); + break; + } + if (level != 0) { + lprcat("\nThe shaft only extends 5 feet downward!"); + return; + } + if (packweight() > 45 + 3 * (c[STRENGTH] + c[STREXTRA])) { + lprcat("\nYou slip and fall down the shaft"); + beep(); + lastnum = 275; + losehp(30 + rnd(20)); + bottomhp(); + } else + lprcat("climb down"); + nap(3000); + newcavelevel(MAXLEVEL); + for (i = 0; i < MAXY; i++) + for (j = 0; j < MAXX; j++) /* put player near + * volcano shaft */ + if (item[j][i] == OVOLUP) { + playerx = j; + playery = i; + j = MAXX; + i = MAXY; + positionplayer(); + } + draws(0, MAXX, 0, MAXY); + bot_linex(); + return; + + case OVOLUP: + lprcat("\nYou have found "); + lprcat(objectname[OVOLUP]); + lprcat("\nDo you (c) climb up"); + iopts(); + i = 0; + while ((i != 'c') && (i != 'i') && (i != '\33')) + i = ttgetch(); + if ((i == '\33') || (i == 'i')) { + ignore(); + break; + } + if (level != 11) { + lprcat("\nThe shaft only extends 8 feet upwards before you find a blockage!"); + return; + } + if (packweight() > 45 + 5 * (c[STRENGTH] + c[STREXTRA])) { + lprcat("\nYou slip and fall down the shaft"); + beep(); + lastnum = 275; + losehp(15 + rnd(20)); + bottomhp(); + return; + } + lprcat("climb up"); + lflush(); + nap(3000); + newcavelevel(0); + for (i = 0; i < MAXY; i++) + for (j = 0; j < MAXX; j++) /* put player near + * volcano shaft */ + if (item[j][i] == OVOLDOWN) { + playerx = j; + playery = i; + j = MAXX; + i = MAXY; + positionplayer(); + } + draws(0, MAXX, 0, MAXY); + bot_linex(); + return; + + case OTRAPARROWIV: + if (rnd(17) < 13) + return; /* for an arrow trap */ + item[playerx][playery] = OTRAPARROW; + know[playerx][playery] = 0; + + case OTRAPARROW: + lprcat("\nYou are hit by an arrow"); + beep(); /* for an arrow trap */ + lastnum = 259; + losehp(rnd(10) + level); + bottomhp(); + return; + + case OIVDARTRAP: + if (rnd(17) < 13) + return; /* for a dart trap */ + item[playerx][playery] = ODARTRAP; + know[playerx][playery] = 0; + + case ODARTRAP: + lprcat("\nYou are hit by a dart"); + beep(); /* for a dart trap */ + lastnum = 260; + losehp(rnd(5)); + if ((--c[STRENGTH]) < 3) + c[STRENGTH] = 3; + bottomline(); + return; + + case OIVTRAPDOOR: + if (rnd(17) < 13) + return; /* for a trap door */ + item[playerx][playery] = OTRAPDOOR; + know[playerx][playery] = 1; + + case OTRAPDOOR: + lastnum = 272; /* a trap door */ + if ((level == MAXLEVEL - 1) || (level == MAXLEVEL + MAXVLEVEL - 1)) { + lprcat("\nYou fell through a bottomless trap door!"); + beep(); + nap(3000); + died(271); + } + lprcat("\nYou fall through a trap door!"); + beep(); /* for a trap door */ + losehp(rnd(5 + level)); + nap(2000); + newcavelevel(level + 1); + draws(0, MAXX, 0, MAXY); + bot_linex(); + return; + + + case OTRADEPOST: + if (nearbymonst()) + return; + lprcat("\nYou have found the Larn trading Post."); + lprcat("\nDo you (g) go inside, or (i) stay here? "); + i = 0; + while ((i != 'g') && (i != 'i') && (i != '\33')) + i = ttgetch(); + if (i == 'g') + otradepost(); + else + lprcat("stay here"); + return; + + case OHOME: + if (nearbymonst()) + return; + lprcat("\nYou have found your way home."); + lprcat("\nDo you (g) go inside, or (i) stay here? "); + i = 0; + while ((i != 'g') && (i != 'i') && (i != '\33')) + i = ttgetch(); + if (i == 'g') + ohome(); + else + lprcat("stay here"); + return; + + case OWALL: + break; + + case OANNIHILATION: + died(283); + return; /* annihilated by sphere of annihilation */ + + case OLRS: + if (nearbymonst()) + return; + lprcat("\n\nThere is an LRS office here."); + lprcat("\nDo you (g) go inside, or (i) stay here? "); + i = 0; + while ((i != 'g') && (i != 'i') && (i != '\33')) + i = ttgetch(); + if (i == 'g') + olrs(); /* the larn revenue service */ + else + lprcat(" stay here"); + break; + + default: + finditem(i); + break; + }; +} + +/* + function to say what object we found and ask if player wants to take it + */ +static void +finditem(int theitem) +{ + int tmp, i; + lprintf("\n\nYou have found %s ", objectname[theitem]); + tmp = iarg[playerx][playery]; + switch (theitem) { + case ODIAMOND: + case ORUBY: + case OEMERALD: + case OSAPPHIRE: + case OSPIRITSCARAB: + case OORBOFDRAGON: + case OCUBEofUNDEAD: + case ONOTHEFT: + break; + + default: + if (tmp > 0) + lprintf("+ %ld", (long) tmp); + else if (tmp < 0) + lprintf(" %ld", (long) tmp); + } + lprcat("\nDo you want to (t) take it"); + iopts(); + i = 0; + while (i != 't' && i != 'i' && i != '\33') + i = ttgetch(); + if (i == 't') { + lprcat("take"); + if (take(theitem, tmp) == 0) + forget(); + return; + } + ignore(); +} + + + +/* + subroutine to process the stair cases + if dir > 0 the up else down + */ +static void +ostairs(int dir) +{ + int k; + lprcat("\nDo you (s) stay here "); + if (dir > 0) + lprcat("(u) go up "); + else + lprcat("(d) go down "); + lprcat("or (f) kick stairs? "); + + while (1) + switch (ttgetch()) { + case '\33': + case 's': + case 'i': + lprcat("stay here"); + return; + + case 'f': + lprcat("kick stairs"); + if (rnd(2) == 1) + lprcat("\nI hope you feel better. Showing anger rids you of frustration."); + else { + k = rnd((level + 1) << 1); + lprintf("\nYou hurt your foot dumb dumb! You suffer %ld hit points", (long) k); + lastnum = 276; + losehp(k); + bottomline(); + } + return; + + case 'u': + lprcat("go up"); + if (dir < 0) + lprcat("\nThe stairs don't go up!"); + else if (level >= 2 && level != 11) { + k = level; + newcavelevel(level - 1); + draws(0, MAXX, 0, MAXY); + bot_linex(); + } else + lprcat("\nThe stairs lead to a dead end!"); + return; + + case 'd': + lprcat("go down"); + if (dir > 0) + lprcat("\nThe stairs don't go down!"); + else if (level != 0 && level != 10 && level != 13) { + k = level; + newcavelevel(level + 1); + draws(0, MAXX, 0, MAXY); + bot_linex(); + } else + lprcat("\nThe stairs lead to a dead end!"); + return; + }; +} + + + +/* + subroutine to handle a teleport trap +/- 1 level maximum + */ +void +oteleport(int err) +{ + int tmp; + if (err) + if (rnd(151) < 3) + died(264); /* stuck in a rock */ + c[TELEFLAG] = 1; /* show ?? on bottomline if been teleported */ + if (level == 0) + tmp = 0; + else if (level < MAXLEVEL) { + tmp = rnd(5) + level - 3; + if (tmp >= MAXLEVEL) + tmp = MAXLEVEL - 1; + if (tmp < 1) + tmp = 1; + } else { + tmp = rnd(3) + level - 2; + if (tmp >= MAXLEVEL + MAXVLEVEL) + tmp = MAXLEVEL + MAXVLEVEL - 1; + if (tmp < MAXLEVEL) + tmp = MAXLEVEL; + } + playerx = rnd(MAXX - 2); + playery = rnd(MAXY - 2); + if (level != tmp) + newcavelevel(tmp); + positionplayer(); + draws(0, MAXX, 0, MAXY); + bot_linex(); +} + + +/* + function to process a potion + */ +static void +opotion(int pot) +{ + lprcat("\nDo you (d) drink it, (t) take it"); + iopts(); + while (1) + switch (ttgetch()) { + case '\33': + case 'i': + ignore(); + return; + + case 'd': + lprcat("drink\n"); + forget(); /* destroy potion */ + quaffpotion(pot); + return; + + case 't': + lprcat("take\n"); + if (take(OPOTION, pot) == 0) + forget(); + return; + }; +} + +/* + function to drink a potion + */ +void +quaffpotion(int pot) +{ + int i, j, k; + if (pot < 0 || pot >= MAXPOTION) + return; /* check for within bounds */ + potionname[pot] = potionhide[pot]; + switch (pot) { + case 9: + lprcat("\nYou feel greedy . . ."); + nap(2000); + for (i = 0; i < MAXY; i++) + for (j = 0; j < MAXX; j++) + if ((item[j][i] == OGOLDPILE) || (item[j][i] == OMAXGOLD)) { + know[j][i] = 1; + show1cell(j, i); + } + showplayer(); + return; + + case 19: + lprcat("\nYou feel greedy . . ."); + nap(2000); + for (i = 0; i < MAXY; i++) + for (j = 0; j < MAXX; j++) { + k = item[j][i]; + if ((k == ODIAMOND) || (k == ORUBY) || (k == OEMERALD) || (k == OMAXGOLD) + || (k == OSAPPHIRE) || (k == OLARNEYE) || (k == OGOLDPILE)) { + know[j][i] = 1; + show1cell(j, i); + } + } + showplayer(); + return; + + case 20: + c[HP] = c[HPMAX]; + break; /* instant healing */ + + case 1: + lprcat("\nYou feel better"); + if (c[HP] == c[HPMAX]) + raisemhp(1); + else if ((c[HP] += rnd(20) + 20 + c[LEVEL]) > c[HPMAX]) + c[HP] = c[HPMAX]; + break; + + case 2: + lprcat("\nSuddenly, you feel much more skillful!"); + raiselevel(); + raisemhp(1); + return; + + case 3: + lprcat("\nYou feel strange for a moment"); + c[rund(6)]++; + break; + + case 4: + lprcat("\nYou feel more self confident!"); + c[WISDOM] += rnd(2); + break; + + case 5: + lprcat("\nWow! You feel great!"); + if (c[STRENGTH] < 12) + c[STRENGTH] = 12; + else + c[STRENGTH]++; + break; + + case 6: + lprcat("\nYour charm went up by one!"); + c[CHARISMA]++; + break; + + case 8: + lprcat("\nYour intelligence went up by one!"); + c[INTELLIGENCE]++; + break; + + case 10: + for (i = 0; i < MAXY; i++) + for (j = 0; j < MAXX; j++) + if (mitem[j][i]) { + know[j][i] = 1; + show1cell(j, i); + } + /* monster detection */ return; + + case 12: + lprcat("\nThis potion has no taste to it"); + return; + + case 15: + lprcat("\nWOW!!! You feel Super-fantastic!!!"); + if (c[HERO] == 0) + for (i = 0; i < 6; i++) + c[i] += 11; + c[HERO] += 250; + break; + + case 16: + lprcat("\nYou have a greater intestinal constitude!"); + c[CONSTITUTION]++; + break; + + case 17: + lprcat("\nYou now have incredibly bulging muscles!!!"); + if (c[GIANTSTR] == 0) + c[STREXTRA] += 21; + c[GIANTSTR] += 700; + break; + + case 18: + lprcat("\nYou feel a chill run up your spine!"); + c[FIRERESISTANCE] += 1000; + break; + + case 0: + lprcat("\nYou fall asleep. . ."); + i = rnd(11) - (c[CONSTITUTION] >> 2) + 2; + while (--i > 0) { + parse2(); + nap(1000); + } + cursors(); + lprcat("\nYou woke up!"); + return; + + case 7: + lprcat("\nYou become dizzy!"); + if (--c[STRENGTH] < 3) + c[STRENGTH] = 3; + break; + + case 11: + lprcat("\nYou stagger for a moment . ."); + for (i = 0; i < MAXY; i++) + for (j = 0; j < MAXX; j++) + know[j][i] = 0; + nap(2000); + draws(0, MAXX, 0, MAXY); /* potion of forgetfulness */ + return; + + case 13: + lprcat("\nYou can't see anything!"); /* blindness */ + c[BLINDCOUNT] += 500; + return; + + case 14: + lprcat("\nYou feel confused"); + c[CONFUSE] += 20 + rnd(9); + return; + + case 21: + lprcat("\nYou don't seem to be affected"); + return; /* cure dianthroritis */ + + case 22: + lprcat("\nYou feel a sickness engulf you"); /* poison */ + c[HALFDAM] += 200 + rnd(200); + return; + + case 23: + lprcat("\nYou feel your vision sharpen"); /* see invisible */ + c[SEEINVISIBLE] += rnd(1000) + 400; + monstnamelist[INVISIBLESTALKER] = 'I'; + return; + }; + bottomline(); /* show new stats */ + return; +} + + +/* + function to process a magic scroll + */ +static void +oscroll(int typ) +{ + lprcat("\nDo you "); + if (c[BLINDCOUNT] == 0) + lprcat("(r) read it, "); + lprcat("(t) take it"); + iopts(); + while (1) + switch (ttgetch()) { + case '\33': + case 'i': + ignore(); + return; + + case 'r': + if (c[BLINDCOUNT]) + break; + lprcat("read"); + forget(); + if (typ == 2 || typ == 15) { + show1cell(playerx, playery); + cursors(); + } + /* destroy it */ read_scroll(typ); + return; + + case 't': + lprcat("take"); + if (take(OSCROLL, typ) == 0) + forget(); /* destroy it */ + return; + }; +} + +/* + data for the function to read a scroll + */ +static int xh, yh, yl, xl; +static u_char curse[] = { + BLINDCOUNT, CONFUSE, AGGRAVATE, HASTEMONST, ITCHING, + LAUGHING, DRAINSTRENGTH, CLUMSINESS, INFEEBLEMENT, HALFDAM +}; + +static u_char exten[] = { + PROTECTIONTIME, DEXCOUNT, STRCOUNT, CHARMCOUNT, INVISIBILITY, + CANCELLATION, HASTESELF, GLOBE, SCAREMONST, HOLDMONST, TIMESTOP +}; + +static u_char time_change[] = { + HASTESELF, HERO, ALTPRO, PROTECTIONTIME, DEXCOUNT, STRCOUNT, + GIANTSTR, CHARMCOUNT, INVISIBILITY, CANCELLATION, HASTESELF, + AGGRAVATE, SCAREMONST, STEALTH, AWARENESS, HOLDMONST, + HASTEMONST, FIRERESISTANCE, GLOBE, SPIRITPRO, UNDEADPRO, + HALFDAM, SEEINVISIBLE, ITCHING, CLUMSINESS, WTW +}; + +/* + * function to adjust time when time warping and taking courses in school + */ +void +adjusttime(long tim) +{ + int j; + for (j = 0; j < 26; j++)/* adjust time related parameters */ + if (c[time_change[j]]) + if ((c[time_change[j]] -= tim) < 1) + c[time_change[j]] = 1; + regen(); +} + +/* + function to read a scroll + */ +void +read_scroll(int typ) +{ + int i, j; + if (typ < 0 || typ >= MAXSCROLL) + return; /* be sure we are within bounds */ + scrollname[typ] = scrollhide[typ]; + switch (typ) { + case 0: + lprcat("\nYour armor glows for a moment"); + enchantarmor(); + return; + + case 1: + lprcat("\nYour weapon glows for a moment"); + enchweapon(); + return; /* enchant weapon */ + + case 2: + lprcat("\nYou have been granted enlightenment!"); + yh = min(playery + 7, MAXY); + xh = min(playerx + 25, MAXX); + yl = max(playery - 7, 0); + xl = max(playerx - 25, 0); + for (i = yl; i < yh; i++) + for (j = xl; j < xh; j++) + know[j][i] = 1; + nap(2000); + draws(xl, xh, yl, yh); + return; + + case 3: + lprcat("\nThis scroll seems to be blank"); + return; + + case 4: + createmonster(makemonst(level + 1)); + return; /* this one creates a monster */ + + case 5: + something(level); /* create artifact */ + return; + + case 6: + c[AGGRAVATE] += 800; + return; /* aggravate monsters */ + + case 7: + gltime += (i = rnd(1000) - 850); /* time warp */ + if (i >= 0) + lprintf("\nYou went forward in time by %ld mobuls", (long) ((i + 99) / 100)); + else + lprintf("\nYou went backward in time by %ld mobuls", (long) (-(i + 99) / 100)); + adjusttime((long) i); /* adjust time for time warping */ + return; + + case 8: + oteleport(0); + return; /* teleportation */ + + case 9: + c[AWARENESS] += 1800; + return; /* expanded awareness */ + + case 10: + c[HASTEMONST] += rnd(55) + 12; + return; /* haste monster */ + + case 11: + for (i = 0; i < MAXY; i++) + for (j = 0; j < MAXX; j++) + if (mitem[j][i]) + hitp[j][i] = monster[mitem[j][i]].hitpoints; + return; /* monster healing */ + case 12: + c[SPIRITPRO] += 300 + rnd(200); + bottomline(); + return; /* spirit protection */ + + case 13: + c[UNDEADPRO] += 300 + rnd(200); + bottomline(); + return; /* undead protection */ + + case 14: + c[STEALTH] += 250 + rnd(250); + bottomline(); + return; /* stealth */ + + case 15: + lprcat("\nYou have been granted enlightenment!"); /* magic mapping */ + for (i = 0; i < MAXY; i++) + for (j = 0; j < MAXX; j++) + know[j][i] = 1; + nap(2000); + draws(0, MAXX, 0, MAXY); + return; + + case 16: + c[HOLDMONST] += 30; + bottomline(); + return; /* hold monster */ + + case 17: + for (i = 0; i < 26; i++) /* gem perfection */ + switch (iven[i]) { + case ODIAMOND: + case ORUBY: + case OEMERALD: + case OSAPPHIRE: + j = ivenarg[i]; + j &= 255; + j <<= 1; + if (j > 255) + j = 255; /* double value */ + ivenarg[i] = j; + break; + } + break; + + case 18: + for (i = 0; i < 11; i++) + c[exten[i]] <<= 1; /* spell extension */ + break; + + case 19: + for (i = 0; i < 26; i++) { /* identify */ + if (iven[i] == OPOTION) + potionname[ivenarg[i]] = potionhide[ivenarg[i]]; + if (iven[i] == OSCROLL) + scrollname[ivenarg[i]] = scrollhide[ivenarg[i]]; + } + break; + + case 20: + for (i = 0; i < 10; i++) /* remove curse */ + if (c[curse[i]]) + c[curse[i]] = 1; + break; + + case 21: + annihilate(); + break; /* scroll of annihilation */ + + case 22: + godirect(22, 150, "The ray hits the %s", 0, ' '); /* pulverization */ + break; + case 23: + c[LIFEPROT]++; + break; /* life protection */ + }; +} + + + +static void +oorb(void) +{ +} + +static void +opit(void) +{ + int i; + if (rnd(101) < 81) { + if (rnd(70) > 9 * c[DEXTERITY] - packweight() || rnd(101) < 5) { + if (level == MAXLEVEL - 1) + obottomless(); + else if (level == MAXLEVEL + MAXVLEVEL - 1) + obottomless(); + else { + if (rnd(101) < 20) { + i = 0; + lprcat("\nYou fell into a pit! Your fall is cushioned by an unknown force\n"); + } else { + i = rnd(level * 3 + 3); + lprintf("\nYou fell into a pit! You suffer %ld hit points damage", (long) i); + lastnum = 261; /* if he dies scoreboard + * will say so */ + } + losehp(i); + nap(2000); + newcavelevel(level + 1); + draws(0, MAXX, 0, MAXY); + } + } + } +} + +static void +obottomless(void) +{ + lprcat("\nYou fell into a bottomless pit!"); + beep(); + nap(3000); + died(262); +} + +static void +oelevator(int dir) +{ +#ifdef lint + int x; + x = dir; + dir = x; +#endif /* lint */ +} + +static void +ostatue(void) +{ +} + +static void +omirror(void) +{ +} + +static void +obook(void) +{ + lprcat("\nDo you "); + if (c[BLINDCOUNT] == 0) + lprcat("(r) read it, "); + lprcat("(t) take it"); + iopts(); + while (1) + switch (ttgetch()) { + case '\33': + case 'i': + ignore(); + return; + + case 'r': + if (c[BLINDCOUNT]) + break; + lprcat("read"); + /* no more book */ readbook(iarg[playerx][playery]); + forget(); + return; + + case 't': + lprcat("take"); + if (take(OBOOK, iarg[playerx][playery]) == 0) + forget(); /* no more book */ + return; + }; +} + +/* + function to read a book + */ +void +readbook(int lev) +{ + int i, tmp; + if (lev <= 3) + i = rund((tmp = splev[lev]) ? tmp : 1); + else + i = rnd((tmp = splev[lev] - 9) ? tmp : 1) + 9; + spelknow[i] = 1; + lprintf("\nSpell \"%s\": %s\n%s", spelcode[i], spelname[i], speldescript[i]); + if (rnd(10) == 4) { + lprcat("\nYour int went up by one!"); + c[INTELLIGENCE]++; + bottomline(); + } +} + +static void +ocookie(void) +{ + const char *p; + + lprcat("\nDo you (e) eat it, (t) take it"); + iopts(); + while (1) + switch (ttgetch()) { + case '\33': + case 'i': + ignore(); + return; + + case 'e': + lprcat("eat\nThe cookie tasted good."); + forget(); /* no more cookie */ + if (c[BLINDCOUNT]) + return; + if (!(p = fortune())) + return; + lprcat(" A message inside the cookie reads:\n"); + lprcat(p); + return; + + case 't': + lprcat("take"); + if (take(OCOOKIE, 0) == 0) + forget(); /* no more book */ + return; + }; +} + + +/* + * routine to pick up some gold -- if arg==OMAXGOLD then the pile is worth + * 100* the argument + */ +static void +ogold(int arg) +{ + long i; + i = iarg[playerx][playery]; + if (arg == OMAXGOLD) + i *= 100; + else if (arg == OKGOLD) + i *= 1000; + else if (arg == ODGOLD) + i *= 10; + lprintf("\nIt is worth %ld!", (long) i); + c[GOLD] += i; + bottomgold(); + item[playerx][playery] = know[playerx][playery] = 0; /* destroy gold */ +} + +static void +ohome(void) +{ + int i; + nosignal = 1; /* disable signals */ + for (i = 0; i < 26; i++) + if (iven[i] == OPOTION) + if (ivenarg[i] == 21) { + iven[i] = 0; /* remove the potion of cure + * dianthroritis from + * inventory */ + clear(); + lprcat("Congratulations. You found a potion of cure dianthroritis.\n"); + lprcat("\nFrankly, No one thought you could do it. Boy! Did you surprise them!\n"); + if (gltime > TIMELIMIT) { + lprcat("\nThe doctor has the sad duty to inform you that your daughter died!\n"); + lprcat("You didn't make it in time. In your agony, you kill the doctor,\nyour wife, and yourself! Too bad!\n"); + nap(5000); + died(269); + } else { + lprcat("\nThe doctor is now administering the potion, and in a few moments\n"); + lprcat("Your daughter should be well on her way to recovery.\n"); + nap(6000); + lprcat("\nThe potion is"); + nap(3000); + lprcat(" working! The doctor thinks that\n"); + lprcat("your daughter will recover in a few days. Congratulations!\n"); + beep(); + nap(5000); + died(263); + } + } + while (1) { + clear(); + lprintf("Welcome home %s. Latest word from the doctor is not good.\n", logname); + + if (gltime > TIMELIMIT) { + lprcat("\nThe doctor has the sad duty to inform you that your daughter died!\n"); + lprcat("You didn't make it in time. In your agony, you kill the doctor,\nyour wife, and yourself! Too bad!\n"); + nap(5000); + died(269); + } + lprcat("\nThe diagnosis is confirmed as dianthroritis. He guesses that\n"); + lprintf("your daughter has only %ld mobuls left in this world. It's up to you,\n", (long) ((TIMELIMIT - gltime + 99) / 100)); + lprintf("%s, to find the only hope for your daughter, the very rare\n", logname); + lprcat("potion of cure dianthroritis. It is rumored that only deep in the\n"); + lprcat("depths of the caves can this potion be found.\n\n\n"); + lprcat("\n ----- press "); + standout("return"); + lprcat(" to continue, "); + standout("escape"); + lprcat(" to leave ----- "); + i = ttgetch(); + while (i != '\33' && i != '\n') + i = ttgetch(); + if (i == '\33') { + drawscreen(); + nosignal = 0; /* enable signals */ + return; + } + } +} + +/* routine to save program space */ +void +iopts(void) +{ + lprcat(", or (i) ignore it? "); +} + +void +ignore(void) +{ + lprcat("ignore\n"); +} diff --git a/larn/pathnames.h b/larn/pathnames.h new file mode 100644 index 0000000..0790dfc --- /dev/null +++ b/larn/pathnames.h @@ -0,0 +1,38 @@ +/* $NetBSD: pathnames.h,v 1.8 2003/08/07 09:37:23 agc Exp $ */ + +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pathnames.h 5.2 (Berkeley) 4/27/95 + */ + +#define _PATH_LOG "/var/games/larn/llog12.0" +#define _PATH_SCORE "/var/games/larn/lscore12.0" +#define _PATH_HELP "/usr/share/games/larn/larn.help" +#define _PATH_LEVELS "/usr/share/games/larn/larnmaze" +#define _PATH_PLAYERIDS "/var/games/larn/playerids" diff --git a/larn/regen.c b/larn/regen.c new file mode 100644 index 0000000..2c8537f --- /dev/null +++ b/larn/regen.c @@ -0,0 +1,187 @@ +/* $NetBSD: regen.c,v 1.6 2012/06/19 05:30:44 dholland Exp $ */ + +/* regen.c Larn is copyrighted 1986 by Noah Morgan. */ +#include <sys/cdefs.h> +#ifndef lint +__RCSID("$NetBSD: regen.c,v 1.6 2012/06/19 05:30:44 dholland Exp $"); +#endif /* not lint */ + +#include "header.h" +#include "extern.h" +/* + ******* + REGEN() + ******* + regen() + + subroutine to regenerate player hp and spells + */ +void +regen(void) +{ + int i, flag; + long *d; + d = c; +#ifdef EXTRA + d[MOVESMADE]++; +#endif + if (d[TIMESTOP]) { + if (--d[TIMESTOP] <= 0) + bottomline(); + return; + } /* for stop time spell */ + flag = 0; + + if (d[STRENGTH] < 3) { + d[STRENGTH] = 3; + flag = 1; + } + if ((d[HASTESELF] == 0) || ((d[HASTESELF] & 1) == 0)) + gltime++; + + if (d[HP] != d[HPMAX]) + if (d[REGENCOUNTER]-- <= 0) { /* regenerate hit points */ + d[REGENCOUNTER] = 22 + (d[HARDGAME] << 1) - d[LEVEL]; + if ((d[HP] += d[REGEN]) > d[HPMAX]) + d[HP] = d[HPMAX]; + bottomhp(); + } + if (d[SPELLS] < d[SPELLMAX]) /* regenerate spells */ + if (d[ECOUNTER]-- <= 0) { + d[ECOUNTER] = 100 + 4 * (d[HARDGAME] - d[LEVEL] - d[ENERGY]); + d[SPELLS]++; + bottomspell(); + } + if (d[HERO]) + if (--d[HERO] <= 0) { + for (i = 0; i < 6; i++) + d[i] -= 10; + flag = 1; + } + if (d[ALTPRO]) + if (--d[ALTPRO] <= 0) { + d[MOREDEFENSES] -= 3; + flag = 1; + } + if (d[PROTECTIONTIME]) + if (--d[PROTECTIONTIME] <= 0) { + d[MOREDEFENSES] -= 2; + flag = 1; + } + if (d[DEXCOUNT]) + if (--d[DEXCOUNT] <= 0) { + d[DEXTERITY] -= 3; + flag = 1; + } + if (d[STRCOUNT]) + if (--d[STRCOUNT] <= 0) { + d[STREXTRA] -= 3; + flag = 1; + } + if (d[BLINDCOUNT]) + if (--d[BLINDCOUNT] <= 0) { + cursors(); + lprcat("\nThe blindness lifts "); + beep(); + } + if (d[CONFUSE]) + if (--d[CONFUSE] <= 0) { + cursors(); + lprcat("\nYou regain your senses"); + beep(); + } + if (d[GIANTSTR]) + if (--d[GIANTSTR] <= 0) { + d[STREXTRA] -= 20; + flag = 1; + } + if (d[CHARMCOUNT]) + if ((--d[CHARMCOUNT]) <= 0) + flag = 1; + if (d[INVISIBILITY]) + if ((--d[INVISIBILITY]) <= 0) + flag = 1; + if (d[CANCELLATION]) + if ((--d[CANCELLATION]) <= 0) + flag = 1; + if (d[WTW]) + if ((--d[WTW]) <= 0) + flag = 1; + if (d[HASTESELF]) + if ((--d[HASTESELF]) <= 0) + flag = 1; + if (d[AGGRAVATE]) + --d[AGGRAVATE]; + if (d[SCAREMONST]) + if ((--d[SCAREMONST]) <= 0) + flag = 1; + if (d[STEALTH]) + if ((--d[STEALTH]) <= 0) + flag = 1; + if (d[AWARENESS]) + --d[AWARENESS]; + if (d[HOLDMONST]) + if ((--d[HOLDMONST]) <= 0) + flag = 1; + if (d[HASTEMONST]) + --d[HASTEMONST]; + if (d[FIRERESISTANCE]) + if ((--d[FIRERESISTANCE]) <= 0) + flag = 1; + if (d[GLOBE]) + if (--d[GLOBE] <= 0) { + d[MOREDEFENSES] -= 10; + flag = 1; + } + if (d[SPIRITPRO]) + if (--d[SPIRITPRO] <= 0) + flag = 1; + if (d[UNDEADPRO]) + if (--d[UNDEADPRO] <= 0) + flag = 1; + if (d[HALFDAM]) + if (--d[HALFDAM] <= 0) { + cursors(); + lprcat("\nYou now feel better "); + beep(); + } + if (d[SEEINVISIBLE]) + if (--d[SEEINVISIBLE] <= 0) { + monstnamelist[INVISIBLESTALKER] = ' '; + cursors(); + lprcat("\nYou feel your vision return to normal"); + beep(); + } + if (d[ITCHING]) { + if (d[ITCHING] > 1) + if ((d[WEAR] != -1) || (d[SHIELD] != -1)) + if (rnd(100) < 50) { + d[WEAR] = d[SHIELD] = -1; + cursors(); + lprcat("\nThe hysteria of itching forces you to remove your armor!"); + beep(); + recalc(); + bottomline(); + } + if (--d[ITCHING] <= 0) { + cursors(); + lprcat("\nYou now feel the irritation subside!"); + beep(); + } + } + if (d[CLUMSINESS]) { + if (d[WIELD] != -1) + if (d[CLUMSINESS] > 1) + if (item[playerx][playery] == 0) /* only if nothing there */ + if (rnd(100) < 33) /* drop your weapon due + * to clumsiness */ + drop_object((int) d[WIELD]); + if (--d[CLUMSINESS] <= 0) { + cursors(); + lprcat("\nYou now feel less awkward!"); + beep(); + } + } + if (flag) + bottomline(); +} diff --git a/larn/savelev.c b/larn/savelev.c new file mode 100644 index 0000000..9954ff0 --- /dev/null +++ b/larn/savelev.c @@ -0,0 +1,67 @@ +/* $NetBSD: savelev.c,v 1.7 2012/06/19 05:30:44 dholland Exp $ */ + +/* savelev.c Larn is copyrighted 1986 by Noah Morgan. */ +#include <sys/cdefs.h> +#ifndef lint +__RCSID("$NetBSD: savelev.c,v 1.7 2012/06/19 05:30:44 dholland Exp $"); +#endif /* not lint */ +#include "header.h" +#include "extern.h" + +/* + * routine to save the present level into storage + */ +void +savelevel(void) +{ + struct cel *pcel; + u_char *pitem, *pknow, *pmitem; + short *phitp, *piarg; + struct cel *pecel; + pcel = &cell[level * MAXX * MAXY]; /* pointer to this level's + * cells */ + pecel = pcel + MAXX * MAXY; /* pointer to past end of this + * level's cells */ + pitem = item[0]; + piarg = iarg[0]; + pknow = know[0]; + pmitem = mitem[0]; + phitp = hitp[0]; + while (pcel < pecel) { + pcel->mitem = *pmitem++; + pcel->hitp = *phitp++; + pcel->item = *pitem++; + pcel->know = *pknow++; + pcel->iarg = *piarg++; + pcel++; + } +} + +/* + * routine to restore a level from storage + */ +void +getlevel(void) +{ + struct cel *pcel; + u_char *pitem, *pknow, *pmitem; + short *phitp, *piarg; + struct cel *pecel; + pcel = &cell[level * MAXX * MAXY]; /* pointer to this level's + * cells */ + pecel = pcel + MAXX * MAXY; /* pointer to past end of this + * level's cells */ + pitem = item[0]; + piarg = iarg[0]; + pknow = know[0]; + pmitem = mitem[0]; + phitp = hitp[0]; + while (pcel < pecel) { + *pmitem++ = pcel->mitem; + *phitp++ = pcel->hitp; + *pitem++ = pcel->item; + *pknow++ = pcel->know; + *piarg++ = pcel->iarg; + pcel++; + } +} diff --git a/larn/scores.c b/larn/scores.c new file mode 100644 index 0000000..49a6f87 --- /dev/null +++ b/larn/scores.c @@ -0,0 +1,855 @@ +/* $NetBSD: scores.c,v 1.21 2012/06/19 05:30:44 dholland Exp $ */ + +/* + * scores.c Larn is copyrighted 1986 by Noah Morgan. + * + * Functions in this file are: + * + * readboard() Function to read in the scoreboard into a static buffer + * writeboard() Function to write the scoreboard from readboard()'s buffer + * makeboard() Function to create a new scoreboard (wipe out old one) + * hashewon() Function to return 1 if player has won a game before, else 0 + * long paytaxes(x) Function to pay taxes if any are due winshou() + * ubroutine to print out the winning scoreboard shou(x) + * ubroutine to print out the non-winners scoreboard showscores() + * unction to show the scoreboard on the terminal showallscores() + * Function to show scores and the iven lists that go with them sortboard() + * unction to sort the scoreboard newscore(score, whoo, whyded, winner) + * Function to add entry to scoreboard new1sub(score,i,whoo,taxes) + * Subroutine to put player into a new2sub(score,i,whoo,whyded) + * Subroutine to put player into a died(x) Subroutine to record who + * played larn, and what the score was diedsub(x) Subroutine to print out a + * line showing player when he is killed diedlog() Subroutine to read a + * log file and print it out in ascii format getplid(name) + * on to get players id # from id file + * + */ +#include <sys/cdefs.h> +#ifndef lint +__RCSID("$NetBSD: scores.c,v 1.21 2012/06/19 05:30:44 dholland Exp $"); +#endif /* not lint */ +#include <sys/types.h> +#include <sys/times.h> +#include <sys/stat.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <stdlib.h> +#include "header.h" +#include "extern.h" + +struct scofmt { /* This is the structure for the scoreboard */ + long score; /* the score of the player */ + long suid; /* the user id number of the player */ + short what; /* the number of the monster that killed + * player */ + short level; /* the level player was on when he died */ + short hardlev;/* the level of difficulty player played at */ + short order; /* the relative ordering place of this entry */ + char who[40];/* the name of the character */ + char sciv[26][2]; /* this is the inventory list of the + * character */ +}; +struct wscofmt { /* This is the structure for the winning + * scoreboard */ + long score; /* the score of the player */ + long timeused; /* the time used in mobuls to win the + * game */ + long taxes; /* taxes he owes to LRS */ + long suid; /* the user id number of the player */ + short hardlev;/* the level of difficulty player played at */ + short order; /* the relative ordering place of this entry */ + char who[40];/* the name of the character */ +}; + +struct log_fmt { /* 102 bytes struct for the log file */ + long score; /* the players score */ + int32_t diedtime; /* time when game was over */ + short cavelev;/* level in caves */ + short diff; /* difficulty player played at */ +#ifdef EXTRA + long elapsedtime; /* real time of game in seconds */ + long bytout; /* bytes input and output */ + long bytin; + long moves; /* number of moves made by player */ + short ac; /* armor class of player */ + short hp, hpmax; /* players hitpoints */ + short cputime;/* CPU time needed in seconds */ + short killed, spused; /* monsters killed and spells cast */ + short usage; /* usage of the CPU in % */ + short lev; /* player level */ +#endif + char who[12];/* player name */ + char what[46]; /* what happened to player */ +}; + +static struct scofmt sco[SCORESIZE]; /* the structure for the scoreboard */ +static struct wscofmt winr[SCORESIZE]; /* struct for the winning scoreboard */ +static struct log_fmt logg; /* structure for the log file */ +static const char *whydead[] = { + "quit", "suspended", "self - annihilated", "shot by an arrow", + "hit by a dart", "fell into a pit", "fell into a bottomless pit", + "a winner", "trapped in solid rock", "killed by a missing save file", + "killed by an old save file", "caught by the greedy cheater checker trap", + "killed by a protected save file", "killed his family and committed suicide", + "erased by a wayward finger", "fell through a bottomless trap door", + "fell through a trap door", "drank some poisonous water", + "fried by an electric shock", "slipped on a volcano shaft", + "killed by a stupid act of frustration", "attacked by a revolting demon", + "hit by his own magic", "demolished by an unseen attacker", + "fell into the dreadful sleep", "killed by an exploding chest", + /* 26 */ "killed by a missing maze data file", "annihilated in a sphere", + "died a post mortem death", "wasted by a malloc() failure" +}; + +static int readboard(void); +static int writeboard(void); +static int winshou(void); +static int shou(int); +static int sortboard(void); +static void newscore(long, char *, int, int); +static void new1sub(long, int, char *, long); +static void new2sub(long, int, char *, int); +static void diedsub(int); + +/* + * readboard() Function to read in the scoreboard into a static buffer + * + * returns -1 if unable to read in the scoreboard, returns 0 if all is OK + */ +static int +readboard(void) +{ + int i; + + if (gid != egid) + setegid(egid); + i = lopen(scorefile); + if (gid != egid) + setegid(gid); + if (i < 0) { + lprcat("Can't read scoreboard\n"); + lflush(); + return (-1); + } + lrfill((char *) sco, sizeof(sco)); + lrfill((char *) winr, sizeof(winr)); + lrclose(); + lcreat((char *) 0); + return (0); +} + +/* + * writeboard() Function to write the scoreboard from readboard()'s buffer + * + * returns -1 if unable to write the scoreboard, returns 0 if all is OK + */ +static int +writeboard(void) +{ + int i; + + set_score_output(); + if (gid != egid) + setegid(egid); + i = lcreat(scorefile); + if (gid != egid) + setegid(gid); + if (i < 0) { + lprcat("Can't write scoreboard\n"); + lflush(); + return (-1); + } + lwrite((char *) sco, sizeof(sco)); + lwrite((char *) winr, sizeof(winr)); + lwclose(); + lcreat((char *) 0); + return (0); +} + +/* + * makeboard() Function to create a new scoreboard (wipe out old one) + * + * returns -1 if unable to write the scoreboard, returns 0 if all is OK + */ +int +makeboard(void) +{ + int i; + set_score_output(); + for (i = 0; i < SCORESIZE; i++) { + winr[i].taxes = winr[i].score = sco[i].score = 0; + winr[i].order = sco[i].order = i; + } + if (writeboard()) + return (-1); + if (gid != egid) + setegid(egid); + chmod(scorefile, 0660); + if (gid != egid) + setegid(gid); + return (0); +} + +/* + * hashewon() Function to return 1 if player has won a game before, else 0 + * + * This function also sets c[HARDGAME] to appropriate value -- 0 if not a + * winner, otherwise the next level of difficulty listed in the winners + * scoreboard. This function also sets outstanding_taxes to the value in + * the winners scoreboard. + */ +int +hashewon(void) +{ + int i; + c[HARDGAME] = 0; + if (readboard() < 0) + return (0); /* can't find scoreboard */ + for (i = 0; i < SCORESIZE; i++) /* search through winners scoreboard */ + if (winr[i].suid == userid) + if (winr[i].score > 0) { + c[HARDGAME] = winr[i].hardlev + 1; + outstanding_taxes = winr[i].taxes; + return (1); + } + return (0); +} + +/* + * long paytaxes(x) Function to pay taxes if any are due + * + * Enter with the amount (in gp) to pay on the taxes. + * Returns amount actually paid. + */ +long +paytaxes(long x) +{ + int i; + long amt; + if (x < 0) + return (0L); + if (readboard() < 0) + return (0L); + for (i = 0; i < SCORESIZE; i++) + if (winr[i].suid == userid) /* look for players winning + * entry */ + if (winr[i].score > 0) { /* search for a winning + * entry for the player */ + amt = winr[i].taxes; + if (x < amt) + amt = x; /* don't overpay taxes + * (Ughhhhh) */ + winr[i].taxes -= amt; + outstanding_taxes -= amt; + set_score_output(); + if (writeboard() < 0) + return (0); + return (amt); + } + return (0L); /* couldn't find user on winning scoreboard */ +} + +/* + * winshou() Subroutine to print out the winning scoreboard + * + * Returns the number of players on scoreboard that were shown + */ +static int +winshou(void) +{ + struct wscofmt *p; + int i, j, count; + for (count = j = i = 0; i < SCORESIZE; i++) /* is there anyone on + * the scoreboard? */ + if (winr[i].score != 0) { + j++; + break; + } + if (j) { + lprcat("\n Score Difficulty Time Needed Larn Winners List\n"); + + for (i = 0; i < SCORESIZE; i++) /* this loop is needed to + * print out the */ + for (j = 0; j < SCORESIZE; j++) { /* winners in order */ + p = &winr[j]; /* pointer to the scoreboard + * entry */ + if (p->order == i) { + if (p->score) { + count++; + lprintf("%10ld %2ld %5ld Mobuls %s \n", + (long) p->score, (long) p->hardlev, (long) p->timeused, p->who); + } + break; + } + } + } + return (count); /* return number of people on scoreboard */ +} + +/* + * shou(x) Subroutine to print out the non-winners scoreboard + * int x; + * + * Enter with 0 to list the scores, enter with 1 to list inventories too + * Returns the number of players on scoreboard that were shown + */ +static int +shou(int x) +{ + int i, j, n, k; + int count; + for (count = j = i = 0; i < SCORESIZE; i++) /* is the scoreboard + * empty? */ + if (sco[i].score != 0) { + j++; + break; + } + if (j) { + lprcat("\n Score Difficulty Larn Visitor Log\n"); + for (i = 0; i < SCORESIZE; i++) /* be sure to print them out + * in order */ + for (j = 0; j < SCORESIZE; j++) + if (sco[j].order == i) { + if (sco[j].score) { + count++; + lprintf("%10ld %2ld %s ", + (long) sco[j].score, (long) sco[j].hardlev, sco[j].who); + if (sco[j].what < 256) + lprintf("killed by a %s", monster[sco[j].what].name); + else + lprintf("%s", whydead[sco[j].what - 256]); + if (x != 263) + lprintf(" on %s", levelname[sco[j].level]); + if (x) { + for (n = 0; n < 26; n++) { + iven[n] = sco[j].sciv[n][0]; + ivenarg[n] = sco[j].sciv[n][1]; + } + for (k = 1; k < 99; k++) + for (n = 0; n < 26; n++) + if (k == iven[n]) { + srcount = 0; + show3(n); + } + lprcat("\n\n"); + } else + lprc('\n'); + } + j = SCORESIZE; + } + } + return (count); /* return the number of players just shown */ +} + +/* + * showscores() Function to show the scoreboard on the terminal + * + * Returns nothing of value + */ +static char esb[] = "The scoreboard is empty.\n"; +void +showscores(void) +{ + int i, j; + lflush(); + lcreat((char *) 0); + if (readboard() < 0) + return; + i = winshou(); + j = shou(0); + if (i + j == 0) + lprcat(esb); + else + lprc('\n'); + lflush(); +} + +/* + * showallscores() Function to show scores and the iven lists that go with them + * + * Returns nothing of value + */ +void +showallscores(void) +{ + int i, j; + lflush(); + lcreat((char *) 0); + if (readboard() < 0) + return; + c[WEAR] = c[WIELD] = c[SHIELD] = -1; /* not wielding or wearing + * anything */ + for (i = 0; i < MAXPOTION; i++) + potionname[i] = potionhide[i]; + for (i = 0; i < MAXSCROLL; i++) + scrollname[i] = scrollhide[i]; + i = winshou(); + j = shou(1); + if (i + j == 0) + lprcat(esb); + else + lprc('\n'); + lflush(); +} + +/* + * sortboard() Function to sort the scoreboard + * + * Returns 0 if no sorting done, else returns 1 + */ +static int +sortboard(void) +{ + int i, j = 0, pos; + long jdat; + for (i = 0; i < SCORESIZE; i++) + sco[i].order = winr[i].order = -1; + pos = 0; + while (pos < SCORESIZE) { + jdat = 0; + for (i = 0; i < SCORESIZE; i++) + if ((sco[i].order < 0) && (sco[i].score >= jdat)) { + j = i; + jdat = sco[i].score; + } + sco[j].order = pos++; + } + pos = 0; + while (pos < SCORESIZE) { + jdat = 0; + for (i = 0; i < SCORESIZE; i++) + if ((winr[i].order < 0) && (winr[i].score >= jdat)) { + j = i; + jdat = winr[i].score; + } + winr[j].order = pos++; + } + return (1); +} + +/* + * newscore(score, whoo, whyded, winner) Function to add entry to scoreboard + * int score, winner, whyded; + * char *whoo; + * + * Enter with the total score in gp in score, players name in whoo, + * died() reason # in whyded, and TRUE/FALSE in winner if a winner + * ex. newscore(1000, "player 1", 32, 0); + */ +static void +newscore(long score, char *whoo, int whyded, int winner) +{ + int i; + long taxes; + if (readboard() < 0) + return; /* do the scoreboard */ + /* if a winner then delete all non-winning scores */ + if (cheat) + winner = 0; /* if he cheated, don't let him win */ + if (winner) { + for (i = 0; i < SCORESIZE; i++) + if (sco[i].suid == userid) + sco[i].score = 0; + taxes = score * TAXRATE; + score += 100000 * c[HARDGAME]; /* bonus for winning */ + /* + * if he has a slot on the winning scoreboard update it if + * greater score + */ + for (i = 0; i < SCORESIZE; i++) + if (winr[i].suid == userid) { + new1sub(score, i, whoo, taxes); + return; + } + /* + * he had no entry. look for last entry and see if he has a + * greater score + */ + for (i = 0; i < SCORESIZE; i++) + if (winr[i].order == SCORESIZE - 1) { + new1sub(score, i, whoo, taxes); + return; + } + } else if (!cheat) { /* for not winning scoreboard */ + /* + * if he has a slot on the scoreboard update it if greater + * score + */ + for (i = 0; i < SCORESIZE; i++) + if (sco[i].suid == userid) { + new2sub(score, i, whoo, whyded); + return; + } + /* + * he had no entry. look for last entry and see if he has a + * greater score + */ + for (i = 0; i < SCORESIZE; i++) + if (sco[i].order == SCORESIZE - 1) { + new2sub(score, i, whoo, whyded); + return; + } + } +} + +/* + * new1sub(score,i,whoo,taxes) Subroutine to put player into a + * int score,i,whyded,taxes; winning scoreboard entry if his score + * char *whoo; is high enough + * + * Enter with the total score in gp in score, players name in whoo, + * died() reason # in whyded, and TRUE/FALSE in winner if a winner + * slot in scoreboard in i, and the tax bill in taxes. + * Returns nothing of value + */ +static void +new1sub(long score, int i, char *whoo, long taxes) +{ + struct wscofmt *p; + p = &winr[i]; + p->taxes += taxes; + if ((score >= p->score) || (c[HARDGAME] > p->hardlev)) { + strcpy(p->who, whoo); + p->score = score; + p->hardlev = c[HARDGAME]; + p->suid = userid; + p->timeused = gltime / 100; + } +} + +/* + * new2sub(score,i,whoo,whyded) Subroutine to put player into a + * int score,i,whyded,taxes; non-winning scoreboard entry if his + * char *whoo; score is high enough + * + * Enter with the total score in gp in score, players name in whoo, + * died() reason # in whyded, and slot in scoreboard in i. + * Returns nothing of value + */ +static void +new2sub(long score, int i, char *whoo, int whyded) +{ + int j; + struct scofmt *p; + p = &sco[i]; + if ((score >= p->score) || (c[HARDGAME] > p->hardlev)) { + strcpy(p->who, whoo); + p->score = score; + p->what = whyded; + p->hardlev = c[HARDGAME]; + p->suid = userid; + p->level = level; + for (j = 0; j < 26; j++) { + p->sciv[j][0] = iven[j]; + p->sciv[j][1] = ivenarg[j]; + } + } +} + +/* + * died(x) Subroutine to record who played larn, and what the score was + * int x; + * + * if x < 0 then don't show scores + * died() never returns! (unless c[LIFEPROT] and a reincarnatable death!) + * + * < 256 killed by the monster number + * 256 quit + * 257 suspended + * 258 self - annihilated + * 259 shot by an arrow + * 260 hit by a dart + * 261 fell into a pit + * 262 fell into a bottomless pit + * 263 a winner + * 264 trapped in solid rock + * 265 killed by a missing save file + * 266 killed by an old save file + * 267 caught by the greedy cheater checker trap + * 268 killed by a protected save file + * 269 killed his family and killed himself + * 270 erased by a wayward finger + * 271 fell through a bottomless trap door + * 272 fell through a trap door + * 273 drank some poisonous water + * 274 fried by an electric shock + * 275 slipped on a volcano shaft + * 276 killed by a stupid act of frustration + * 277 attacked by a revolting demon + * 278 hit by his own magic + * 279 demolished by an unseen attacker + * 280 fell into the dreadful sleep + * 281 killed by an exploding chest + * 282 killed by a missing maze data file + * 283 killed by a sphere of annihilation + * 284 died a post mortem death + * 285 malloc() failure + * 300 quick quit -- don't put on scoreboard + */ + +static int scorerror; +void +died(int x) +{ + int f, win; + char ch; + const char *mod; + time_t zzz; + if (c[LIFEPROT] > 0) { /* if life protection */ + switch ((x > 0) ? x : -x) { + case 256: + case 257: + case 262: + case 263: + case 265: + case 266: + case 267: + case 268: + case 269: + case 271: + case 282: + case 284: + case 285: + case 300: + goto invalid; /* can't be saved */ + }; + --c[LIFEPROT]; + c[HP] = 1; + --c[CONSTITUTION]; + cursors(); + lprcat("\nYou feel wiiieeeeerrrrrd all over! "); + beep(); + lflush(); + sleep(4); + return; /* only case where died() returns */ + } +invalid: + clearvt100(); + lflush(); + f = 0; + if (ckpflag) + unlink(ckpfile);/* remove checkpoint file if used */ + if (x < 0) { + f++; + x = -x; + } /* if we are not to display the scores */ + if ((x == 300) || (x == 257)) + exit(0); /* for quick exit or saved game */ + if (x == 263) + win = 1; + else + win = 0; + c[GOLD] += c[BANKACCOUNT]; + c[BANKACCOUNT] = 0; + /* now enter the player at the end of the scoreboard */ + newscore(c[GOLD], logname, x, win); + diedsub(x); /* print out the score line */ + lflush(); + + set_score_output(); + if ((wizard == 0) && (c[GOLD] > 0)) { /* wizards can't score */ +#ifndef NOLOG + if (gid != egid) + setegid(egid); + if (lappend(logfile) < 0) { /* append to file */ + if (lcreat(logfile) < 0) { /* and can't create new + * log file */ + lcreat((char *) 0); + lprcat("\nCan't open record file: I can't post your score.\n"); + sncbr(); + resetscroll(); + lflush(); + exit(0); + } + if (gid != egid) + setegid(egid); + chmod(logfile, 0660); + if (gid != egid) + setegid(gid); + } + if (gid != egid) + setegid(gid); + strcpy(logg.who, loginname); + logg.score = c[GOLD]; + logg.diff = c[HARDGAME]; + if (x < 256) { + ch = *monster[x].name; + if (ch == 'a' || ch == 'e' || ch == 'i' || ch == 'o' || ch == 'u') + mod = "an"; + else + mod = "a"; + snprintf(logg.what, sizeof(logg.what), + "killed by %s %s", mod, monster[x].name); + } else + snprintf(logg.what, sizeof(logg.what), + "%s", whydead[x - 256]); + logg.cavelev = level; + time(&zzz); /* get CPU time -- write out score info */ + logg.diedtime = zzz; +#ifdef EXTRA + times(&cputime);/* get CPU time -- write out score info */ + logg.cputime = i = (cputime.tms_utime + cputime.tms_stime) / 60 + c[CPUTIME]; + logg.lev = c[LEVEL]; + logg.ac = c[AC]; + logg.hpmax = c[HPMAX]; + logg.hp = c[HP]; + logg.elapsedtime = (zzz - initialtime + 59) / 60; + logg.usage = (10000 * i) / (zzz - initialtime); + logg.bytin = c[BYTESIN]; + logg.bytout = c[BYTESOUT]; + logg.moves = c[MOVESMADE]; + logg.spused = c[SPELLSCAST]; + logg.killed = c[MONSTKILLED]; +#endif + lwrite((char *) &logg, sizeof(struct log_fmt)); + lwclose(); +#endif /* NOLOG */ + + /* + * now for the scoreboard maintenance -- not for a suspended + * game + */ + if (x != 257) { + if (sortboard()) { + set_score_output(); + scorerror = writeboard(); + } + } + } + if ((x == 256) || (x == 257) || (f != 0)) + exit(0); + if (scorerror == 0) + showscores(); /* if we updated the scoreboard */ + if (x == 263) + mailbill(); + exit(0); +} + +/* + * diedsub(x) Subroutine to print out the line showing the player when he is killed + * int x; + */ +static void +diedsub(int x) +{ + char ch; + const char *mod; + + lprintf("Score: %ld, Diff: %ld, %s ", (long) c[GOLD], (long) c[HARDGAME], logname); + if (x < 256) { + ch = *monster[x].name; + if (ch == 'a' || ch == 'e' || ch == 'i' || ch == 'o' || ch == 'u') + mod = "an"; + else + mod = "a"; + lprintf("killed by %s %s", mod, monster[x].name); + } else + lprintf("%s", whydead[x - 256]); + if (x != 263) + lprintf(" on %s\n", levelname[level]); + else + lprc('\n'); +} + +/* + * diedlog() Subroutine to read a log file and print it out in ascii format + */ +void +diedlog(void) +{ + int n; + char *p; + static char q[] = "?"; + struct stat stbuf; + time_t t; + + lcreat((char *) 0); + if (lopen(logfile) < 0) { + lprintf("Can't locate log file <%s>\n", logfile); + return; + } + if (fstat(io_infd, &stbuf) < 0) { + lprintf("Can't stat log file <%s>\n", logfile); + return; + } + for (n = stbuf.st_size / sizeof(struct log_fmt); n > 0; --n) { + lrfill((char *) &logg, sizeof(struct log_fmt)); + t = logg.diedtime; + if ((p = ctime(&t)) == NULL) + p = q; + else { + p[16] = '\n'; + p[17] = 0; + } + lprintf("Score: %ld, Diff: %ld, %s %s on %ld at %s", (long) (logg.score), (long) (logg.diff), logg.who, logg.what, (long) (logg.cavelev), p + 4); +#ifdef EXTRA + if (logg.moves <= 0) + logg.moves = 1; + lprintf(" Experience Level: %ld, AC: %ld, HP: %ld/%ld, Elapsed Time: %ld minutes\n", (long) (logg.lev), (long) (logg.ac), (long) (logg.hp), (long) (logg.hpmax), (long) (logg.elapsedtime)); + lprintf(" CPU time used: %ld seconds, Machine usage: %ld.%02ld%%\n", (long) (logg.cputime), (long) (logg.usage / 100), (long) (logg.usage % 100)); + lprintf(" BYTES in: %ld, out: %ld, moves: %ld, deaths: %ld, spells cast: %ld\n", (long) (logg.bytin), (long) (logg.bytout), (long) (logg.moves), (long) (logg.killed), (long) (logg.spused)); + lprintf(" out bytes per move: %ld, time per move: %ld ms\n", (long) (logg.bytout / logg.moves), (long) ((logg.cputime * 1000) / logg.moves)); +#endif + } + lflush(); + lrclose(); + return; +} + +#ifndef UIDSCORE +/* + * getplid(name) Function to get players id # from id file + * + * Enter with the name of the players character in name. + * Returns the id # of the players character, or -1 if failure. + * This routine will try to find the name in the id file, if its not there, + * it will try to make a new entry in the file. Only returns -1 if can't + * find him in the file, and can't make a new entry in the file. + * Format of playerids file: + * Id # in ascii \n character name \n + */ +static int havepid = -1; /* playerid # if previously done */ +int +getplid(nam) + char *nam; +{ + int fd7, high = 999, no; + char *p, *p2; + char name[80]; + if (havepid != -1) + return (havepid); /* already did it */ + lflush(); /* flush any pending I/O */ + snprintf(name, sizeof(name), "%s\n", nam);/* append a \n to name */ + if (lopen(playerids) < 0) { /* no file, make it */ + if ((fd7 = creat(playerids, 0664)) < 0) + return (-1); /* can't make it */ + close(fd7); + goto addone; /* now append new playerid record to file */ + } + for (;;) { /* now search for the name in the player id + * file */ + p = lgetl(); + if (p == NULL) + break; /* EOF? */ + no = atoi(p); /* the id # */ + p2 = lgetl(); + if (p2 == NULL) + break; /* EOF? */ + if (no > high) + high = no; /* accumulate highest id # */ + if (strcmp(p2, name) == 0) { /* we found him */ + return (no); /* his id number */ + } + } + lrclose(); + /* if we get here, we didn't find him in the file -- put him there */ +addone: + if (lappend(playerids) < 0) + return (-1); /* can't open file for append */ + lprintf("%ld\n%s", (long) ++high, name); /* new id # and name */ + lwclose(); + lcreat((char *) 0); /* re-open terminal channel */ + return (high); +} +#endif /* UIDSCORE */ diff --git a/larn/signal.c b/larn/signal.c new file mode 100644 index 0000000..807579a --- /dev/null +++ b/larn/signal.c @@ -0,0 +1,138 @@ +/* $NetBSD: signal.c,v 1.9 2012/06/19 05:30:44 dholland Exp $ */ + +/* "Larn is copyrighted 1986 by Noah Morgan.\n" */ + +#include <sys/cdefs.h> +#ifndef lint +__RCSID("$NetBSD: signal.c,v 1.9 2012/06/19 05:30:44 dholland Exp $"); +#endif /* not lint */ + +#include <signal.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include "header.h" +#include "extern.h" + +static void s2choose(void); +static void cntlc(int); +static void sgam(int); +static void tstop(int); +static void sigpanic(int); + +#define BIT(a) (1<<((a)-1)) + +static void +s2choose(void) +{ /* text to be displayed if ^C during intro + * screen */ + cursor(1, 24); + lprcat("Press "); + setbold(); + lprcat("return"); + resetbold(); + lprcat(" to continue: "); + lflush(); +} + +static void +cntlc(int n) +{ /* what to do for a ^C */ + if (nosignal) + return; /* don't do anything if inhibited */ + signal(SIGQUIT, SIG_IGN); + signal(SIGINT, SIG_IGN); + quit(); + if (predostuff == 1) + s2choose(); + else + showplayer(); + lflush(); + signal(SIGQUIT, cntlc); + signal(SIGINT, cntlc); +} + +/* + * subroutine to save the game if a hangup signal + */ +static void +sgam(int n) +{ + savegame(savefilename); + wizard = 1; + died(-257); /* hangup signal */ +} + +#ifdef SIGTSTP +static void +tstop(int n) +{ /* control Y */ + if (nosignal) + return; /* nothing if inhibited */ + lcreat((char *) 0); + clearvt100(); + lflush(); + signal(SIGTSTP, SIG_DFL); +#ifdef SIGVTALRM + /* + * looks like BSD4.2 or higher - must clr mask for signal to take + * effect + */ + sigsetmask(sigblock(0) & ~BIT(SIGTSTP)); +#endif + kill(getpid(), SIGTSTP); + + setupvt100(); + signal(SIGTSTP, tstop); + if (predostuff == 1) + s2choose(); + else + drawscreen(); + showplayer(); + lflush(); +} +#endif /* SIGTSTP */ + +/* + * subroutine to issue the needed signal traps called from main() + */ +void +sigsetup(void) +{ + signal(SIGQUIT, cntlc); + signal(SIGINT, cntlc); + signal(SIGKILL, SIG_IGN); + signal(SIGHUP, sgam); + signal(SIGILL, sigpanic); + signal(SIGTRAP, sigpanic); + signal(SIGIOT, sigpanic); + /* signal(SIGEMT, sigpanic); 20150209 bkw: not on linux */ + signal(SIGFPE, sigpanic); + signal(SIGBUS, sigpanic); + signal(SIGSEGV, sigpanic); + signal(SIGSYS, sigpanic); + signal(SIGPIPE, sigpanic); + signal(SIGTERM, sigpanic); +#ifdef SIGTSTP + signal(SIGTSTP, tstop); + signal(SIGSTOP, tstop); +#endif /* SIGTSTP */ +} + +/* + * routine to process a fatal error signal + */ +static void +sigpanic(int sig) +{ + char buf[128]; + signal(sig, SIG_DFL); + snprintf(buf, sizeof(buf), + "\nLarn - Panic! Signal %d received [%s]", sig, + sys_siglist[sig]); /* 20150209 bkw: no sys_signame on linux */ + write(2, buf, strlen(buf)); + sleep(2); + sncbr(); + savegame(savefilename); + kill(getpid(), sig); /* this will terminate us */ +} diff --git a/larn/store.c b/larn/store.c new file mode 100644 index 0000000..2d33538 --- /dev/null +++ b/larn/store.c @@ -0,0 +1,910 @@ +/* $NetBSD: store.c,v 1.16 2012/06/19 05:30:44 dholland Exp $ */ + +/*- + * Copyright (c) 1988 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)store.c 5.4 (Berkeley) 5/13/91"; +#else +__RCSID("$NetBSD: store.c,v 1.16 2012/06/19 05:30:44 dholland Exp $"); +#endif +#endif /* not lint */ + +/* store.c Larn is copyrighted 1986 by Noah Morgan. */ +#include "header.h" +#include "extern.h" + +static void handsfull(void); +static void outofstock(void); +static void nogold(void); +static void dnditem(int); +static void banktitle(const char *); +static void obanksub(void); +static void otradhead(void); +static void cnsitm(void); + +static int dndcount = 0, dnditm = 0; + +/* number of items in the dnd inventory table */ +#define MAXITM 83 + +/* this is the data for the stuff in the dnd store */ +struct _itm itm[90] = { + /* + * cost iven name iven arg how gp + * iven[] ivenarg[] many + */ + + {2, OLEATHER, 0, 3}, + {10, OSTUDLEATHER, 0, 2}, + {40, ORING, 0, 2}, + {85, OCHAIN, 0, 2}, + {220, OSPLINT, 0, 1}, + {400, OPLATE, 0, 1}, + {900, OPLATEARMOR, 0, 1}, + {2600, OSSPLATE, 0, 1}, + {150, OSHIELD, 0, 1}, + + /* + * cost iven name iven arg how gp + * iven[] ivenarg[] many + */ + + {2, ODAGGER, 0, 3}, + {20, OSPEAR, 0, 3}, + {80, OFLAIL, 0, 2}, + {150, OBATTLEAXE, 0, 2}, + {450, OLONGSWORD, 0, 2}, + {1000, O2SWORD, 0, 2}, + {5000, OSWORD, 0, 1}, + {16500, OLANCE, 0, 1}, + {6000, OSWORDofSLASHING, 0, 0}, + {10000, OHAMMER, 0, 0}, + + /* + * cost iven name iven arg how gp + * iven[] ivenarg[] many + */ + + {150, OPROTRING, 1, 1}, + {85, OSTRRING, 1, 1}, + {120, ODEXRING, 1, 1}, + {120, OCLEVERRING, 1, 1}, + {180, OENERGYRING, 0, 1}, + {125, ODAMRING, 0, 1}, + {220, OREGENRING, 0, 1}, + {1000, ORINGOFEXTRA, 0, 1}, + + {280, OBELT, 0, 1}, + + {400, OAMULET, 0, 1}, + + {6500, OORBOFDRAGON, 0, 0}, + {5500, OSPIRITSCARAB, 0, 0}, + {5000, OCUBEofUNDEAD, 0, 0}, + {6000, ONOTHEFT, 0, 0}, + + {590, OCHEST, 6, 1}, + {200, OBOOK, 8, 1}, + {10, OCOOKIE, 0, 3}, + + /* + * cost iven name iven arg how gp + * iven[] ivenarg[] many + */ + + {20, OPOTION, 0, 6}, + {90, OPOTION, 1, 5}, + {520, OPOTION, 2, 1}, + {100, OPOTION, 3, 2}, + {50, OPOTION, 4, 2}, + {150, OPOTION, 5, 2}, + {70, OPOTION, 6, 1}, + {30, OPOTION, 7, 7}, + {200, OPOTION, 8, 1}, + {50, OPOTION, 9, 1}, + {80, OPOTION, 10, 1}, + + /* + * cost iven name iven arg how gp + * iven[] ivenarg[] many + */ + + {30, OPOTION, 11, 3}, + {20, OPOTION, 12, 5}, + {40, OPOTION, 13, 3}, + {35, OPOTION, 14, 2}, + {520, OPOTION, 15, 1}, + {90, OPOTION, 16, 2}, + {200, OPOTION, 17, 2}, + {220, OPOTION, 18, 4}, + {80, OPOTION, 19, 6}, + {370, OPOTION, 20, 3}, + {50, OPOTION, 22, 1}, + {150, OPOTION, 23, 3}, + + /* + * cost iven name iven arg how gp + * iven[] ivenarg[] many + */ + + {100, OSCROLL, 0, 2}, + {125, OSCROLL, 1, 2}, + {60, OSCROLL, 2, 4}, + {10, OSCROLL, 3, 4}, + {100, OSCROLL, 4, 3}, + {200, OSCROLL, 5, 2}, + {110, OSCROLL, 6, 1}, + {500, OSCROLL, 7, 2}, + {200, OSCROLL, 8, 2}, + {250, OSCROLL, 9, 4}, + {20, OSCROLL, 10, 5}, + {30, OSCROLL, 11, 3}, + + /* + * cost iven name iven arg how gp + * iven[] ivenarg[] many + */ + + {340, OSCROLL, 12, 1}, + {340, OSCROLL, 13, 1}, + {300, OSCROLL, 14, 2}, + {400, OSCROLL, 15, 2}, + {500, OSCROLL, 16, 2}, + {1000, OSCROLL, 17, 1}, + {500, OSCROLL, 18, 1}, + {340, OSCROLL, 19, 2}, + {220, OSCROLL, 20, 3}, + {3900, OSCROLL, 21, 0}, + {610, OSCROLL, 22, 1}, + {3000, OSCROLL, 23, 0} +}; + +/* + function for the dnd store + */ +static void +dnd_2hed(void) +{ + lprcat("Welcome to the Larn Thrift Shoppe. We stock many items explorers find useful\n"); + lprcat(" in their adventures. Feel free to browse to your hearts content.\n"); + lprcat("Also be advised, if you break 'em, you pay for 'em."); +} + +static void +dnd_hed(void) +{ + int i; + for (i = dnditm; i < 26 + dnditm; i++) + dnditem(i); + cursor(50, 18); + lprcat("You have "); +} + +static void +handsfull(void) +{ + lprcat("\nYou can't carry anything more!"); + lflush(); + nap(2200); +} + +static void +outofstock(void) +{ + lprcat("\nSorry, but we are out of that item."); + lflush(); + nap(2200); +} + +static void +nogold(void) +{ + lprcat("\nYou don't have enough gold to pay for that!"); + lflush(); + nap(2200); +} + +void +dndstore(void) +{ + int i; + dnditm = 0; + nosignal = 1; /* disable signals */ + clear(); + dnd_2hed(); + if (outstanding_taxes > 0) { + lprcat("\n\nThe Larn Revenue Service has ordered us to not do business with tax evaders.\n"); + beep(); + lprintf("They have also told us that you owe %ld gp in back taxes, and as we must\n", (long) outstanding_taxes); + lprcat("comply with the law, we cannot serve you at this time. Soo Sorry.\n"); + cursors(); + lprcat("\nPress "); + standout("escape"); + lprcat(" to leave: "); + lflush(); + i = 0; + while (i != '\33') + i = ttgetch(); + drawscreen(); + nosignal = 0; /* enable signals */ + return; + } + dnd_hed(); + while (1) { + cursor(59, 18); + lprintf("%ld gold pieces", (long) c[GOLD]); + cltoeoln(); + cl_dn(1, 20); /* erase to eod */ + lprcat("\nEnter your transaction ["); + standout("space"); + lprcat(" for more, "); + standout("escape"); + lprcat(" to leave]? "); + i = 0; + while ((i < 'a' || i > 'z') && (i != ' ') && (i != '\33') && (i != 12)) + i = ttgetch(); + if (i == 12) { + clear(); + dnd_2hed(); + dnd_hed(); + } else if (i == '\33') { + drawscreen(); + nosignal = 0; /* enable signals */ + return; + } else if (i == ' ') { + cl_dn(1, 4); + if ((dnditm += 26) >= MAXITM) + dnditm = 0; + dnd_hed(); + } else { /* buy something */ + lprc(i);/* echo the byte */ + i += dnditm - 'a'; + if (i >= MAXITM) + outofstock(); + else if (itm[i].qty <= 0) + outofstock(); + else if (pocketfull()) + handsfull(); + else if (c[GOLD] < itm[i].price * 10) + nogold(); + else { + if (itm[i].obj == OPOTION) { + potionname[itm[i].arg] = potionhide[itm[i].arg]; + } else if (itm[i].obj == OSCROLL) { + scrollname[itm[i].arg] = scrollhide[itm[i].arg]; + } + c[GOLD] -= itm[i].price * 10; + itm[i].qty--; + take(itm[i].obj, itm[i].arg); + if (itm[i].qty == 0) + dnditem(i); + nap(1001); + } + } + + } +} + +/* + dnditem(index) + + to print the item list; used in dndstore() enter with the index into itm + */ +static void +dnditem(int i) +{ + int j, k; + if (i >= MAXITM) + return; + cursor((j = (i & 1) * 40 + 1), (k = ((i % 26) >> 1) + 5)); + if (itm[i].qty == 0) { + lprintf("%39s", ""); + return; + } + lprintf("%c) ", (i % 26) + 'a'); + if (itm[i].obj == OPOTION) { + lprintf("potion of%s", potionhide[itm[i].arg]); + } else if (itm[i].obj == OSCROLL) { + lprintf("scroll of%s", scrollhide[itm[i].arg]); + } else + lprintf("%s", objectname[itm[i].obj]); + cursor(j + 31, k); + lprintf("%6ld", (long) (itm[i].price * 10)); +} + + + +/* + for the college of larn + */ +u_char course[26] = {0}; /* the list of courses taken */ +static char coursetime[] = {10, 15, 10, 20, 10, 10, 10, 5}; +/* + function to display the header info for the school + */ +static void +sch_hed(void) +{ + clear(); + lprcat("The College of Larn offers the exciting opportunity of higher education to\n"); + lprcat("all inhabitants of the caves. Here is a list of the class schedule:\n\n\n"); + lprcat("\t\t Course Name \t Time Needed\n\n"); + + if (course[0] == 0) + lprcat("\t\ta) Fighters Training I 10 mobuls"); /* line 7 of crt */ + lprc('\n'); + if (course[1] == 0) + lprcat("\t\tb) Fighters Training II 15 mobuls"); + lprc('\n'); + if (course[2] == 0) + lprcat("\t\tc) Introduction to Wizardry 10 mobuls"); + lprc('\n'); + if (course[3] == 0) + lprcat("\t\td) Applied Wizardry 20 mobuls"); + lprc('\n'); + if (course[4] == 0) + lprcat("\t\te) Behavioral Psychology 10 mobuls"); + lprc('\n'); + if (course[5] == 0) + lprcat("\t\tf) Faith for Today 10 mobuls"); + lprc('\n'); + if (course[6] == 0) + lprcat("\t\tg) Contemporary Dance 10 mobuls"); + lprc('\n'); + if (course[7] == 0) + lprcat("\t\th) History of Larn 5 mobuls"); + + lprcat("\n\n\t\tAll courses cost 250 gold pieces."); + cursor(30, 18); + lprcat("You are presently carrying "); +} + +void +oschool(void) +{ + int i; + long time_used; + nosignal = 1; /* disable signals */ + sch_hed(); + while (1) { + cursor(57, 18); + lprintf("%ld gold pieces. ", (long) c[GOLD]); + cursors(); + lprcat("\nWhat is your choice ["); + standout("escape"); + lprcat(" to leave] ? "); + yrepcount = 0; + i = 0; + while ((i < 'a' || i > 'h') && (i != '\33') && (i != 12)) + i = ttgetch(); + if (i == 12) { + sch_hed(); + continue; + } else if (i == '\33') { + nosignal = 0; + drawscreen(); /* enable signals */ + return; + } + lprc(i); + if (c[GOLD] < 250) + nogold(); + else if (course[i - 'a']) { + lprcat("\nSorry, but that class is filled."); + nap(1000); + } else if (i <= 'h') { + c[GOLD] -= 250; + time_used = 0; + switch (i) { + case 'a': + c[STRENGTH] += 2; + c[CONSTITUTION]++; + lprcat("\nYou feel stronger!"); + cl_line(16, 7); + break; + + case 'b': + if (course[0] == 0) { + lprcat("\nSorry, but this class has a prerequisite of Fighters Training I"); + c[GOLD] += 250; + time_used = -10000; + break; + } + lprcat("\nYou feel much stronger!"); + cl_line(16, 8); + c[STRENGTH] += 2; + c[CONSTITUTION] += 2; + break; + + case 'c': + c[INTELLIGENCE] += 2; + lprcat("\nThe task before you now seems more attainable!"); + cl_line(16, 9); + break; + + case 'd': + if (course[2] == 0) { + lprcat("\nSorry, but this class has a prerequisite of Introduction to Wizardry"); + c[GOLD] += 250; + time_used = -10000; + break; + } + lprcat("\nThe task before you now seems very attainable!"); + cl_line(16, 10); + c[INTELLIGENCE] += 2; + break; + + case 'e': + c[CHARISMA] += 3; + lprcat("\nYou now feel like a born leader!"); + cl_line(16, 11); + break; + + case 'f': + c[WISDOM] += 2; + lprcat("\nYou now feel more confident that you can find the potion in time!"); + cl_line(16, 12); + break; + + case 'g': + c[DEXTERITY] += 3; + lprcat("\nYou feel like dancing!"); + cl_line(16, 13); + break; + + case 'h': + c[INTELLIGENCE]++; + lprcat("\nYour instructor told you that the Eye of Larn is rumored to be guarded\n"); + lprcat("by a platinum dragon who possesses psionic abilities. "); + cl_line(16, 14); + break; + } + time_used += coursetime[i - 'a'] * 100; + if (time_used > 0) { + gltime += time_used; + course[i - 'a']++; /* remember that he has + * taken that course */ + c[HP] = c[HPMAX]; + c[SPELLS] = c[SPELLMAX]; /* he regenerated */ + + if (c[BLINDCOUNT]) + c[BLINDCOUNT] = 1; /* cure blindness too! */ + if (c[CONFUSE]) + c[CONFUSE] = 1; /* end confusion */ + adjusttime((long) time_used); /* adjust parameters for + * time change */ + } + nap(1000); + } + } +} + + +/* + * for the first national bank of Larn + */ +int lasttime = 0; /* last time he was in bank */ + +void +obank(void) +{ + banktitle(" Welcome to the First National Bank of Larn."); +} +void +obank2(void) +{ + banktitle("Welcome to the 5th level branch office of the First National Bank of Larn."); +} + +static void +banktitle(const char *str) +{ + nosignal = 1; /* disable signals */ + clear(); + lprcat(str); + if (outstanding_taxes > 0) { + int i; + lprcat("\n\nThe Larn Revenue Service has ordered that your account be frozen until all\n"); + beep(); + lprintf("levied taxes have been paid. They have also told us that you owe %ld gp in\n", (long) outstanding_taxes); + lprcat("taxes, and we must comply with them. We cannot serve you at this time. Sorry.\n"); + lprcat("We suggest you go to the LRS office and pay your taxes.\n"); + cursors(); + lprcat("\nPress "); + standout("escape"); + lprcat(" to leave: "); + lflush(); + i = 0; + while (i != '\33') + i = ttgetch(); + drawscreen(); + nosignal = 0; /* enable signals */ + return; + } + lprcat("\n\n\tGemstone\t Appraisal\t\tGemstone\t Appraisal"); + obanksub(); + nosignal = 0; /* enable signals */ + drawscreen(); +} + +/* + * function to put interest on your bank account + */ +void +ointerest(void) +{ + int i; + if (c[BANKACCOUNT] < 0) + c[BANKACCOUNT] = 0; + else if ((c[BANKACCOUNT] > 0) && (c[BANKACCOUNT] < 500000)) { + i = (gltime - lasttime) / 100; /* # mobuls elapsed */ + while ((i-- > 0) && (c[BANKACCOUNT] < 500000)) + c[BANKACCOUNT] += c[BANKACCOUNT] / 250; + if (c[BANKACCOUNT] > 500000) + c[BANKACCOUNT] = 500000; /* interest limit */ + } + lasttime = (gltime / 100) * 100; +} + +static short gemorder[26] = {0}; /* the reference to screen location + * for each */ +static long gemvalue[26] = {0}; /* the appraisal of the gems */ +void +obanksub(void) +{ + long amt; + int i, k; + ointerest(); /* credit any needed interest */ + + for (k = i = 0; i < 26; i++) + switch (iven[i]) { + case OLARNEYE: + case ODIAMOND: + case OEMERALD: + case ORUBY: + case OSAPPHIRE: + + if (iven[i] == OLARNEYE) { + gemvalue[i] = 250000 - ((gltime * 7) / 100) * 100; + if (gemvalue[i] < 50000) + gemvalue[i] = 50000; + } else + gemvalue[i] = (255 & ivenarg[i]) * 100; + gemorder[i] = k; + cursor((k % 2) * 40 + 1, (k >> 1) + 4); + lprintf("%c) %s", i + 'a', objectname[iven[i]]); + cursor((k % 2) * 40 + 33, (k >> 1) + 4); + lprintf("%5ld", (long) gemvalue[i]); + k++; + }; + cursor(31, 17); + lprintf("You have %8ld gold pieces in the bank.", (long) c[BANKACCOUNT]); + cursor(40, 18); + lprintf("You have %8ld gold pieces", (long) c[GOLD]); + if (c[BANKACCOUNT] + c[GOLD] >= 500000) + lprcat("\nNote: Larndom law states that only deposits under 500,000gp can earn interest."); + while (1) { + cl_dn(1, 20); + lprcat("\nYour wish? [("); + standout("d"); + lprcat(") deposit, ("); + standout("w"); + lprcat(") withdraw, ("); + standout("s"); + lprcat(") sell a stone, or "); + standout("escape"); + lprcat("] "); + yrepcount = 0; + i = 0; + while (i != 'd' && i != 'w' && i != 's' && i != '\33') + i = ttgetch(); + switch (i) { + case 'd': + lprcat("deposit\nHow much? "); + amt = readnum((long) c[GOLD]); + if (amt < 0) { + lprcat("\nSorry, but we can't take negative gold!"); + nap(2000); + amt = 0; + } else if (amt > c[GOLD]) { + lprcat(" You don't have that much."); + nap(2000); + } else { + c[GOLD] -= amt; + c[BANKACCOUNT] += amt; + } + break; + + case 'w': + lprcat("withdraw\nHow much? "); + amt = readnum((long) c[BANKACCOUNT]); + if (amt < 0) { + lprcat("\nSorry, but we don't have any negative gold!"); + nap(2000); + amt = 0; + } else if (amt > c[BANKACCOUNT]) { + lprcat("\nYou don't have that much in the bank!"); + nap(2000); + } else { + c[GOLD] += amt; + c[BANKACCOUNT] -= amt; + } + break; + + case 's': + lprcat("\nWhich stone would you like to sell? "); + i = 0; + while ((i < 'a' || i > 'z') && i != '*') + i = ttgetch(); + if (i == '*') + for (i = 0; i < 26; i++) { + if (gemvalue[i]) { + c[GOLD] += gemvalue[i]; + iven[i] = 0; + gemvalue[i] = 0; + k = gemorder[i]; + cursor((k % 2) * 40 + 1, (k >> 1) + 4); + lprintf("%39s", ""); + } + } + else { + if (gemvalue[i = i - 'a'] == 0) { + lprintf("\nItem %c is not a gemstone!", i + 'a'); + nap(2000); + break; + } + c[GOLD] += gemvalue[i]; + iven[i] = 0; + gemvalue[i] = 0; + k = gemorder[i]; + cursor((k % 2) * 40 + 1, (k >> 1) + 4); + lprintf("%39s", ""); + } + break; + + case '\33': + return; + }; + cursor(40, 17); + lprintf("%8ld", (long) c[BANKACCOUNT]); + cursor(49, 18); + lprintf("%8ld", (long) c[GOLD]); + } +} + +#if 0 /* XXX: apparently unused */ +/* + subroutine to appraise any stone for the bank + */ +static void +appraise(int gemstone) +{ + int j, amt; + for (j = 0; j < 26; j++) + if (iven[j] == gemstone) { + lprintf("\nI see you have %s", objectname[gemstone]); + if (gemstone == OLARNEYE) + lprcat(" I must commend you. I didn't think\nyou could get it."); + lprcat(" Shall I appraise it for you? "); + yrepcount = 0; + if (getyn() == 'y') { + lprcat("yes.\n Just one moment please \n"); + nap(1000); + if (gemstone == OLARNEYE) { + amt = 250000 - ((gltime * 7) / 100) * 100; + if (amt < 50000) + amt = 50000; + } else + amt = (255 & ivenarg[j]) * 100; + lprintf("\nI can see this is an excellent stone, It is worth %ld", (long) amt); + lprcat("\nWould you like to sell it to us? "); + yrepcount = 0; + if (getyn() == 'y') { + lprcat("yes\n"); + c[GOLD] += amt; + iven[j] = 0; + } else + lprcat("no thank you.\n"); + if (gemstone == OLARNEYE) + lprcat("It is, of course, your privilege to keep the stone\n"); + } else + lprcat("no\nO. K.\n"); + } +} +#endif /* 0 - unused */ + +/* + function for the trading post + */ +static void +otradhead(void) +{ + clear(); + lprcat("Welcome to the Larn Trading Post. We buy items that explorers no longer find\n"); + lprcat("useful. Since the condition of the items you bring in is not certain,\n"); + lprcat("and we incur great expense in reconditioning the items, we usually pay\n"); + lprcat("only 20% of their value were they to be new. If the items are badly\n"); + lprcat("damaged, we will pay only 10% of their new value.\n\n"); +} + +void +otradepost(void) +{ + int i, j, value, isub, izarg; + dnditm = dndcount = 0; + nosignal = 1; /* disable signals */ + resetscroll(); + otradhead(); + while (1) { + lprcat("\nWhat item do you want to sell to us ["); + standout("*"); + lprcat(" for list, or "); + standout("escape"); + lprcat("] ? "); + i = 0; + while (i > 'z' || (i < 'a' && i != '*' && i != '\33' && i != '.')) + i = ttgetch(); + if (i == '\33') { + setscroll(); + recalc(); + drawscreen(); + nosignal = 0; /* enable signals */ + return; + } + isub = i - 'a'; + j = 0; + if (iven[isub] == OSCROLL) + if (scrollname[ivenarg[isub]][0] == 0) { + j = 1; + cnsitm(); + } /* can't sell unidentified item */ + if (iven[isub] == OPOTION) + if (potionname[ivenarg[isub]][0] == 0) { + j = 1; + cnsitm(); + } /* can't sell unidentified item */ + if (!j) { + if (i == '*') { + clear(); + qshowstr(); + otradhead(); + } else if (iven[isub] == 0) + lprintf("\nYou don't have item %c!", isub + 'a'); + else { + for (j = 0; j < MAXITM; j++) + if ((itm[j].obj == iven[isub]) || (iven[isub] == ODIAMOND) || (iven[isub] == ORUBY) || (iven[isub] == OEMERALD) || (iven[isub] == OSAPPHIRE)) { + srcount = 0; + show3(isub); /* show what the item + * was */ + if ((iven[isub] == ODIAMOND) || (iven[isub] == ORUBY) + || (iven[isub] == OEMERALD) || (iven[isub] == OSAPPHIRE)) + value = 20 * ivenarg[isub]; + else if ((itm[j].obj == OSCROLL) || (itm[j].obj == OPOTION)) + value = 2 * itm[j + ivenarg[isub]].price; + else { + izarg = ivenarg[isub]; + value = itm[j].price; /* appreciate if a +n + * object */ + if (izarg >= 0) + value *= 2; + while ((izarg-- > 0) && ((value = 14 * (67 + value) / 10) < 500000)); + } + lprintf("\nItem (%c) is worth %ld gold pieces to us. Do you want to sell it? ", i, (long) value); + yrepcount = 0; + if (getyn() == 'y') { + lprcat("yes\n"); + c[GOLD] += value; + if (c[WEAR] == isub) + c[WEAR] = -1; + if (c[WIELD] == isub) + c[WIELD] = -1; + if (c[SHIELD] == isub) + c[SHIELD] = -1; + adjustcvalues(iven[isub], ivenarg[isub]); + iven[isub] = 0; + } else + lprcat("no thanks.\n"); + j = MAXITM + 100; /* get out of the inner + * loop */ + } + if (j <= MAXITM + 2) + lprcat("\nSo sorry, but we are not authorized to accept that item."); + } + } + } +} + +static void +cnsitm(void) +{ + lprcat("\nSorry, we can't accept unidentified objects."); +} + +/* + * for the Larn Revenue Service + */ +void +olrs(void) +{ + int i, first; + long amt; + first = nosignal = 1; /* disable signals */ + clear(); + resetscroll(); + cursor(1, 4); + lprcat("Welcome to the Larn Revenue Service district office. How can we help you?"); + while (1) { + if (first) { + first = 0; + goto nxt; + } + cursors(); + lprcat("\n\nYour wish? [("); + standout("p"); + lprcat(") pay taxes, or "); + standout("escape"); + lprcat("] "); + yrepcount = 0; + i = 0; + while (i != 'p' && i != '\33') + i = ttgetch(); + switch (i) { + case 'p': + lprcat("pay taxes\nHow much? "); + amt = readnum((long) c[GOLD]); + if (amt < 0) { + lprcat("\nSorry, but we can't take negative gold\n"); + amt = 0; + } else if (amt > c[GOLD]) + lprcat(" You don't have that much.\n"); + else + c[GOLD] -= paytaxes(amt); + break; + + case '\33': + nosignal = 0; /* enable signals */ + setscroll(); + drawscreen(); + return; + }; + +nxt: cursor(1, 6); + if (outstanding_taxes > 0) + lprintf("You presently owe %ld gp in taxes. ", (long) outstanding_taxes); + else + lprcat("You do not owe us any taxes. "); + cursor(1, 8); + if (c[GOLD] > 0) + lprintf("You have %6ld gp. ", (long) c[GOLD]); + else + lprcat("You have no gold pieces. "); + } +} diff --git a/larn/tok.c b/larn/tok.c new file mode 100644 index 0000000..6bab595 --- /dev/null +++ b/larn/tok.c @@ -0,0 +1,235 @@ +/* $NetBSD: tok.c,v 1.11 2012/06/19 05:30:44 dholland Exp $ */ + +/* tok.c Larn is copyrighted 1986 by Noah Morgan. */ +#include <sys/cdefs.h> +#ifndef lint +__RCSID("$NetBSD: tok.c,v 1.11 2012/06/19 05:30:44 dholland Exp $"); +#endif /* not lint */ + +#include <sys/types.h> +#include <string.h> +#include <sys/ioctl.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/wait.h> +#include <ctype.h> +#include "header.h" +#include "extern.h" + +/* Keystrokes (roughly) between checkpoints */ +#define CHECKPOINT_INTERVAL 400 + +static char lastok = 0; +int yrepcount = 0; +#ifndef FLUSHNO +#define FLUSHNO 5 +#endif /* FLUSHNO */ +static int flushno = FLUSHNO; /* input queue flushing threshold */ +#define MAXUM 52 /* maximum number of user re-named monsters */ +#define MAXMNAME 40 /* max length of a monster re-name */ +static char usermonster[MAXUM][MAXMNAME]; /* the user named monster + * name goes here */ +static u_char usermpoint = 0; /* the user monster pointer */ + +/* + lexical analyzer for larn + */ +int +yylex(void) +{ + char cc; + int ic; + if (hit2flag) { + hit2flag = 0; + yrepcount = 0; + return (' '); + } + if (yrepcount > 0) { + --yrepcount; + return (lastok); + } else + yrepcount = 0; + if (yrepcount == 0) { + bottomdo(); + showplayer(); + } /* show where the player is */ + lflush(); + while (1) { + c[BYTESIN]++; + /* check for periodic checkpointing */ + if (ckpflag) + if ((c[BYTESIN] % CHECKPOINT_INTERVAL) == 0) { +#ifndef DOCHECKPOINTS + savegame(ckpfile); +#else + wait(0); /* wait for other forks to + * finish */ + if (fork() == 0) { + savegame(ckpfile); + exit(); + } +#endif + } + do { /* if keyboard input buffer is too big, flush + * some of it */ + ioctl(0, FIONREAD, &ic); + if (ic > flushno) + read(0, &cc, 1); + } + while (ic > flushno); + + if (read(0, &cc, 1) != 1) + return (lastok = -1); + + if (cc == 'Y' - 64) { /* control Y -- shell escape */ + resetscroll(); + clear();/* scrolling region, home, clear, no + * attributes */ + if ((ic = fork()) == 0) { /* child */ + execl("/bin/csh", "/bin/csh", NULL); + exit(1); + } + wait(0); + if (ic < 0) { /* error */ + write(2, "Can't fork off a shell!\n", 25); + sleep(2); + } + setscroll(); + return (lastok = 'L' - 64); /* redisplay screen */ + } + if ((cc <= '9') && (cc >= '0')) { + yrepcount = yrepcount * 10 + cc - '0'; + } else { + if (yrepcount > 0) + --yrepcount; + return (lastok = cc); + } + } +} + +/* + * flushall() Function to flush all type-ahead in the input buffer + */ +void +flushall(void) +{ + char cc; + int ic; + for (;;) { /* if keyboard input buffer is too big, flush + * some of it */ + ioctl(0, FIONREAD, &ic); + if (ic <= 0) + return; + while (ic > 0) { + read(0, &cc, 1); + --ic; + } /* gobble up the byte */ + } +} + +/* + function to set the desired hardness + enter with hard= -1 for default hardness, else any desired hardness + */ +void +sethard(int hard) +{ + int j, k, i; + struct monst *mp; + + j = c[HARDGAME]; + hashewon(); + if (restorflag == 0) { /* don't set c[HARDGAME] if restoring game */ + if (hard >= 0) + c[HARDGAME] = hard; + } else + c[HARDGAME] = j;/* set c[HARDGAME] to proper value if + * restoring game */ + + if ((k = c[HARDGAME]) != 0) + for (j = 0; j <= MAXMONST + 8; j++) { + mp = &monster[j]; + i = ((6 + k) * mp->hitpoints + 1) / 6; + mp->hitpoints = (i < 0) ? 32767 : i; + i = ((6 + k) * mp->damage + 1) / 5; + mp->damage = (i > 127) ? 127 : i; + i = (10 * mp->gold) / (10 + k); + mp->gold = (i > 32767) ? 32767 : i; + i = mp->armorclass - k; + mp->armorclass = (i < -127) ? -127 : i; + i = (7 * mp->experience) / (7 + k) + 1; + mp->experience = (i <= 0) ? 1 : i; + } +} + +/* + function to read and process the larn options file + */ +void +readopts(void) +{ + const char *i; + int j, k; + int flag; + + flag = 1; /* set to 0 if a name is specified */ + + if (lopen(optsfile) < 0) { + strcpy(logname, loginname); + return; /* user name if no character name */ + } + i = " "; + while (*i) { + if ((i = lgetw()) == NULL) + break; /* check for EOF */ + while ((*i == ' ') || (*i == '\t')) + i++; /* eat leading whitespace */ + + if (strcmp(i, "bold-objects") == 0) + boldon = 1; + else if (strcmp(i, "enable-checkpointing") == 0) + ckpflag = 1; + else if (strcmp(i, "inverse-objects") == 0) + boldon = 0; + else if (strcmp(i, "female") == 0) + sex = 0; /* male or female */ + else if (strcmp(i, "monster:") == 0) { /* name favorite monster */ + if ((i = lgetw()) == 0) + break; + strlcpy(usermonster[usermpoint], i, MAXMNAME); + if (usermpoint >= MAXUM) + continue; /* defined all of em */ + if (isalpha(j = usermonster[usermpoint][0])) { + for (k = 1; k < MAXMONST + 8; k++) /* find monster */ + if (monstnamelist[k] == j) { + monster[k].name = &usermonster[usermpoint++][0]; + break; + } + } + } else if (strcmp(i, "male") == 0) + sex = 1; + else if (strcmp(i, "name:") == 0) { /* defining players name */ + if ((i = lgetw()) == 0) + break; + strlcpy(logname, i, LOGNAMESIZE); + flag = 0; + } else if (strcmp(i, "no-introduction") == 0) + nowelcome = 1; + else if (strcmp(i, "no-beep") == 0) + nobeep = 1; + else if (strcmp(i, "process-name:") == 0) { + if ((i = lgetw()) == 0) + break; + strlcpy(psname, i, PSNAMESIZE); + } else if (strcmp(i, "play-day-play") == 0) { + /* bypass time restrictions: ignored */ + } else if (strcmp(i, "savefile:") == 0) { /* defining savefilename */ + if ((i = lgetw()) == 0) + break; + strcpy(savefilename, i); + flag = 0; + } + } + if (flag) + strcpy(logname, loginname); +} @@ -0,0 +1,404 @@ +for dir in boggle bs cgram ching colorbars dab dm grdc hack hals_end larn paranoia rogue tetris; do ( cd $dir && pmake CFLAGS=" -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99" ); done +pmake: "/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/boggle/Makefile" line 8: warning: Couldn't read shell's output for "cd /home/urchlay/bsd-games-extra/bsd-games-extra-20150209/boggle/mkdict; " +pmake: "/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/boggle/Makefile" line 10: warning: Couldn't read shell's output for "cd /home/urchlay/bsd-games-extra/bsd-games-extra-20150209/boggle/mkindex; " +all ===> boggle +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -Werror -c bog.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -Werror -c help.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -Werror -c mach.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -Werror -c prtable.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -Werror -c timer.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -Werror -c word.c +cc -o boggle bog.o help.o mach.o prtable.o timer.o word.o -lcurses -lbsd +all ===> mkdict +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -Werror -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/boggle/mkdict/../boggle -c mkdict.c +cc -o mkdict mkdict.o +all ===> mkindex +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -Werror -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/boggle/mkindex/../boggle -c mkindex.c +cc -o mkindex mkindex.o +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c bs.c +cc -o bs bs.o -lncurses +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c cgram.c +cc -o cgram cgram.o -lcurses +all ===> ching +all ===> castching +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -Werror -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/ching/castching/../include -c castching.c +cc -o castching castching.o +all ===> printching +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -Werror -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/ching/printching/../include -c printching.c +cc -o printching printching.o +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c colorbars.c +cc -o colorbars colorbars.o -lcurses +c++ -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c algor.cc +cc1plus: warning: command line option ‘-std=gnu99’ is valid for C/ObjC but not for C++ [enabled by default] +cc1plus: warning: command line option ‘-std=gnu99’ is valid for C/ObjC but not for C++ [enabled by default] +c++ -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c board.cc +cc1plus: warning: command line option ‘-std=gnu99’ is valid for C/ObjC but not for C++ [enabled by default] +cc1plus: warning: command line option ‘-std=gnu99’ is valid for C/ObjC but not for C++ [enabled by default] +c++ -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c main.cc +cc1plus: warning: command line option ‘-std=gnu99’ is valid for C/ObjC but not for C++ [enabled by default] +cc1plus: warning: command line option ‘-std=gnu99’ is valid for C/ObjC but not for C++ [enabled by default] +c++ -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c human.cc +cc1plus: warning: command line option ‘-std=gnu99’ is valid for C/ObjC but not for C++ [enabled by default] +cc1plus: warning: command line option ‘-std=gnu99’ is valid for C/ObjC but not for C++ [enabled by default] +c++ -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c box.cc +cc1plus: warning: command line option ‘-std=gnu99’ is valid for C/ObjC but not for C++ [enabled by default] +cc1plus: warning: command line option ‘-std=gnu99’ is valid for C/ObjC but not for C++ [enabled by default] +c++ -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c player.cc +cc1plus: warning: command line option ‘-std=gnu99’ is valid for C/ObjC but not for C++ [enabled by default] +cc1plus: warning: command line option ‘-std=gnu99’ is valid for C/ObjC but not for C++ [enabled by default] +c++ -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c gamescreen.cc +cc1plus: warning: command line option ‘-std=gnu99’ is valid for C/ObjC but not for C++ [enabled by default] +cc1plus: warning: command line option ‘-std=gnu99’ is valid for C/ObjC but not for C++ [enabled by default] +c++ -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c ttyscrn.cc +cc1plus: warning: command line option ‘-std=gnu99’ is valid for C/ObjC but not for C++ [enabled by default] +cc1plus: warning: command line option ‘-std=gnu99’ is valid for C/ObjC but not for C++ [enabled by default] +c++ -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c random.cc +cc1plus: warning: command line option ‘-std=gnu99’ is valid for C/ObjC but not for C++ [enabled by default] +cc1plus: warning: command line option ‘-std=gnu99’ is valid for C/ObjC but not for C++ [enabled by default] +c++ -o dab algor.o board.o main.o human.o box.o player.o gamescreen.o ttyscrn.o random.o -lcurses -lm +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -I/usr.bin/who -DSUPPORT_UTMPX -DSUPPORT_UTMP -c dm.c +cc -o dm dm.o +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c grdc.c +cc -o grdc grdc.o -lncurses +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -o makedefs makedefs.c +./makedefs /home/urchlay/bsd-games-extra/bsd-games-extra-20150209/hack/def.objects.h > hack.onames.h +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c alloc.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.Decl.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.apply.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.bones.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.cmd.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.do.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.do_name.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.do_wear.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.dog.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.eat.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.end.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.engrave.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.fight.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.invent.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.ioctl.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.lev.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.main.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.makemon.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.mhitu.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.mklev.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.mkmaze.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.mkobj.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.mkshop.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.mon.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.monst.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.o_init.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.objnam.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.options.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.pager.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.potion.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.pri.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.read.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.rip.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.rumors.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.save.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.search.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.shk.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.shknam.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.steal.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.termcap.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.timeout.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.topl.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.track.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.trap.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.tty.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.u_init.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.unix.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.vault.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.version.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.wield.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.wizard.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.worm.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.worn.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.zap.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c rnd.c +cc -o hack alloc.o hack.Decl.o hack.apply.o hack.bones.o hack.o hack.cmd.o hack.do.o hack.do_name.o hack.do_wear.o hack.dog.o hack.eat.o hack.end.o hack.engrave.o hack.fight.o hack.invent.o hack.ioctl.o hack.lev.o hack.main.o hack.makemon.o hack.mhitu.o hack.mklev.o hack.mkmaze.o hack.mkobj.o hack.mkshop.o hack.mon.o hack.monst.o hack.o_init.o hack.objnam.o hack.options.o hack.pager.o hack.potion.o hack.pri.o hack.read.o hack.rip.o hack.rumors.o hack.save.o hack.search.o hack.shk.o hack.shknam.o hack.steal.o hack.termcap.o hack.timeout.o hack.topl.o hack.track.o hack.trap.o hack.tty.o hack.u_init.o hack.unix.o hack.vault.o hack.version.o hack.wield.o hack.wizard.o hack.worm.o hack.worn.o hack.zap.o rnd.o -ltermcap -lbsd +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hals_end.c +cc -o hals_end hals_end.o +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DSYSV -DVER=12 -DSUBVER=0 -DNONAP -DUIDSCORE -DTERMIOS -DVT100 -c main.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DSYSV -DVER=12 -DSUBVER=0 -DNONAP -DUIDSCORE -DTERMIOS -DVT100 -c object.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DSYSV -DVER=12 -DSUBVER=0 -DNONAP -DUIDSCORE -DTERMIOS -DVT100 -c create.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DSYSV -DVER=12 -DSUBVER=0 -DNONAP -DUIDSCORE -DTERMIOS -DVT100 -c tok.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DSYSV -DVER=12 -DSUBVER=0 -DNONAP -DUIDSCORE -DTERMIOS -DVT100 -c display.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DSYSV -DVER=12 -DSUBVER=0 -DNONAP -DUIDSCORE -DTERMIOS -DVT100 -c global.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DSYSV -DVER=12 -DSUBVER=0 -DNONAP -DUIDSCORE -DTERMIOS -DVT100 -c data.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DSYSV -DVER=12 -DSUBVER=0 -DNONAP -DUIDSCORE -DTERMIOS -DVT100 -c io.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DSYSV -DVER=12 -DSUBVER=0 -DNONAP -DUIDSCORE -DTERMIOS -DVT100 -c monster.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DSYSV -DVER=12 -DSUBVER=0 -DNONAP -DUIDSCORE -DTERMIOS -DVT100 -c store.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DSYSV -DVER=12 -DSUBVER=0 -DNONAP -DUIDSCORE -DTERMIOS -DVT100 -c diag.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DSYSV -DVER=12 -DSUBVER=0 -DNONAP -DUIDSCORE -DTERMIOS -DVT100 -c help.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DSYSV -DVER=12 -DSUBVER=0 -DNONAP -DUIDSCORE -DTERMIOS -DVT100 -c config.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DSYSV -DVER=12 -DSUBVER=0 -DNONAP -DUIDSCORE -DTERMIOS -DVT100 -c nap.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DSYSV -DVER=12 -DSUBVER=0 -DNONAP -DUIDSCORE -DTERMIOS -DVT100 -c bill.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DSYSV -DVER=12 -DSUBVER=0 -DNONAP -DUIDSCORE -DTERMIOS -DVT100 -c scores.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DSYSV -DVER=12 -DSUBVER=0 -DNONAP -DUIDSCORE -DTERMIOS -DVT100 -c signal.c +signal.c: In function ‘tstop’: +signal.c:81:2: warning: ‘sigsetmask’ is deprecated (declared at /usr/include/signal.h:200) [-Wdeprecated-declarations] + sigsetmask(sigblock(0) & ~BIT(SIGTSTP)); + ^ +signal.c:81:2: warning: ‘sigblock’ is deprecated (declared at /usr/include/signal.h:197) [-Wdeprecated-declarations] +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DSYSV -DVER=12 -DSUBVER=0 -DNONAP -DUIDSCORE -DTERMIOS -DVT100 -c action.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DSYSV -DVER=12 -DSUBVER=0 -DNONAP -DUIDSCORE -DTERMIOS -DVT100 -c moreobj.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DSYSV -DVER=12 -DSUBVER=0 -DNONAP -DUIDSCORE -DTERMIOS -DVT100 -c movem.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DSYSV -DVER=12 -DSUBVER=0 -DNONAP -DUIDSCORE -DTERMIOS -DVT100 -c regen.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DSYSV -DVER=12 -DSUBVER=0 -DNONAP -DUIDSCORE -DTERMIOS -DVT100 -c fortune.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DSYSV -DVER=12 -DSUBVER=0 -DNONAP -DUIDSCORE -DTERMIOS -DVT100 -c savelev.c +cc -o larn main.o object.o create.o tok.o display.o global.o data.o io.o monster.o store.o diag.o help.o config.o nap.o bill.o scores.o signal.o action.o moreobj.o movem.o regen.o fortune.o savelev.o -lcurses -lbsd +gcc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -o paranoia paranoia.c +paranoia.c:33:1: warning: return type defaults to ‘int’ [enabled by default] + more() + ^ +paranoia.c: In function ‘more’: +paranoia.c:41:3: warning: implicit declaration of function ‘character’ [-Wimplicit-function-declaration] + character(); + ^ +paranoia.c: At top level: +paranoia.c:47:1: warning: return type defaults to ‘int’ [enabled by default] + new_clone(resume) + ^ +paranoia.c:67:1: warning: return type defaults to ‘int’ [enabled by default] + dice_roll(number,faces) + ^ +paranoia.c:75:1: warning: return type defaults to ‘int’ [enabled by default] + instructions() + ^ +paranoia.c:96:1: warning: return type defaults to ‘int’ [enabled by default] + character() + ^ +paranoia.c:122:1: warning: return type defaults to ‘int’ [enabled by default] + choose(a,aptr,b,bptr) + ^ +paranoia.c:132:1: warning: return type defaults to ‘int’ [enabled by default] + page1() + ^ +paranoia.c:140:1: warning: return type defaults to ‘int’ [enabled by default] + page2() + ^ +paranoia.c:158:1: warning: return type defaults to ‘int’ [enabled by default] + page3() + ^ +paranoia.c:166:1: warning: return type defaults to ‘int’ [enabled by default] + page4() + ^ +paranoia.c:174:1: warning: return type defaults to ‘int’ [enabled by default] + page5() + ^ +paranoia.c:185:1: warning: return type defaults to ‘int’ [enabled by default] + page6() + ^ +paranoia.c:197:1: warning: return type defaults to ‘int’ [enabled by default] + page7() + ^ +paranoia.c:224:1: warning: return type defaults to ‘int’ [enabled by default] + page8() + ^ +paranoia.c:241:1: warning: return type defaults to ‘int’ [enabled by default] + page9() + ^ +paranoia.c:254:1: warning: return type defaults to ‘int’ [enabled by default] + page10() + ^ +paranoia.c:282:1: warning: return type defaults to ‘int’ [enabled by default] + page11() + ^ +paranoia.c:313:1: warning: return type defaults to ‘int’ [enabled by default] + page12() + ^ +paranoia.c:325:1: warning: return type defaults to ‘int’ [enabled by default] + page13() + ^ +paranoia.c:338:1: warning: return type defaults to ‘int’ [enabled by default] + page14() + ^ +paranoia.c:352:1: warning: return type defaults to ‘int’ [enabled by default] + page15() + ^ +paranoia.c:376:1: warning: return type defaults to ‘int’ [enabled by default] + page16() + ^ +paranoia.c:387:1: warning: return type defaults to ‘int’ [enabled by default] + page17() + ^ +paranoia.c:424:1: warning: return type defaults to ‘int’ [enabled by default] + page18() + ^ +paranoia.c:440:1: warning: return type defaults to ‘int’ [enabled by default] + page19() + ^ +paranoia.c:453:1: warning: return type defaults to ‘int’ [enabled by default] + page20() + ^ +paranoia.c:465:1: warning: return type defaults to ‘int’ [enabled by default] + page21() + ^ +paranoia.c:474:1: warning: return type defaults to ‘int’ [enabled by default] + page22() + ^ +paranoia.c:486:1: warning: return type defaults to ‘int’ [enabled by default] + page23() + ^ +paranoia.c:495:1: warning: return type defaults to ‘int’ [enabled by default] + page24() + ^ +paranoia.c:503:1: warning: return type defaults to ‘int’ [enabled by default] + page25() + ^ +paranoia.c:511:1: warning: return type defaults to ‘int’ [enabled by default] + page26() + ^ +paranoia.c:521:1: warning: return type defaults to ‘int’ [enabled by default] + page27() + ^ +paranoia.c:527:1: warning: return type defaults to ‘int’ [enabled by default] + page28() + ^ +paranoia.c:533:1: warning: return type defaults to ‘int’ [enabled by default] + page29() + ^ +paranoia.c:551:1: warning: return type defaults to ‘int’ [enabled by default] + page30() + ^ +paranoia.c:578:1: warning: return type defaults to ‘int’ [enabled by default] + page31() + ^ +paranoia.c:591:1: warning: return type defaults to ‘int’ [enabled by default] + page32() + ^ +paranoia.c:601:1: warning: return type defaults to ‘int’ [enabled by default] + page33() + ^ +paranoia.c:614:1: warning: return type defaults to ‘int’ [enabled by default] + page34() + ^ +paranoia.c:642:1: warning: return type defaults to ‘int’ [enabled by default] + page35() + ^ +paranoia.c:661:1: warning: return type defaults to ‘int’ [enabled by default] + page36() + ^ +paranoia.c:684:1: warning: return type defaults to ‘int’ [enabled by default] + page37() + ^ +paranoia.c:700:1: warning: return type defaults to ‘int’ [enabled by default] + page38() + ^ +paranoia.c:717:1: warning: return type defaults to ‘int’ [enabled by default] + page39() + ^ +paranoia.c:735:1: warning: return type defaults to ‘int’ [enabled by default] + page40() + ^ +paranoia.c: In function ‘page40’: +paranoia.c:740:9: warning: unknown escape sequence: '\`' [enabled by default] + printf("The Troubleshooter looks around with a \`who me?\' expression on his face, but\n"); + ^ +paranoia.c: At top level: +paranoia.c:767:1: warning: return type defaults to ‘int’ [enabled by default] + page41() + ^ +paranoia.c:781:1: warning: return type defaults to ‘int’ [enabled by default] + page42() + ^ +paranoia.c:791:1: warning: return type defaults to ‘int’ [enabled by default] + page43() + ^ +paranoia.c:807:1: warning: return type defaults to ‘int’ [enabled by default] + page44() + ^ +paranoia.c:825:1: warning: return type defaults to ‘int’ [enabled by default] + page45() + ^ +paranoia.c:839:1: warning: return type defaults to ‘int’ [enabled by default] + page46() + ^ +paranoia.c:847:1: warning: return type defaults to ‘int’ [enabled by default] + page47() + ^ +paranoia.c:859:1: warning: return type defaults to ‘int’ [enabled by default] + page48() + ^ +paranoia.c:869:1: warning: return type defaults to ‘int’ [enabled by default] + page49() + ^ +paranoia.c:876:1: warning: return type defaults to ‘int’ [enabled by default] + page50() + ^ +paranoia.c:885:1: warning: return type defaults to ‘int’ [enabled by default] + page51() + ^ +paranoia.c:892:1: warning: return type defaults to ‘int’ [enabled by default] + page52() + ^ +paranoia.c:899:1: warning: return type defaults to ‘int’ [enabled by default] + page53() + ^ +paranoia.c:905:1: warning: return type defaults to ‘int’ [enabled by default] + page54() + ^ +paranoia.c:919:1: warning: return type defaults to ‘int’ [enabled by default] + page55() + ^ +paranoia.c:947:1: warning: return type defaults to ‘int’ [enabled by default] + page56() + ^ +paranoia.c:955:1: warning: return type defaults to ‘int’ [enabled by default] + page57() + ^ +paranoia.c:962:1: warning: return type defaults to ‘int’ [enabled by default] + next_page(this_page) + ^ +paranoia.c:1030:1: warning: return type defaults to ‘int’ [enabled by default] + main() + ^ +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DUNIX -c hit.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DUNIX -c init.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DUNIX -c inventory.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DUNIX -c level.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DUNIX -c machdep.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DUNIX -c main.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DUNIX -c message.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DUNIX -c monster.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DUNIX -c move.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DUNIX -c object.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DUNIX -c pack.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DUNIX -c play.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DUNIX -c random.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DUNIX -c ring.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DUNIX -c room.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DUNIX -c save.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DUNIX -c score.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DUNIX -c spec_hit.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DUNIX -c throw.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DUNIX -c trap.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DUNIX -c use.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DUNIX -c zap.c +cc -o rogue hit.o init.o inventory.o level.o machdep.o main.o message.o monster.o move.o object.o pack.o play.o random.o ring.o room.o save.o score.o spec_hit.o throw.o trap.o use.o zap.o -lcurses -lbsd +all ===> USD.doc +groff -me rogue.me > paper.ps +rogue.me:158: warning [p 2, 2.8i, div `|k', 0.6i]: cannot adjust line +rogue.me:159: warning [p 2, 2.8i, div `|k', 0.7i]: cannot adjust line +rogue.me:160: warning [p 2, 2.8i, div `|k', 0.9i]: cannot adjust line +rogue.me:161: warning [p 2, 2.8i, div `|k', 1.1i]: cannot adjust line +rogue.me:162: warning [p 2, 2.8i, div `|k', 1.2i]: cannot adjust line +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c input.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c screen.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c shapes.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c scores.c +cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c tetris.c +cc -o tetris input.o screen.o shapes.o scores.o tetris.o -lcurses -lbsd +[ -x boggle/mkdict/mkdict ] && cd boggle && pmake realall +pmake: "/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/boggle/Makefile" line 8: warning: Couldn't read shell's output for "cd /home/urchlay/bsd-games-extra/bsd-games-extra-20150209/boggle/mkdict; " +pmake: "/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/boggle/Makefile" line 10: warning: Couldn't read shell's output for "cd /home/urchlay/bsd-games-extra/bsd-games-extra-20150209/boggle/mkindex; " +pmake: ${_MKTARGET_CREATE} expands to empty string +rm -f dictionary +./mkdict//mkdict < /usr/share/dict/words > dictionary +38620 words +pmake: ${_MKTARGET_CREATE} expands to empty string +rm -f dictindex +./mkindex//mkindex < dictionary > dictindex diff --git a/paranoia/Makefile b/paranoia/Makefile new file mode 100644 index 0000000..725d461 --- /dev/null +++ b/paranoia/Makefile @@ -0,0 +1,16 @@ +PROG= paranoia + +BINDIR=/usr/games +LIBDIR= $(DESTDIR)/lib +MANDIR= /usr/man/man6 +CC= gcc +CFLAGS= -O2 + +$(PROG): + $(CC) $(CFLAGS) -o $(PROG) $(PROG).c + +install: $(PROG) + install -c -o root -g root -m 755 $(PROG) $(DESTDIR)/$(BINDIR) + +clean: + rm -f $(PROG) core *.o diff --git a/paranoia/README b/paranoia/README new file mode 100644 index 0000000..fe31c76 --- /dev/null +++ b/paranoia/README @@ -0,0 +1,8 @@ + This is a very simple, but amusing, adventure game. It is based on the +role-playing game "Paranoia". It requires no instructions, as it is self- +prompting, and has been written in the simplest C that I could imagine, so +as to be implementable on almost any system. This also means that it could +be translateable to any other language. + + Note that since it uses random functions to simulate dice rolls, it will +choose its own pathways in some circumstances, unlike many adventure games. diff --git a/paranoia/README.linux b/paranoia/README.linux new file mode 100644 index 0000000..81b87b5 --- /dev/null +++ b/paranoia/README.linux @@ -0,0 +1,10 @@ +8/25/93 + +As usual the Makefiles were done completely from scratch. Verify that +DESTDIR is properly set in the Makefile. + +Note: we currently do not have a man page for this program. The program +is designed to not need a man page. Run it and see! + +Curt Olson +curt@sledge.mn.org diff --git a/paranoia/paranoia.c b/paranoia/paranoia.c new file mode 100644 index 0000000..251e530 --- /dev/null +++ b/paranoia/paranoia.c @@ -0,0 +1,1036 @@ +/* This is a solo paranoia game taken from the Jan/Feb issue (No 77) of + "SpaceGamer/FantasyGamer" magazine. + + Article by Sam Shirley. + + Implemented in C on Vax 11/780 under UNIX by Tim Lister + + This is a public domain adventure and may not be sold for profit */ + +#define MOXIE 13 +#define AGILITY 15 +#define MAXKILL 7 /* The maximum number of UV's you can kill */ + +int clone=1; +int page=1; +int computer_request=0; +int ultra_violet=0; +int action_doll=0; +int hit_points=10; +int read_letter=0; +int plato_clone=3; +int blast_door=0; +int killer_count=0; + +char get_char() +{ + char c; + c=getchar(); + if (c!='\n') while(getchar()!='\n'); + return c; +} + +more() +{ + printf("---------- More ----------"); +#ifdef DEBUG + printf("(page %d)",page); +#endif + if (get_char()=='p') + { + character(); + printf("---------- More ----------"); + (void)get_char(); + }; +} + +new_clone(resume) +int resume; +{ + printf("\nClone %d just died.\n",clone); + if (++clone>6) + { + printf("\n*** You Lose ***\n\nAll your clones are dead. Your name has been stricken from the records.\n\n THE END\n"); + return 0; + } + else + { + printf("Clone %d now activated.\n",clone); + ultra_violet=0; + action_doll=0; + hit_points=10; + killer_count=0; + return resume; + } +} + +dice_roll(number,faces) +int number, faces; +{ + int i,total=0; + for(i=number;i>0;i--) total+= rand()%faces+1; + return total; +} + +instructions() +{ + printf("\n\n\n\nWelcome to Paranoia!\n\n"); + printf("HOW TO PLAY:\n\n"); + printf(" Just press <RETURN> until you are asked to make a choice.\n"); + printf(" Select 'a' or 'b' or whatever for your choice, then press <RETURN>.\n"); + printf(" You may select 'p' at any time to get a display of your statistics.\n"); + printf(" Always choose the least dangerous option. Continue doing this until you win.\n"); + printf(" At times you will use a skill or engage in combat and and will be informed of\n"); + printf(" the outcome. These sections will be self explanatory.\n\n"); + printf("HOW TO DIE:\n\n"); + printf(" As Philo-R-DMD you will die at times during the adventure.\n"); + printf(" When this happens you will be given an new clone at a particular location.\n"); + printf(" The new Philo-R will usually have to retrace some of the old Philo-R\'s path;\n"); + printf(" hopefully he won\'t make the same mistake as his predecessor.\n\n"); + printf("HOW TO WIN:\n\n"); + printf(" Simply complete the mission before you expend all six clones.\n"); + printf(" If you make it, congratulations.\n"); + printf(" If not, you can try again later.\n"); +} + +character() +{ + printf("===============================================================================\n"); + printf("The Character : Philo-R-DMD %d\n", clone); + printf("Primary Attributes Secondary Attributes\n"); + printf("===============================================================================\n"); + printf("Strength ..................... 13 Carrying Capacity ................. 30\n"); + printf("Endurance .................... 13 Damage Bonus ....................... 0\n"); + printf("Agility ...................... 15 Macho Bonus ....................... -1\n"); + printf("Manual Dexterity ............. 15 Melee Bonus ...................... +5%%\n"); + printf("Moxie ........................ 13 Aimed Weapon Bonus .............. +10%%\n"); + printf("Chutzpah ...................... 8 Comprehension Bonus .............. +4%%\n"); + printf("Mechanical Aptitude .......... 14 Believability Bonus .............. +5%%\n"); + printf("Power Index .................. 10 Repair Bonus ..................... +5%%\n"); + printf("===============================================================================\n"); + printf("Credits: 160 Secret Society: Illuminati Secret Society Rank: 1\n"); + printf("Service Group: Power Services Mutant Power: Precognition\n"); + printf("Weapon: laser pistol; to hit, 40%%; type, L; Range, 50m; Reload, 6r; Malfnt, 00\n"); + printf("Skills: Basics 1(20%%), Aimed Weapon Combat 2(35%%), Laser 3(40%%),\n Personal Development 1(20%%), Communications 2(29%%), Intimidation 3(34%%)\n"); + printf("Equipment: Red Reflec Armour, Laser Pistol, Laser Barrel (red),\n"); + printf(" Notebook & Stylus, Knife, Com Unit 1, Jump suit,\n"); + printf(" Secret Illuminati Eye-In-The-Pyramid(tm) Decoder ring,\n"); + printf(" Utility Belt & Pouches\n"); + printf("===============================================================================\n"); +} + +choose(a,aptr,b,bptr) +int a,b; +char *aptr, *bptr; +{ + printf("\nSelect \'a\' or \'b\' :\n"); + printf(" a - %s.\n b - %s.\n", aptr, bptr); + if (get_char()=='a') return a; + else return b; +} + +page1() +{ + printf(" You wake up face down on the red and pink checked E-Z-Kleen linoleum floor.\n"); + printf(" You recognise the pattern, it\'s the type preferred in the internal security\nbriefing cells. When you finally look around you, you see that you are alone\n"); + printf("in a large mission briefing room.\n"); + return 57; +} + +page2() +{ + printf("\"Greetings,\" says the kindly Internal Security self incrimination expert who\n"); + printf("meets you at the door, \"How are we doing today?\" He offers you a doughnut\n"); + printf("and coffee and asks what brings you here. This doesn\'t seem so bad, so you\n"); + printf("tell him that you have come to confess some possible security lapses. He\n"); + printf("smiles knowingly, deftly catching your coffee as you slump to the floor.\n"); + printf("\"Nothing to be alarmed about; it\'s just the truth serum,\" he says,\n"); + printf("dragging you back into a discussion room.\n"); + printf("The next five hours are a dim haze, but you can recall snatches of conversation\n"); + printf("about your secret society, your mutant power, and your somewhat paranoid\n"); + printf("distrust of The Computer. This should explain why you are hogtied and moving\n"); + printf("slowly down the conveyer belt towards the meat processing unit in Food\n"); + printf("Services.\n"); + if (computer_request==1) return new_clone(45); + else return new_clone(32); +} + +page3() +{ + printf("You walk to the nearest Computer terminal and request more information about\n"); + printf("Christmas. The Computer says, \"That is an A-1 ULTRAVIOLET ONLY IMMEDIATE\n"); + printf("TERMINATION classified topic. What is your clearance please, Troubleshooter?\"\n"); + return choose(4,"You give your correct clearance",5,"You lie and claim Ultraviolet clearance"); +} + +page4() +{ + printf("\"That is classified information, Troubleshooter, thank you for your inquiry.\n"); + printf(" Please report to an Internal Security self incrimination station as soon as\n"); + printf(" possible.\"\n"); + return 9; +} + +page5() +{ + printf("The computer says, \"Troubleshooter, you are not wearing the correct colour\n"); + printf("uniform. You must put on an Ultraviolet uniform immediately. I have seen to\n"); + printf("your needs and ordered one already; it will be here shortly. Please wait with\n"); + printf("your back to the wall until it arrives.\" In less than a minute an infrared\n"); + printf("arrives carrying a white bundle. He asks you to sign for it, then hands it to\n"); + printf("you and stands back, well outside of a fragmentation grenade\'s blast radius.\n"); + return choose(6, "You open the package and put on the uniform", 7, "You finally come to your senses and run for it"); +} + +page6() +{ + printf("The uniform definitely makes you look snappy and pert. It really looks\n"); + printf("impressive, and even has the new lopsided lapel fashion that you admire so\n"); + printf("much. What\'s more, citizens of all ranks come to obsequious attention as you\n"); + printf("walk past. This isn\'t so bad being an Ultraviolet. You could probably come\n"); + printf("to like it, given time.\n"); + printf("The beeping computer terminal interrupts your musings.\n"); + ultra_violet=1; + return 8; +} + +page7() +{ + printf("The corridor lights dim and are replaced by red battle lamps as the Security\n"); + printf("Breach alarms howl all around you. You run headlong down the corridor and\n"); + printf("desperately windmill around a corner, only to collide with a squad of 12 Blue\n"); + printf("clearance Vulture squadron soldiers. \"Stop, Slime Face,\" shouts the\n"); + printf("commander, \"or there won\'t be enough of you left for a tissue sample.\"\n"); + printf("\"All right, soldiers, stuff the greasy traitor into the uniform,\" he orders,\n"); + printf("waving the business end of his blue laser scant inches from your nose.\n"); + printf("With his other hand he shakes open a white bundle to reveal a pristine new\n"); + printf("Ultraviolet citizen's uniform.\n"); + printf("One of the Vulture squadron Troubleshooters grabs you by the neck in the\n"); + printf("exotic and very painful Vulture Clamp(tm) death grip (you saw a special about\n"); + printf("it on the Teela O\'Malley show), while the rest tear off your clothes and\n"); + printf("force you into the Ultraviolet uniform. The moment you are dressed they step\n"); + printf("clear and stand at attention.\n"); + printf("\"Thank you for your cooperation, sir,\" says the steely eyed leader of the\n"); + printf("Vulture Squad. \"We will be going about our business now.\" With perfect\n"); + printf("timing the Vultures wheel smartly and goosestep down the corridor.\n"); + printf("Special Note: don\'t make the mistake of assuming that your skills have\n"); + printf("improved any because of the uniform; you\'re only a Red Troubleshooter\n"); + printf("traitorously posing as an Ultraviolet, and don\'t you forget it!\n"); + printf("Suddenly, a computer terminal comes to life beside you.\n"); + ultra_violet=1; + return 8; +} + +page8() +{ + printf("\"Now, about your question, citizen. Christmas was an old world marketing ploy\n"); + printf("to induce lower clearance citizens to purchase vast quantities of goods, thus\n"); + printf("accumulation a large amount of credit under the control of a single class of\n"); + printf("citizen known as Retailers. The strategy used is to imply that all good\n"); + printf("citizens give gifts during Christmas, thus if one wishes to be a valuable\n"); + printf("member of society one must also give gifts during Christmas. More valuable\n"); + printf("gifts make one a more valuable member, and thus did the Retailers come to\n"); + printf("control a disproportionate amount of the currency. In this way Christmas\n"); + printf("eventually caused the collapse of the old world. Understandably, Christmas\n"); + printf("has been declared a treasonable practice in Alpha Complex.\n"); + printf("Thank you for your inquiry.\"\n"); + printf("You continue on your way to GDH7-beta.\n"); + return 10; +} + +page9() +{ + int choice; + printf("As you walk toward the tubecar that will take you to GDH7-beta, you pass one\n"); + printf("of the bright blue and orange Internal Security self incrimination stations.\n"); + printf("Inside, you can see an IS agent cheerfully greet an infrared citizen and then\n"); + printf("lead him at gunpoint into one of the rubber lined discussion rooms.\n"); + choice=choose(2,"You decide to stop here and chat, as ordered by The Computer",10,"You just continue blithely on past"); + if (choice==2) computer_request = 1; + else computer_request = 0; + return choice; +} + +page10() +{ + int choice; + printf("You stroll briskly down the corridor, up a ladder, across an unrailed catwalk,\n"); + printf("under a perilously swinging blast door in urgent need of repair, and into\n"); + printf("tubecar grand central. This is the bustling hub of Alpha Complex tubecar\n"); + printf("transportation. Before you spreads a spaghetti maze of magnalift tube tracks\n"); + printf("and linear accelerators. You bravely study the specially enhanced 3-D tube\n"); + printf("route map; you wouldn\'t be the first Troubleshooter to take a fast tube ride\n"); + printf("to nowhere.\n"); + if (ultra_violet==0) + { + choice=choose(3,"You decide to ask The Computer about Christmas using a nearby terminal",10,"You think you have the route worked out, so you\'ll board a tube train"); + if (choice==3) return choice; + }; + printf("You nervously select a tubecar and step aboard.\n"); + if (dice_roll(2,10)<MOXIE) + { + printf("You just caught a purple line tubecar.\n"); + return 13; + } + else + { + printf("You just caught a brown line tubecar.\n"); + return 48; + } +} + +page11() +{ + printf("The printing on the folder says \"Experimental Self Briefing.\"\n"); + printf("You open it and begin to read the following:\n"); + printf("Step 1: Compel the briefing subject to attend the briefing.\n"); + printf(" Note: See Experimental Briefing Sub Form Indigo-WY-2,\n"); + printf(" \'Experimental Self Briefing Subject Acquisition Through The Use Of\n"); + printf(" Neurotoxin Room Foggers.\'\n"); + printf("Step 2: Inform the briefing subject that the briefing has begun.\n"); + printf(" ATTENTION: THE BRIEFING HAS BEGUN.\n"); + printf("Step 3: Present the briefing material to the briefing subject.\n"); + printf(" GREETINGS TROUBLESHOOTER.\n"); + printf(" YOU HAVE BEEN SPECIALLY SELECTED TO SINGLEHANDEDLY\n"); + printf(" WIPE OUT A DEN OF TRAITOROUS CHRISTMAS ACTIVITY. YOUR MISSION IS TO\n"); + printf(" GO TO GOODS DISTRIBUTION HALL 7-BETA AND ASSESS ANY CHRISTMAS ACTIVITY\n"); + printf(" YOU FIND THERE. YOU ARE TO INFILTRATE THESE CHRISTMAS CELEBRANTS,\n"); + printf(" LOCATE THEIR RINGLEADER, AN UNKNOWN MASTER RETAILER, AND BRING HIM\n"); + printf(" BACK FOR EXECUTION AND TRIAL. THANK YOU. THE COMPUTER IS YOUR FRIEND.\n"); + printf("Step 4: Sign the briefing subject\'s briefing release form to indicate that\n"); + printf(" the briefing subject has completed the briefing.\n"); + printf(" ATTENTION: PLEASE SIGN YOUR BRIEFING RELEASE FORM.\n"); + printf("Step 5: Terminate the briefing\n"); + printf(" ATTENTION: THE BRIEFING IS TERMINATED.\n"); + more(); + printf("You walk to the door and hold your signed briefing release form up to the\n"); + printf("plexiglass window. A guard scrutinises it for a moment and then slides back\n"); + printf("the megabolts holding the door shut. You are now free to continue the\n"); + printf("mission.\n"); + return choose(3,"You wish to ask The Computer for more information about Christmas",10,"You have decided to go directly to Goods Distribution Hall 7-beta"); +} + +page12() +{ + printf("You walk up to the door and push the button labelled \"push to exit.\"\n"); + printf("Within seconds a surly looking guard shoves his face into the small plexiglass\n"); + printf("window. You can see his mouth forming words but you can\'t hear any of them.\n"); + printf("You just stare at him blankly for a few moments until he points down to a\n"); + printf("speaker on your side of the door. When you put your ear to it you can barely\n"); + printf("hear him say, \"Let\'s see your briefing release form, bud. You aren\'t\n"); + printf("getting out of here without it.\"\n"); + return choose(11,"You sit down at the table and read the Orange packet",57,"You stare around the room some more"); +} + +page13() +{ + printf("You step into the shiny plasteel tubecar, wondering why the shape has always\n"); + printf("reminded you of bullets. The car shoots forward the instant your feet touch\n"); + printf("the slippery gray floor, pinning you immobile against the back wall as the\n"); + printf("tubecar careens toward GDH7-beta. Your only solace is the knowledge that it\n"); + printf("could be worse, much worse.\n"); + printf("Before too long the car comes to a stop. You can see signs for GDH7-beta\n"); + printf("through the window. With a little practice you discover that you can crawl\n"); + printf("to the door and pull open the latch.\n"); + return 14; +} + +page14() +{ + printf("You manage to pull yourself out of the tubecar and look around. Before you is\n"); + printf("one of the most confusing things you have ever seen, a hallway that is\n"); + printf("simultaneously both red and green clearance. If this is the result of\n"); + printf("Christmas then it\'s easy to see the evils inherent in its practice.\n"); + printf("You are in the heart of a large goods distribution centre. You can see all\n"); + printf("about you evidence of traitorous secret society Christmas celebration; rubber\n"); + printf("faced robots whiz back and forth selling toys to holiday shoppers, simul-plast\n"); + printf("wreaths hang from every light fixture, while ahead in the shadows is a citizen\n"); + printf("wearing a huge red synthetic flower.\n"); + return 22; +} + +page15() +{ + printf("You are set upon by a runty robot with a queer looking face and two pointy\n"); + printf("rubber ears poking from beneath a tattered cap. \"Hey mister,\" it says,\n"); + printf("\"you done all your last minute Christmas shopping? I got some real neat junk\n"); + printf("here. You don\'t wanna miss the big day tommorrow, if you know what I mean.\"\n"); + printf("The robot opens its bag to show you a pile of shoddy Troubleshooter dolls. It\n"); + printf("reaches in and pulls out one of them. \"Look, these Action Troubleshooter(tm)\n"); + printf("dolls are the neatest thing. This one\'s got moveable arms and when you\n"); + printf("squeeze him, his little rifle squirts realistic looking napalm. It\'s only\n"); + printf("50 credits. Oh yeah, Merry Christmas.\"\n"); + printf("\nSelect \'a\', \'b\' or \'c\' :\n"); + printf(" a - You decide to buy the doll.\n"); + printf(" b - You shoot the robot.\n"); + printf(" c - You ignore the robot and keep searching the hall.\n"); + switch(get_char()) + { + case 'a' : return 16; + case 'b' : return 17; + case 'c' : + default : return 22; + } +} + +page16() +{ + printf("The doll is a good buy for fifty credits; it will make a fine Christmas present\n"); + printf("for one of your friends. After the sale the robot rolls away. You can use\n"); + printf("the doll later in combat. It works just like a cone rifle firing napalm,\n"); + printf("except that occasionally it will explode and blow the user to smithereens.\n"); + printf("But don\'t let that stop you.\n"); + action_doll=1; + return 22; +} + +page17() +{ + int i, robot_hp=15; + printf("You whip out your laser and shoot the robot, but not before it squeezes the\n"); + printf("toy at you. The squeeze toy has the same effect as a cone rifle firing napalm,\n"); + printf("and the elfbot\'s armour has no effect against your laser.\n"); + for(i=0;i<2;i++) + { + if(dice_roll(1,100)<=25) + { + printf("You have been hit!\n"); + hit_points-= dice_roll(1,10); + if (hit_points<=0) return new_clone(45); + } + else printf("It missed you, but not by much!\n"); + if(dice_roll(1,100)<=40) + { + printf("You zapped the little bastard!\n"); + robot_hp-= dice_roll(2,10); + if (robot_hp<=0) + { + printf("You wasted it! Good shooting!\n"); + printf("You will need more evidence, so you search GDH7-beta further\n"); + if (hit_points<10) printf("after the GDH medbot has patched you up.\n"); + hit_points=10; + return 22; + } + } + else printf("Damn! You missed!\n"); + }; + printf("It tried to fire again, but the toy exploded and demolished it.\n"); + printf("You will need more evidence, so you search GDH7-beta further\n"); + if (hit_points<10) printf("after the GDH medbot has patched you up.\n"); + hit_points=10; + return 22; +} + +page18() +{ + printf("You walk to the centre of the hall, ogling like an infrared fresh from the\n"); + printf("clone vats. Towering before you is the most unearthly thing you have ever\n"); + printf("seen, a green multi armed mutant horror hulking 15 feet above your head.\n"); + printf("Its skeletal body is draped with hundreds of metallic strips (probably to\n"); + printf("negate the effects of some insidious mutant power), and the entire hideous\n"); + printf("creature is wrapped in a thousand blinking hazard lights. It\'s times like\n"); + printf("this when you wish you\'d had some training for this job. Luckily the\n"); + printf("creature doesn\'t take notice of you but stands unmoving, as though waiting for\n"); + printf("a summons from its dark lord, the Master Retailer.\n"); + printf("WHAM, suddenly you are struck from behind.\n"); + if (dice_roll(2,10)<AGILITY) return 19; + else return 20; +} + +page19() +{ + printf("Quickly you regain your balance, whirl and fire your laser into the Ultraviolet\n"); + printf("citizen behind you. For a moment your heart leaps to your throat, then you\n"); + printf("realise that he is indeed dead and you will be the only one filing a report on\n"); + printf("this incident. Besides, he was participating in this traitorous Christmas\n"); + printf("shopping, as is evident from the rain of shoddy toys falling all around you.\n"); + printf("Another valorous deed done in the service of The Computer!\n"); + if (++killer_count>(MAXKILL-clone)) return 21; + if (read_letter==1) return 22; + return choose(34,"You search the body, keeping an eye open for Internal Security",22,"You run away like the cowardly dog you are"); +} + +page20() +{ + printf("Oh no! you can\'t keep your balance. You\'re falling, falling head first into\n"); + printf("the Christmas beast\'s gaping maw. It\'s a valiant struggle; you think you are\n"); + printf("gone when its poisonous needles dig into your flesh, but with a heroic effort\n"); + printf("you jerk a string of lights free and jam the live wires into the creature\'s\n"); + printf("spine. The Christmas beast topples to the ground and begins to burn, filling\n"); + printf("the area with a thick acrid smoke. It takes only a moment to compose yourself,\n"); + printf("and then you are ready to continue your search for the Master Retailer.\n"); + return 22; +} + +page21() +{ + printf("You have been wasting the leading citizens of Alpha Complex at a prodigious\n"); + printf("rate. This has not gone unnoticed by the Internal Security squad at GDH7-beta.\n"); + printf("Suddenly, a net of laser beams spear out of the gloomy corners of the hall,\n"); + printf("chopping you into teeny, weeny bite size pieces.\n"); + return new_clone(45); +} + +page22() +{ + printf("You are searching Goods Distribution Hall 7-beta.\n"); + switch(dice_roll(1,4)) + { + case 1: return 18; + case 2: return 15; + case 3: return 18; + case 4: return 29; + } +} + +page23() +{ + printf("You go to the nearest computer terminal and declare yourself a mutant.\n"); + printf("\"A mutant, he\'s a mutant,\" yells a previously unnoticed infrared who had\n"); + printf("been looking over your shoulder. You easily gun him down, but not before a\n"); + printf("dozen more citizens take notice and aim their weapons at you.\n"); + return choose(28,"You tell them that it was really only a bad joke",24,"You want to fight it out, one against twelve"); +} + +page24() +{ + printf("Golly, I never expected someone to pick this. I haven\'t even designed\n"); + printf("the 12 citizens who are going to make a sponge out of you. Tell you what,\n"); + printf("I\'ll give you a second chance.\n"); + return choose(28,"You change your mind and say it was only a bad joke",25,"You REALLY want to shoot it out"); +} + +page25() +{ + printf("Boy, you really can\'t take a hint!\n"); + printf("They\'re closing in. Their trigger fingers are twitching, they\'re about to\n"); + printf("shoot. This is your last chance.\n"); + return choose(28,"You tell them it was all just a bad joke",26,"You are going to shoot"); +} + +page26() +{ + printf("You can read the cold, sober hatred in their eyes (They really didn\'t think\n"); + printf("it was funny), as they tighten the circle around you. One of them shoves a\n"); + printf("blaster up your nose, but that doesn\'t hurt as much as the multi-gigawatt\n"); + printf("carbonium tipped food drill in the small of your back.\n"); + printf("You spend the remaining micro-seconds of your life wondering what you did wrong\n"); + return new_clone(32); +} + +page27() +{ + /* doesn't exist. Can't happen with computer version. + designed to catch dice cheats */ +} + +page28() +{ + printf("They don\'t think it\'s funny.\n"); + return 26; +} + +page29() +{ + printf("\"Psst, hey citizen, come here. Pssfft,\" you hear. When you peer around\n"); + printf("you can see someone\'s dim outline in the shadows. \"I got some information\n"); + printf("on the Master Retailer. It\'ll only cost you 30 psst credits.\"\n"); + printf("\nSelect \'a\', \'b\' or \'c\' :\n"); + printf(" a - You pay the 30 credits for the info.\n"); + printf(" b - You would rather threaten him for the information.\n"); + printf(" c - You ignore him and walk away.\n"); + switch(get_char()) + { + case 'a' : return 30; + case 'b' : return 31; + case 'c' : + default : return 22; + } +} + +page30() +{ + printf("You step into the shadows and offer the man a thirty credit bill. \"Just drop\n"); + printf("it on the floor,\" he says. \"So you\'re looking for the Master Retailer, pssfft?\n"); + printf("I\'ve seen him, he\'s a fat man in a fuzzy red and white jump suit. They say\n"); + printf("he\'s a high programmer with no respect for proper security. If you want to\n"); + printf("find him then pssfft step behind me and go through the door.\"\n"); + printf("Behind the man is a reinforced plasteel blast door. The centre of it has been\n"); + printf("buckled toward you in a manner you only saw once before when you were field\n"); + printf("testing the rocket assist plasma slingshot (you found it easily portable but\n"); + printf("prone to misfire). Luckily it isn\'t buckled too far for you to make out the\n"); + printf("warning sign. WARNING!! Don\'t open this door or the same thing will happen to\n"); + printf("you. Opening this door is a capital offense. Do not do it. Not at all. This\n"); + printf("is not a joke.\n"); + printf("\nSelect \'a\', \'b\' or \'c\' :\n"); + printf(" a - You use your Precognition mutant power on opening the door.\n"); + printf(" b - You just go through the door anyway.\n"); + printf(" c - You decide it\'s too dangerous and walk away.\n"); + switch(get_char()) + { + case 'a' : return 56; + case 'b' : return 33; + case 'c' : + default : return 22; + } +} + +page31() +{ + printf("Like any good troubleshooter you make the least expensive decision and threaten\n"); + printf("him for information. With lightning like reflexes you whip out your laser and\n"); + printf("stick it up his nose. \"Talk, you traitorous Christmas celebrator, or who nose\n"); + printf("what will happen to you, yuk yuk,\" you pun menacingly, and then you notice\n"); + printf("something is very wrong. He doesn\'t have a nose. As a matter of fact he\'s\n"); + printf("made of one eighth inch cardboard and your laser is sticking through the other\n"); + printf("side of his head. \"Are you going to pay?\" says his mouth speaker,\n"); + printf("\"or are you going to pssfft go away stupid?\"\n"); + return choose(30,"You pay the 30 credits",22,"You pssfft go away stupid"); +} + +page32() +{ + printf("Finally it\'s your big chance to prove that you\'re as good a troubleshooter\n"); + printf("as your previous clone. You walk briskly to mission briefing and pick up your\n"); + printf("previous clone\'s personal effects and notepad. After reviewing the notes you\n"); + printf("know what has to be done. You catch the purple line to Goods Distribution Hall\n"); + printf("7-beta and begin to search for the blast door.\n"); + return 22; +} + +page33() +{ + blast_door=1; + printf("You release the megabolts on the blast door, then strain against it with your\n"); + printf("awesome strength. Slowly the door creaks open. You bravely leap through the\n"); + printf("opening and smack your head into the barrel of a 300 mm \'ultra shock\' class\n"); + printf("plasma cannon. It\'s dark in the barrel now, but just before your head got\n"); + printf("stuck you can remember seeing a group of technicians anxiously watch you leap\n"); + printf("into the room.\n"); + if (ultra_violet==1) return 35; + else return 36; +} + +page34() +{ + printf("You have found a sealed envelope on the body. You open it and read:\n"); + printf("\"WARNING: Ultraviolet Clearance ONLY. DO NOT READ.\n"); + printf("Memo from Chico-U-MRX4 to Harpo-U-MRX5.\n"); + printf("The planned takeover of the Troubleshooter Training Course goes well, Comrade.\n"); + printf("Once we have trained the unwitting bourgeois troubleshooters to work as\n"); + printf("communist dupes, the overthrow of Alpha Complex will be unstoppable. My survey\n"); + printf("of the complex has convinced me that no one suspects a thing; soon it will be\n"); + printf("too late for them to oppose the revolution. The only thing that could possibly\n"); + printf("impede the people\'s revolution would be someone alerting The Computer to our\n"); + printf("plans (for instance, some enterprising Troubleshooter could tell The Computer\n"); + printf("that the communists have liberated the Troubleshooter Training Course and plan\n"); + printf("to use it as a jumping off point from which to undermine the stability of all\n"); + printf("Alpha Complex), but as we both know, the capitalistic Troubleshooters would\n"); + printf("never serve the interests of the proletariat above their own bourgeois desires.\n"); + printf("P.S. I\'m doing some Christmas shopping later today. Would you like me to pick\n"); + printf("you up something?\"\n"); + more(); + printf("When you put down the memo you are overcome by that strange deja\'vu again.\n"); + printf("You see yourself talking privately with The Computer. You are telling it all\n"); + printf("about the communists\' plan, and then the scene shifts and you see yourself\n"); + printf("showered with awards for foiling the insidious communist plot to take over the\n"); + printf("complex.\n"); + read_letter=1; + return choose(46,"You rush off to the nearest computer terminal to expose the commies",22,"You wander off to look for more evidence"); +} + +page35() +{ + printf("\"Oh master,\" you hear through the gun barrel, \"where have you been? It is\n"); + printf("time for the great Christmas gifting ceremony. You had better hurry and get\n"); + printf("the costume on or the trainee may begin to suspect.\" For the second time\n"); + printf("today you are forced to wear attire not of your own choosing. They zip the\n"); + printf("suit to your chin just as you hear gunfire erupt behind you.\n"); + printf("\"Oh no! Who left the door open? The commies will get in. Quick, fire the\n"); + printf("laser cannon or we\'re all doomed.\"\n"); + printf("\"Too late you capitalist swine, the people\'s revolutionary strike force claims\n"); + printf("this cannon for the proletariat\'s valiant struggle against oppression. Take\n"); + printf("that, you running dog imperialist lackey. ZAP, KAPOW\"\n"); + printf("Just when you think that things couldn\'t get worse, \"Aha, look what we have\n"); + printf("here, the Master Retailer himself with his head caught in his own cannon. His\n"); + printf("death will serve as a symbol of freedom for all Alpha Complex.\n"); + printf("Fire the cannon.\"\n"); + return new_clone(32); +} + +page36() +{ + printf("\"Congratulations, troubleshooter, you have successfully found the lair of the\n"); + printf("Master Retailer and completed the Troubleshooter Training Course test mission,\"\n"); + printf("a muffled voice tells you through the barrel. \"Once we dislodge your head\n"); + printf("from the barrel of the \'Ultra Shock\' plasma cannon you can begin with the\n"); + printf("training seminars, the first of which will concern the 100%% accurate\n"); + printf("identification and elimination of unregistered mutants. If you have any\n"); + printf("objections please voice them now.\"\n"); + printf("\nSelect \'a\', \'b\' or \'c\' :\n"); + printf(" a - You appreciate his courtesy and voice an objection.\n"); + printf(" b - After your head is removed from the cannon, you register as a mutant.\n"); + printf(" c - After your head is removed from the cannon, you go to the unregistered\n"); + printf(" mutant identification and elimination seminar.\n"); + switch(get_char()) + { + case 'a' : return new_clone(32); + case 'b' : return 23; + case 'c' : + default : return 37; + } +} + +page37() +{ + printf("\"Come with me please, Troubleshooter,\" says the Green clearance technician\n"); + printf("after he has dislodged your head from the cannon. \"You have been participating\n"); + printf("in the Troubleshooter Training Course since you got off the tube car in\n"); + printf("GDH7-beta,\" he explains as he leads you down a corridor. \"The entire\n"); + printf("Christmas assignment was a test mission to assess your current level of\n"); + printf("training. You didn\'t do so well. We\'re going to start at the beginning with\n"); + printf("the other student. Ah, here we are, the mutant identification and elimination\n"); + printf("lecture.\" He shows you into a vast lecture hall filled with empty seats.\n"); + printf("There is only one other student here, a Troubleshooter near the front row\n"); + printf("playing with his Action Troubleshooter(tm) figure. \"Find a seat and I will\n"); + printf("begin,\" says the instructor.\n"); + return 38; +} + +page38() +{ + printf("\"I am Plato-B-PHI%d, head of mutant propaganda here at the training course.\n",plato_clone); + printf("If you have any questions about mutants please come to me. Today I will be\n"); + printf("talking about mutant detection. Detecting mutants is very easy. One simply\n"); + printf("watches for certain tell tale signs, such as the green scaly skin, the third\n"); + printf("arm growing from the forehead, or other similar disfigurements so common with\n"); + printf("their kind. There are, however, a few rare specimens that show no outward sign\n"); + printf("of their treason. This has been a significant problem, so our researchers have\n"); + printf("been working on a solution. I would like a volunteer to test this device,\"\n"); + printf("he says, holding up a ray gun looking thing. \"It is a mutant detection ray.\n"); + printf("This little button detects for mutants, and this big button stuns them once\n"); + printf("they are discovered. Who would like to volunteer for a test?\"\n"); + printf("The Troubleshooter down the front squirms deeper into his chair.\n"); + return choose(39,"You volunteer for the test",40,"You duck behind a chair and hope the instructor doesn\'t notice you"); +} + +page39() +{ + printf("You bravely volunteer to test the mutant detection gun. You stand up and walk\n"); + printf("down the steps to the podium, passing a very relieved Troubleshooter along the\n"); + printf("way. When you reach the podium Plato-B-PHI hands you the mutant detection gun\n"); + printf("and says, \"Here, aim the gun at that Troubleshooter and push the small button.\n"); + printf("If you see a purple light, stun him.\" Grasping the opportunity to prove your\n"); + printf("worth to The Computer, you fire the mutant detection ray at the Troubleshooter.\n"); + printf("A brilliant purple nimbus instantly surrounds his body. You slip your finger\n"); + printf("to the large stun button and he falls writhing to the floor.\n"); + printf("\"Good shot,\" says the instructor as you hand him the mutant detection gun,\n"); + printf("\"I\'ll see that you get a commendation for this. It seems you have the hang\n"); + printf("of mutant detection and elimination. You can go on to the secret society\n"); + printf("infiltration class. I\'ll see that the little mutie gets packaged for\n"); + printf("tomorrow\'s mutant dissection class.\"\n"); + return 41; +} + +page40() +{ + printf("You breathe a sigh of relief as Plato-B-PHI picks on the other Troubleshooter.\n"); + printf("\"You down here in the front,\" says the instructor pointing at the other\n"); + printf("Troubleshooter, \"you\'ll make a good volunteer. Please step forward.\"\n"); + printf("The Troubleshooter looks around with a \`who me?\' expression on his face, but\n"); + printf("since he is the only one visible in the audience he figures his number is up.\n"); + printf("He walks down to the podium clutching his Action Troubleshooter(tm) doll before\n"); + printf("him like a weapon. \"Here,\" says Plato-B-PHI, \"take the mutant detection ray\n"); + printf("and point it at the audience. If there are any mutants out there we\'ll know\n"); + printf("soon enough.\" Suddenly your skin prickles with static electricity as a bright\n"); + printf("purple nimbus surrounds your body. \"Ha Ha, got one,\" says the instructor.\n"); + printf("\"Stun him before he gets away.\"\n"); + more(); + while(1) + { + if (dice_roll(1,100)<=30) + { + printf("His shot hits you. You feel numb all over.\n"); + return 49; + } + else printf("His shot just missed.\n"); + + if (dice_roll(1,100)<=40) + { + printf("You just blew his head off. His lifeless hand drops the mutant detector ray.\n"); + return 50; + } + else printf("You burnt a hole in the podium. He sights the mutant detector ray on you.\n"); + } +} + +page41() +{ + printf("You stumble down the hallway of the Troubleshooter Training Course looking for\n"); + printf("your next class. Up ahead you see one of the instructors waving to you. When\n"); + printf("you get there he shakes your hand and says, \"I am Jung-I-PSY. Welcome to the\n"); + printf("secret society infiltration seminar. I hope you ...\" You don\'t catch the\n"); + printf("rest of his greeting because you\'re paying too much attention to his handshake;\n"); + printf("it is the strangest thing that has ever been done to your hand, sort of how it\n"); + printf("would feel if you put a neuro whip in a high energy palm massage unit.\n"); + printf("It doesn\'t take you long to learn what he is up to; you feel him briefly shake\n"); + printf("your hand with the secret Illuminati handshake.\n"); + return choose(42,"You respond with the proper Illuminati code phrase, \"Ewige Blumenkraft\"",43,"You ignore this secret society contact"); +} + +page42() +{ + printf("\"Aha, so you are a member of the elitist Illuminati secret society,\" he says\n"); + printf("loudly, \"that is most interesting.\" He turns to the large class already\n"); + printf("seated in the auditorium and says, \"You see, class, by simply using the correct\n"); + printf("hand shake you can identify the member of any secret society. Please keep your\n"); + printf("weapons trained on him while I call a guard.\n"); + return choose(51,"You run for it",52,"You wait for the guard"); +} + +page43() +{ + printf("You sit through a long lecture on how to recognise and infiltrate secret\n"); + printf("societies, with an emphasis on mimicking secret handshakes. The basic theory,\n"); + printf("which you realise to be sound from your Iluminati training, is that with the\n"); + printf("proper handshake you can pass unnoticed in any secret society gathering.\n"); + printf("What\'s more, the proper handshake will open doors faster than an \'ultra shock\'\n"); + printf("plasma cannon. You are certain that with the information you learn here you\n"); + printf("will easily be promoted to the next level of your Illuminati secret society.\n"); + printf("The lecture continues for three hours, during which you have the opportunity\n"); + printf("to practice many different handshakes. Afterwards everyone is directed to\n"); + printf("attend the graduation ceremony. Before you must go you have a little time to\n"); + printf("talk to The Computer about, you know, certain topics.\n"); + return choose(44,"You go looking for a computer terminal",55,"You go to the graduation ceremony immediately"); +} + +page44() +{ + printf("You walk down to a semi-secluded part of the training course complex and\n"); + printf("activate a computer terminal. \"AT YOUR SERVICE\" reads the computer screen.\n"); + if (read_letter==0) return choose(23,"You register yourself as a mutant",55,"You change your mind and go to the graduation ceremony"); + printf("\nSelect \'a\', \'b\' or \'c\' :\n"); + printf(" a - You register yourself as a mutant.\n"); + printf(" b - You want to chat about the commies.\n"); + printf(" c - You change your mind and go to the graduation ceremony.\n"); + switch(get_char()) + { + case 'a' : return 23; + case 'b' : return 46; + case 'c' : + default : return 55; + } +} + +page45() +{ + printf("\"Hrank Hrank,\" snorts the alarm in your living quarters. Something is up.\n"); + printf("You look at the monitor above the bathroom mirror and see the message you have\n"); + printf("been waiting for all these years. \"ATTENTION TROUBLESHOOTER, YOU ARE BEING\n"); + printf("ACTIVATED. PLEASE REPORT IMMEDIATELY TO MISSION ASSIGNMENT ROOM A17/GAMMA/LB22.\n"); + printf("THANK YOU. THE COMPUTER IS YOUR FRIEND.\" When you arrive at mission\n"); + printf("assignment room A17-gamma/LB22 you are given your previous clone\'s\n"); + printf("remaining possessions and notebook. You puzzle through your predecessor\'s\n"); + printf("cryptic notes, managing to decipher enough to lead you to the tube station and\n"); + printf("the tube car to GDH7-beta.\n"); + return 10; +} + +page46() +{ + printf("\"Why do you ask about the communists, Troubleshooter? It is not in the\n"); + printf("interest of your continued survival to be asking about such topics,\" says\n"); + printf("The Computer.\n"); + return choose(53,"You insist on talking about the communists",54,"You change the subject"); +} + +page47() +{ + printf("The Computer orders the entire Vulture squadron to terminate the Troubleshooter\n"); + printf("Training Course. Unfortunately you too are terminated for possessing\n"); + printf("classified information.\n\n"); + printf("Don\'t act so innocent, we both know that you are an Illuminatus which is in\n"); + printf("itself an act of treason.\n\n"); + printf("Don\'t look to me for sympathy.\n\n"); + printf(" THE END\n"); + return 0; +} + +page48() +{ + printf("The tubecar shoots forward as you enter, slamming you back into a pile of\n"); + printf("garbage. The front end rotates upward and you, the garbage and the garbage\n"); + printf("disposal car shoot straight up out of Alpha Complex. One of the last things\n"); + printf("you see is a small blue sphere slowly dwindling behind you. After you fail to\n"); + printf("report in, you will be assumed dead.\n"); + return new_clone(45); +} + +page49() +{ + printf("The instructor drags your inert body into a specimen detainment cage.\n"); + printf("\"He\'ll make a good subject for tomorrow\'s mutant dissection class,\" you hear.\n"); + return new_clone(32); +} + +page50() +{ + printf("You put down the other Troubleshooter, and then wisely decide to drill a few\n"); + printf("holes in the instructor as well; the only good witness is a dead witness.\n"); + printf("You continue with the training course.\n"); + plato_clone++; + return 41; +} + +page51() +{ + printf("You run for it, but you don\'t run far. Three hundred strange and exotic\n"); + printf("weapons turn you into a freeze dried cloud of soot.\n"); + return new_clone(32); +} + +page52() +{ + printf("You wisely wait until the instructor returns with a Blue Internal Security\n"); + printf("guard. The guard leads you to an Internal Security self incrimination station.\n"); + return 2; +} + +page53() +{ + printf("You tell The Computer about:\n"); + return choose(47,"The commies who have infiltrated the Troubleshooter Training Course\n and the impending People\'s Revolution",54,"Something less dangerous"); +} + +page54() +{ + printf("\"Do not try to change the subject, Troubleshooter,\" says The Computer.\n"); + printf("\"It is a serious crime to ask about the communists. You will be terminated\n"); + printf("immediately. Thank you for your inquiry. The Computer is your friend.\"\n"); + printf("Steel bars drop to your left and right, trapping you here in the hallway.\n"); + printf("A spotlight beams from the computer console to brilliantly iiluminate you while\n"); + printf("the speaker above your head rapidly repeats \"Traitor, Traitor, Traitor.\"\n"); + printf("It doesn\'t take long for a few guards to notice your predicament and come to\n"); + printf("finish you off.\n"); + if (blast_door==0) return new_clone(45); + else return new_clone(32); +} + +page55() +{ + printf("You and 300 other excited graduates are marched from the lecture hall and into\n"); + printf("a large auditorium for the graduation exercise. The auditorium is\n"); + printf("extravagantly decorated in the colours of the graduating class. Great red and\n"); + printf("green plasti-paper ribbons drape from the walls, while a huge sign reading\n"); + printf("\"Congratulations class of GDH7-beta-203.44/A\" hangs from the raised stage down\n"); + printf("front. Once everyone finds a seat the ceremony begins. Jung-I-PSY is the\n"); + printf("first to speak, \"Congratulations students, you have successfully survived the\n"); + printf("Troubleshooter Training Course. It always brings me great pride to address\n"); + printf("the graduating class, for I know, as I am sure you do too, that you are now\n"); + printf("qualified for the most perilous missions The Computer may select for you. The\n"); + printf("thanks is not owed to us of the teaching staff, but to all of you, who have\n"); + printf("persevered and graduated. Good luck and die trying.\" Then the instructor\n"); + printf("begins reading the names of the students who one by one walk to the front of\n"); + printf("the auditorium and receive their diplomas. Soon it is your turn,\n"); + printf("\"Philo-R-DMD, graduating a master of mutant identification and secret society\n"); + printf("infiltration.\" You walk up and receive your diploma from Plato-B-PHI%d, then\n",plato_clone); + printf("return to your seat. There is another speech after the diplomas are handed\n"); + printf("out, but it is cut short by by rapid fire laser bursts from the high spirited\n"); + printf("graduating class. You are free to return to your barracks to wait, trained\n"); + printf("and fully qualified, for your next mission. You also get that cherished\n"); + printf("promotion from the Illuminati secret society. In a week you receive a\n"); + printf("detailed Training Course bill totalling 1,523 credits.\n"); + printf(" THE END\n"); + return 0; +} + +page56() +{ + printf("That familiar strange feeling of deja\'vu envelops you again. It is hard to\n"); + printf("say, but whatever is on the other side of the door does not seem to be intended\n"); + printf("for you.\n"); + return choose(33,"You open the door and step through",22,"You go looking for more information"); +} + +page57() +{ + printf("In the centre of the room is a table and a single chair. There is an Orange\n"); + printf("folder on the table top, but you can\'t make out the lettering on it.\n"); + return choose(11,"You sit down and read the folder",12,"You leave the room"); +} + +next_page(this_page) +int this_page; +{ + printf("\n"); + switch (this_page) + { + case 0 : return 0; + case 1 : return page1(); + case 2 : return page2(); + case 3 : return page3(); + case 4 : return page4(); + case 5 : return page5(); + case 6 : return page6(); + case 7 : return page7(); + case 8 : return page8(); + case 9 : return page9(); + case 10 : return page10(); + case 11 : return page11(); + case 12 : return page12(); + case 13 : return page13(); + case 14 : return page14(); + case 15 : return page15(); + case 16 : return page16(); + case 17 : return page17(); + case 18 : return page18(); + case 19 : return page19(); + case 20 : return page20(); + case 21 : return page21(); + case 22 : return page22(); + case 23 : return page23(); + case 24 : return page24(); + case 25 : return page25(); + case 26 : return page26(); + case 27 : return page27(); + case 28 : return page28(); + case 29 : return page29(); + case 30 : return page30(); + case 31 : return page31(); + case 32 : return page32(); + case 33 : return page33(); + case 34 : return page34(); + case 35 : return page35(); + case 36 : return page36(); + case 37 : return page37(); + case 38 : return page38(); + case 39 : return page39(); + case 40 : return page40(); + case 41 : return page41(); + case 42 : return page42(); + case 43 : return page43(); + case 44 : return page44(); + case 45 : return page45(); + case 46 : return page46(); + case 47 : return page47(); + case 48 : return page48(); + case 49 : return page49(); + case 50 : return page50(); + case 51 : return page51(); + case 52 : return page52(); + case 53 : return page53(); + case 54 : return page54(); + case 55 : return page55(); + case 56 : return page56(); + case 57 : return page57(); + default : break; + } +} + +main() +{ + srand(time(0)); + instructions(); more(); + character(); more(); + while((page=next_page(page))!=0) more(); +} diff --git a/rogue/CHANGES b/rogue/CHANGES new file mode 100644 index 0000000..066ac12 --- /dev/null +++ b/rogue/CHANGES @@ -0,0 +1,55 @@ +$NetBSD: CHANGES,v 1.3 2000/03/13 22:53:22 soren Exp $ + +From: tektronix!zeus.TEK.COM!tims@ucbvax.Berkeley.EDU +Date: 30 Nov 87 15:08:15 PST (Mon) +To: okeeffe.Berkeley.EDU!mckusick@ucbvax.Berkeley.EDU (Kirk McKusick) +Subject: Re: Public domain rogue +Return-Path: tektronix!zeus.TEK.COM!tims@ucbvax.Berkeley.EDU + +Here is a list of discrepencies from the documentation you sent me: + +The -d option not implemented. +The -r option not implemented, use "rogue save_file" instead. +Strength is between 1 and 99, not 3 and 32. +The D command is not implemented. +Only scrolls,potions,wands,and rings may be "call"ed something. +The ^P command may be used to go 4 messages back, instead of just 1. +The @ comand is not implemented. +There are no dark rooms. +ROGUEOPTS of flush,terse,seefloor,askme,inventory are ignored. + 'askquit' is added to prevent ^\ from terminating the game accidentally. + If 'noaskquit' is + found in the ROGUEOPTS string, the ^\ kills the game, otherwise, + the player is asked if he really wants to quit. In either case, no + score file processing is attempted. +The score is keyed to winning scores, and no player may appear twice. + + + + + + +Other differences from "standard" rogue 5.3. This list covers externally +visible differences only. + +There should be NO bugs with any severe consequences. Absolutely NO + game-stopping, or game-winning bugs should be present. +Traps fail occasionally, that is, they sometimes are sprung but miss. +The ^A command prints out some stuff you're probably not interested in. +The '&' command silently saves your screen into the file 'rogue.screen' +Any inventory selection command that takes '*' as a request to list all + appropriate items, can take one of "=?:)]!/" to list only rings, + scrolls, or whatever. +Scrolls and potions, once used, become identified. All other objects become + identified only by scroll of identification. +There is only one scroll of identification, and it works on any item. +ROGUEOPTS + Only the following are implemented: + file,jump,name,askquit,tombstone,passgo + "askquit" is used to prevent accidental termination of the game via ^\ +You may drop objects in doorways. +Prints a picture of a skull, not a tombstone, upon death. +The save/restore game function is faster and machine-independent, but sometimes + requires modification when new variables are added to the source. +The potion of detect monster lasts for the whole level. +Their is no wand of light. diff --git a/rogue/Makefile b/rogue/Makefile new file mode 100644 index 0000000..a0db14a --- /dev/null +++ b/rogue/Makefile @@ -0,0 +1,19 @@ +# $NetBSD: Makefile,v 1.19 2014/07/05 19:22:03 dholland Exp $ +# @(#)Makefile 8.1 (Berkeley) 5/31/93 + +PROG= rogue +CPPFLAGS+=-DUNIX +SRCS= hit.c init.c inventory.c level.c machdep.c main.c \ + message.c monster.c move.c object.c pack.c play.c random.c ring.c \ + room.c save.c score.c spec_hit.c throw.c trap.c use.c zap.c +DPADD= ${LIBCURSES} ${LIBTERMINFO} +# 20150209 bkw: remove -lterminfo, add -lbsd +LDADD= -lcurses -lbsd +HIDEGAME=hidegame +SETGIDGAME=yes +MAN= rogue.6 + +SUBDIR+=USD.doc + +.include <bsd.prog.mk> +.include <bsd.subdir.mk> diff --git a/rogue/USD.doc/Makefile b/rogue/USD.doc/Makefile new file mode 100644 index 0000000..4e0bf2e --- /dev/null +++ b/rogue/USD.doc/Makefile @@ -0,0 +1,10 @@ +# $NetBSD: Makefile,v 1.7 2014/07/05 19:22:42 dholland Exp $ +# @(#)Makefile 8.1 (Berkeley) 6/8/93 + +SECTION=reference/ref6 +ARTICLE=rogue +SRCS= rogue.me +MACROS= -me +ROFF_TBL=yes + +.include <bsd.doc.mk> diff --git a/rogue/USD.doc/rogue.me b/rogue/USD.doc/rogue.me new file mode 100644 index 0000000..b1592dd --- /dev/null +++ b/rogue/USD.doc/rogue.me @@ -0,0 +1,834 @@ +.\" $NetBSD: rogue.me,v 1.6 2004/02/13 11:36:08 wiz Exp $ +.\" +.\" Copyright (c) 1986, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)rogue.me 8.2 (Berkeley) 6/1/94 +.\" +.ds E \s-2<ESCAPE>\s0 +.ds R \s-2<RETURN>\s0 +.ds U \s-2UNIX\s0 +.ie t .ds _ \d\(mi\u +.el .ds _ _ +.de Cs +\&\\$3\*(lq\\$1\*(rq\\$2 +.. +.sp 5 +.ce 1000 +.ps +4 +.vs +4p +.b +A Guide to the Dungeons of Doom +.r +.vs +.ps +.sp 2 +.i +Michael C. Toy +Kenneth C. R. C. Arnold +.r +.sp 2 +Computer Systems Research Group +Department of Electrical Engineering and Computer Science +University of California +Berkeley, California 94720 +.sp 4 +.i ABSTRACT +.ce 0 +.(b I F +.bi Rogue +is a visual CRT based fantasy game +which runs under the \*U\(dg timesharing system. +.(f +\fR\(dg\*U is a trademark of Bell Laboratories\fP +.)f +This paper describes how to play rogue, +and gives a few hints +for those who might otherwise get lost in the Dungeons of Doom. +.)b +\".he '''\fBA Guide to the Dungeons of Doom\fP' +\" .fo ''- % -'' +.eh 'USD:30-%''A Guide to the Dungeons of Doom' +.oh 'A Guide to the Dungeons of Doom''USD:30-%' +.sh 1 Introduction +.pp +You have just finished your years as a student at the local fighter's guild. +After much practice and sweat you have finally completed your training +and are ready to embark upon a perilous adventure. +As a test of your skills, +the local guildmasters have sent you into the Dungeons of Doom. +Your task is to return with the Amulet of Yendor. +Your reward for the completion of this task +will be a full membership in the local guild. +In addition, +you are allowed to keep all the loot you bring back from the dungeons. +.pp +In preparation for your journey, +you are given an enchanted mace, +a bow, and a quiver of arrows +taken from a dragon's hoard in the far off Dark Mountains. +You are also outfitted with elf-crafted armor +and given enough food to reach the dungeons. +You say goodbye to family and friends for what may be the last time +and head up the road. +.pp +You set out on your way to the dungeons +and after several days of uneventful travel, +you see the ancient ruins +that mark the entrance to the Dungeons of Doom. +It is late at night, +so you make camp at the entrance +and spend the night sleeping under the open skies. +In the morning you gather your weapons, +put on your armor, +eat what is almost your last food, +and enter the dungeons. +.sh 1 "What is going on here?" +.pp +You have just begun a game of rogue. +Your goal is to grab as much treasure as you can, +find the Amulet of Yendor, +and get out of the Dungeons of Doom alive. +On the screen, +a map of where you have been +and what you have seen on the current dungeon level is kept. +As you explore more of the level, +it appears on the screen in front of you. +.pp +Rogue differs from most computer fantasy games in that it is screen oriented. +Commands are all one or two keystrokes\** +.(f +\** As opposed to pseudo English sentences. +.)f +and the results of your commands +are displayed graphically on the screen rather +than being explained in words.\** +.(f +\** A minimum screen size of 24 lines by 80 columns is required. +If the screen is larger, only the 24x80 section will be used +for the map. +.)f +.pp +Another major difference between rogue and other computer fantasy games +is that once you have solved all the puzzles in a standard fantasy game, +it has lost most of its excitement and it ceases to be fun. +Rogue, +on the other hand, +generates a new dungeon every time you play it +and even the author finds it an entertaining and exciting game. +.sh 1 "What do all those things on the screen mean?" +.pp +In order to understand what is going on in rogue +you have to first get some grasp of what rogue is doing with the screen. +The rogue screen is intended +to replace the \*(lqYou can see ...\*(rq descriptions +of standard fantasy games. +Figure 1 is a sample of what a rogue screen might look like. +.(z +.hl +.nf +.TS +center; +ce0 ce0 ce0 ce0 ce0 ce0 ce0 ce0 ce0 ce0 ce0 ce. +- - - - - - - - - - - - +| . . . . . . . . . . + +| . . @ . . . . ] . . | +| . . . . B . . . . . | +| . . . . . . . . . . | +- - - - - + - - - - - - +.TE + + +.ce 1000 +Level: 1 Gold: 0 Hp: 12(12) Str: 16(16) Arm: 4 Exp: 1/0 + +Figure 1 +.ce +.hl +.)z +.sh 2 "The bottom line" +.pp +At the bottom line of the screen +are a few pieces of cryptic information +describing your current status. +Here is an explanation of what these things mean: +.ip Level \w'Level\ \ 'u +This number indicates how deep you have gone in the dungeon. +It starts at one and goes up as you go deeper into the dungeon. +.ip Gold \w'Level\ \ 'u +The number of gold pieces you have managed to find +and keep with you so far. +.ip Hp \w'Level\ \ 'u +Your current and maximum health points. +Health points indicate how much damage you can take before you die. +The more you get hit in a fight, +the lower they get. +You can regain health points by resting. +The number in parentheses +is the maximum number your health points can reach. +.ip Str \w'Level\ \ 'u +Your current strength and maximum ever strength. +This can be any integer less than or equal to 99, +or greater than or equal to 1. +The higher the number, +the stronger you are. +The number in the parentheses +is the maximum strength you have attained so far this game. +.ip Arm \w'Level\ \ 'u +Your current armor protection. +This number indicates how effective your armor is +in stopping blows from unfriendly creatures. +The higher this number is, +the more effective the armor. +.ip Exp \w'Level\ \ 'u +These two numbers give your current experience level +and experience points. +As you do things, +you gain experience points. +At certain experience point totals, +you gain an experience level. +The more experienced you are, +the better you are able to fight and to withstand magical attacks. +.sh 2 "The top line" +.pp +The top line of the screen is reserved +for printing messages that describe things +that are impossible to represent visually. +If you see a \*(lq--More--\*(rq on the top line, +this means that rogue wants to print another message on the screen, +but it wants to make certain +that you have read the one that is there first. +To read the next message, +just type a space. +.sh 2 "The rest of the screen" +.pp +The rest of the screen is the map of the level +as you have explored it so far. +Each symbol on the screen represents something. +Here is a list of what the various symbols mean: +.ip @ +This symbol represents you, the adventurer. +.ip "-\^|" +These symbols represent the walls of rooms. +.ip + +A door to/from a room. +.ip . +The floor of a room. +.ip # +The floor of a passage between rooms. +.ip * +A pile or pot of gold. +.ip ) +A weapon of some sort. +.ip ] +A piece of armor. +.ip ! +A flask containing a magic potion. +.ip ? +A piece of paper, usually a magic scroll. +.ip = +A ring with magic properties +.ip / +A magical staff or wand +.ip ^ +A trap, watch out for these. +.ip % +A staircase to other levels +.ip : +A piece of food. +.ip A-Z +The uppercase letters +represent the various inhabitants of the Dungeons of Doom. +Watch out, they can be nasty and vicious. +.sh 1 Commands +.pp +Commands are given to rogue by typing one or two characters. +Most commands can be preceded by a count to repeat them +(e.g. typing +.Cs 10s +will do ten searches). +Commands for which counts make no sense +have the count ignored. +To cancel a count or a prefix, +type \*E. +The list of commands is rather long, +but it can be read at any time during the game with the +.Cs ? +command. +Here it is for reference, +with a short explanation of each command. +.ip ? +The help command. +Asks for a character to give help on. +If you type a +.Cs * , +it will list all the commands, +otherwise it will explain what the character you typed does. +.ip / +This is the \*(lqWhat is that on the screen?\*(rq command. +A +.Cs / +followed by any character that you see on the level, +will tell you what that character is. +For instance, +typing +.Cs /@ +will tell you that the +.Cs @ +symbol represents you, the player. +.ip "h, H, ^H" +Move left. +You move one space to the left. +If you use upper case +.Cs h , +you will continue to move left until you run into something. +This works for all movement commands +(e.g. +.Cs L +means run in direction +.Cs l ) +If you use the \*(lqcontrol\*(rq +.Cs h , +you will continue moving in the specified direction +until you pass something interesting or run into a wall. +You should experiment with this, +since it is a very useful command, +but very difficult to describe. +This also works for all movement commands. +.ip j +Move down. +.ip k +Move up. +.ip l +Move right. +.ip y +Move diagonally up and left. +.ip u +Move diagonally up and right. +.ip b +Move diagonally down and left. +.ip n +Move diagonally down and right. +.ip t +Throw an object. +This is a prefix command. +When followed with a direction +it throws an object in the specified direction. +(e.g. type +.Cs th +to throw +something to the left.) +.ip f +Fight until someone dies. +When followed with a direction +this will force you to fight the creature in that direction +until either you or it bites the big one. +.ip m +Move onto something without picking it up. +This will move you one space in the direction you specify and, +if there is an object there you can pick up, +it won't do it. +.ip z +Zap prefix. +Point a staff or wand in a given direction +and fire it. +Even non-directional staves must be pointed in some direction +to be used. +.ip ^ +Identify trap command. +If a trap is on your map +and you can't remember what type it is, +you can get rogue to remind you +by getting next to it and typing +.Cs ^ +followed by the direction that would move you on top of it. +.ip s +Search for traps and secret doors. +Examine each space immediately adjacent to you +for the existence of a trap or secret door. +There is a large chance that even if there is something there, +you won't find it, +so you might have to search a while before you find something. +.ip > +Climb down a staircase to the next level. +Not surprisingly, this can only be done if you are standing on staircase. +.ip < +Climb up a staircase to the level above. +This can't be done without the Amulet of Yendor in your possession. +.ip "." +Rest. +This is the \*(lqdo nothing\*(rq command. +This is good for waiting and healing. +.ip , +Pick up something. +This picks up whatever you are currently standing on, +if you are standing on anything at all. +.ip i +Inventory. +List what you are carrying in your pack. +.ip I +Selective inventory. +Tells you what a single item in your pack is. +.ip q +Quaff one of the potions you are carrying. +.ip r +Read one of the scrolls in your pack. +.ip e +Eat food from your pack. +.ip w +Wield a weapon. +Take a weapon out of your pack and carry it for use in combat, +replacing the one you are currently using (if any). +.ip W +Wear armor. +You can only wear one suit of armor at a time. +This takes extra time. +.ip T +Take armor off. +You can't remove armor that is cursed. +This takes extra time. +.ip P +Put on a ring. +You can wear only two rings at a time +(one on each hand). +If you aren't wearing any rings, +this command will ask you which hand you want to wear it on, +otherwise, it will place it on the unused hand. +The program assumes that you wield your sword in your right hand. +.ip R +Remove a ring. +If you are only wearing one ring, +this command takes it off. +If you are wearing two, +it will ask you which one you wish to remove, +.ip d +Drop an object. +Take something out of your pack and leave it lying on the floor. +Only one object can occupy each space. +You cannot drop a cursed object at all +if you are wielding or wearing it. +.ip c +Call an object something. +If you have a type of object in your pack +which you wish to remember something about, +you can use the call command to give a name to that type of object. +This is usually used when you figure out what a +potion, scroll, ring, or staff is +after you pick it up but before it is truly identified. Each type of +scroll and potion will become identified after its first use. +.ip o +Examine and set options. +This command is further explained in the section on options. +.ip ^R +Redraws the screen. +Useful if spurious messages or transmission errors +have messed up the display. +.ip ^P +Print last message. +Useful when a message disappears before you can read it. +Consecutive repetitions of this command will reveal the last +five messages. +.ip \*E +Cancel a command, prefix, or count. +.ip ! +Escape to a shell for some commands. +.ip Q +Quit. +Leave the game. +.ip S +Save the current game in a file. +It will ask you whether you wish to use the default save file. +.i Caveat : +Rogue won't let you start up a copy of a saved game, +and it removes the save file as soon as you start up a restored game. +This is to prevent people from saving a game just before a dangerous position +and then restarting it if they die. +To restore a saved game, +give the file name as an argument to rogue. +As in +.ti +1i +.nf +% rogue \fIsave\*_file\fP +.ip v +Prints the program version number. +.ip ) +Print the weapon you are currently wielding +.ip ] +Print the armor you are currently wearing +.ip = +Print the rings you are currently wearing +.sh 1 Rooms +.pp +Rooms in the dungeons are lit as you enter them. +Upon leaving a room, +all monsters inside the room are erased from the screen. +In the darkness of a corridor, you can only see one space +in all directions around you. +.sh 1 Fighting +.pp +If you see a monster and you wish to fight it, +just attempt to run into it. +Many times a monster you find will mind its own business +unless you attack it. +It is often the case that discretion is the better part of valor. +.sh 1 "Objects you can find" +.pp +When you find something in the dungeon, +it is common to want to pick the object up. +This is accomplished in rogue by walking over the object +(unless you use the +.Cs m +prefix, see above). +If you are carrying too many things, +the program will tell you and it won't pick up the object, +otherwise it will add it to your pack +and tell you what you just picked up. +.pp +Many of the commands that operate on objects must prompt you +to find out which object you want to use. +If you change your mind and don't want to do that command after all, +just type an \*E and the command will be aborted. +.pp +Some objects, like armor and weapons, +are easily differentiated. +Others, like scrolls and potions, +are given labels which vary according to type. +During a game, +any two of the same kind of object +with the same label +are the same type. +However, +the labels will vary from game to game. +.pp +When you use one of these labeled objects, +if its effect may be obvious. Potions or scrolls will +become identified at this point, but not other items. +You may want to call these other items something +so you will recognize it later, +you can use the +.Cs call +command +(see above). +.sh 2 Weapons +.pp +Some weapons, +like arrows, +come in bunches, +but most come one at a time. +In order to use a weapon, +you must wield it. +To fire an arrow out of a bow, +you must first wield the bow, +then throw the arrow. +You can only wield one weapon at a time, +but you can't change weapons if the one +you are currently wielding is cursed. +The commands to use weapons are +.Cs w +(wield) +and +.Cs t +(throw). +.sh 2 Armor +.pp +There are various sorts of armor lying around in the dungeon. +Some of it is enchanted, +some is cursed, +and some is just normal. +Different armor types have different armor protection. +The higher the armor protection, +the more protection the armor affords against the blows of monsters. +Here is a list of the various armor types and their normal armor protection: +.(b +.TS +box center; +l r. +\ \ \fIType Protection\fP +None 0 +Leather armor 2 +Studded leather / Ring mail 3 +Scale mail 4 +Chain mail 5 +Banded mail / Splint mail 6 +Plate mail 7 +.TE +.)b +.lp +If a piece of armor is enchanted, +its armor protection will be higher than normal. +If a suit of armor is cursed, +its armor protection will be lower, +and you will not be able to remove it. +However, not all armor with a protection that is lower than normal is cursed. +.pp +The commands to use weapons are +.Cs W +(wear) +and +.Cs T +(take off). +.sh 2 Scrolls +.pp +Scrolls come with titles in an unknown tongue\**. +.(f +\** Actually, it's a dialect spoken only by the twenty-seven members +of a tribe in Outer Mongolia, +but you're not supposed to +.i know +that. +.)f +After you read a scroll, +it disappears from your pack. +The command to use a scroll is +.Cs r +(read). +.sh 2 Potions +.pp +Potions are labeled by the color of the liquid inside the flask. +They disappear after being quaffed. +The command to quaff a potion is +.Cs q +(quaff). +.sh 2 "Staves and Wands" +.pp +Staves and wands do the same kinds of things. +Staves are identified by a type of wood; +wands by a type of metal or bone. +They are generally things you want to do to something +over a long distance, +so you must point them at what you wish to affect +to use them. +Some staves are not affected by the direction they are pointed, though. +Staves come with multiple magic charges, +the number being random, +and when they are used up, +the staff is just a piece of wood or metal. +.pp +The command to use a wand or staff is +.Cs z +(zap) +.sh 2 Rings +.pp +Rings are very useful items, +since they are relatively permanent magic, +unlike the usually fleeting effects of potions, scrolls, and staves. +Of course, +the bad rings are also more powerful. +Most rings also cause you to use up food more rapidly, +the rate varying with the type of ring. +Rings are differentiated by their stone settings. +The commands to use rings are +.Cs P +(put on) +and +.Cs R +(remove). +.sh 2 Food +.pp +Food is necessary to keep you going. +If you go too long without eating you will faint, +and eventually die of starvation. +The command to use food is +.Cs e +(eat). +.sh 1 Options +.pp +Due to variations in personal tastes +and conceptions of the way rogue should do things, +there are a set of options you can set +that cause rogue to behave in various different ways. +.ne 1i +.sh 2 "Setting the options" +.pp +There are two ways to set the options. +The first is with the +.Cs o +command of rogue; +the second is with the +.Cs ROGUEOPTS +environment variable\**. +.(f +\** On Version 6 systems, +there is no equivalent of the ROGUEOPTS feature. +.br +.)f +.br +.sh 3 "Using the `o' command" +.pp +When you type +.Cs o +in rogue, +it clears the screen +and displays the current settings for all the options. +It then places the cursor by the value of the first option +and waits for you to type. +You can type a \*R +which means to go to the next option, +a +.Cs \- +which means to go to the previous option, +an \*E +which means to return to the game, +or you can give the option a value. +For boolean options this merely involves typing +.Cs t +for true or +.Cs f +for false. +For string options, +type the new value followed by a \*R. +.sh 3 "Using the ROGUEOPTS variable" +.pp +The ROGUEOPTS variable is a string +containing a comma separated list of initial values +for the various options. +Boolean variables can be turned on by listing their name +or turned off by putting a +.Cs no +in front of the name. +Thus to set up an environment variable so that +.b jump +is on, +.b passgo +is off, +and the +.b name +is set to \*(lqBlue Meanie\*(rq, +use the command +.nf +.ti +3n +% setenv ROGUEOPTS "jump,nopassgo,name=Blue Meanie"\** +.fi +.(f +\** +For those of you who use the Bourne shell sh (1), the commands would be +.in +3 +.nf +$ ROGUEOPTS="jump,nopassgo,name=Blue Meanie" +$ export ROGUEOPTS +.fi +.in +0 +.)f +.sh 2 "Option list" +.pp +Here is a list of the options +and an explanation of what each one is for. +The default value for each is enclosed in square brackets. +For character string options, +input over forty characters will be ignored. +.ip "\fBjump\fP [\fI\^nojump\^\fP]" +If this option is set, +running moves will not be displayed +until you reach the end of the move. +This saves considerable CPU and display time. +This option defaults to +.i jump +if you are using a slow terminal. +.ip "\fBpassgo\fP [\fI\^nopassgo\^\fP]" +Follow turnings in passageways. +If you run in a passage +and you run into stone or a wall, +rogue will see if it can turn to the right or left. +If it can only turn one way, +it will turn that way. +If it can turn either or neither, +it will stop. +This algorithm can sometimes lead to slightly confusing occurrences +which is why it defaults to \fInopassgo\fP. +.ip "\fBskull\fP [\fI\^skull\^\fP]" +Print out the skull at the end if you get killed. +This is nice but slow, so you can turn it off if you like. +.ip "\fBname\fP [account name]" +This is the name of your character. +It is used if you get on the top ten scorer's list. +.ip "\fBfruit\fP [\fI\^slime-mold\^\fP]" +This should hold the name of a fruit that you enjoy eating. +It is basically a whimsey that rogue uses in a couple of places. +.ip "\fBfile\fP [\fI\^~/rogue.save\^\fP]" +The default file name for saving the game. +If your phone is hung up by accident, +rogue will automatically save the game in this file. +The file name may start with the special character +.Cs ~ +which expands to be your home directory. +.sh 1 Scoring +.pp +Rogue maintains a list +of the top scoring people or scores on your machine. +If you score higher than someone else on this list, +or better your previous score on the list, +you will be inserted in the proper place +under your current name. +.pp +If you quit the game, you get out with all of your gold intact. +If, however, you get killed in the Dungeons of Doom, +your body is forwarded to your next-of-kin, +along with 90% of your gold; +ten percent of your gold is kept by the Dungeons' wizard as a fee\**. +.(f +\** The Dungeon's wizard is named Wally the Wonder Badger. +Invocations should be accompanied by a sizable donation. +.)f +This should make you consider whether you want to take one last hit +at that monster and possibly live, +or quit and thus stop with whatever you have. +If you quit, you do get all your gold, +but if you swing and live, you might find more. +.pp +If you just want to see what the current top players/games list is, +you can type +.ti +1i +.nf +% rogue \-s +.br +.sh 1 Acknowledgements +.pp +Rogue was originally conceived of by Glenn Wichman and Michael Toy. +Ken Arnold and Michael Toy then smoothed out the user interface, +and added jillions of new features. +We would like to thank +Bob Arnold, +Michelle Busch, +Andy Hatcher, +Kipp Hickman, +Mark Horton, +Daniel Jensen, +Bill Joy, +Joe Kalash, +Steve Maurer, +Marty McNary, +Jan Miller, +and +Scott Nelson +for their ideas and assistance; +and also the teeming multitudes +who graciously ignored work, school, and social life to play rogue +and send us bugs, complaints, suggestions, and just plain flames. +And also Mom. +.pp +The public domain version of rogue now distributed with Berkeley UNIX +was written by Timothy Stoehr. diff --git a/rogue/hit.c b/rogue/hit.c new file mode 100644 index 0000000..9d27c1f --- /dev/null +++ b/rogue/hit.c @@ -0,0 +1,466 @@ +/* $NetBSD: hit.c,v 1.10 2008/01/14 03:50:01 dholland Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Timothy C. Stoehr. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)hit.c 8.1 (Berkeley) 5/31/93"; +#else +__RCSID("$NetBSD: hit.c,v 1.10 2008/01/14 03:50:01 dholland Exp $"); +#endif +#endif /* not lint */ + +/* + * hit.c + * + * This source herein may be modified and/or distributed by anybody who + * so desires, with the following restrictions: + * 1.) No portion of this notice shall be removed. + * 2.) Credit shall not be taken for the creation of this source. + * 3.) This code is not to be traded, sold, or used for personal + * gain or profit. + * + */ + +#include "rogue.h" + +static int damage_for_strength(void); +static int get_w_damage(const object *); +static int to_hit(const object *); + +static object *fight_monster = NULL; +char hit_message[HIT_MESSAGE_SIZE] = ""; + +void +mon_hit(object *monster) +{ + short damage, hit_chance; + const char *mn; + float minus; + + if (fight_monster && (monster != fight_monster)) { + fight_monster = 0; + } + monster->trow = NO_ROOM; + if (cur_level >= (AMULET_LEVEL * 2)) { + hit_chance = 100; + } else { + hit_chance = monster->m_hit_chance; + hit_chance -= (((2 * rogue.exp) + (2 * ring_exp)) - r_rings); + } + if (wizard) { + hit_chance /= 2; + } + if (!fight_monster) { + interrupted = 1; + } + mn = mon_name(monster); + + if (!rand_percent(hit_chance)) { + if (!fight_monster) { + messagef(1, "%sthe %s misses", hit_message, mn); + hit_message[0] = 0; + } + return; + } + if (!fight_monster) { + messagef(1, "%sthe %s hit", hit_message, mn); + hit_message[0] = 0; + } + if (!(monster->m_flags & STATIONARY)) { + damage = get_damage(monster->m_damage, 1); + if (cur_level >= (AMULET_LEVEL * 2)) { + minus = (float)((AMULET_LEVEL * 2) - cur_level); + } else { + minus = (float)get_armor_class(rogue.armor) * 3.00; + minus = minus/100.00 * (float)damage; + } + damage -= (short)minus; + } else { + damage = monster->stationary_damage++; + } + if (wizard) { + damage /= 3; + } + if (damage > 0) { + rogue_damage(damage, monster, 0); + } + if (monster->m_flags & SPECIAL_HIT) { + special_hit(monster); + } +} + +void +rogue_hit(object *monster, boolean force_hit) +{ + short damage, hit_chance; + + if (monster) { + if (check_imitator(monster)) { + return; + } + hit_chance = force_hit ? 100 : get_hit_chance(rogue.weapon); + + if (wizard) { + hit_chance *= 2; + } + if (!rand_percent(hit_chance)) { + if (!fight_monster) { + (void)strlcpy(hit_message, "you miss ", + sizeof(hit_message)); + } + goto RET; + } + damage = get_weapon_damage(rogue.weapon); + if (wizard) { + damage *= 3; + } + if (con_mon) { + s_con_mon(monster); + } + if (mon_damage(monster, damage)) { /* still alive? */ + if (!fight_monster) { + (void)strlcpy(hit_message, "you hit ", + sizeof(hit_message)); + } + } +RET: check_gold_seeker(monster); + wake_up(monster); + } +} + +void +rogue_damage(short d, object *monster, short other) +{ + if (d >= rogue.hp_current) { + rogue.hp_current = 0; + print_stats(STAT_HP); + killed_by(monster, other); + } + if (d > 0) { + rogue.hp_current -= d; + print_stats(STAT_HP); + } +} + +int +get_damage(const char *ds, boolean r) +{ + int i = 0, j, n, d, total = 0; + + while (ds[i]) { + n = get_number(ds+i); + while ((ds[i] != 'd') && ds[i]) { + i++; + } + if (ds[i] == 'd') { + i++; + } + + d = get_number(ds+i); + while ((ds[i] != '/') && ds[i]) { + i++; + } + if (ds[i] == '/') { + i++; + } + + for (j = 0; j < n; j++) { + if (r) { + total += get_rand(1, d); + } else { + total += d; + } + } + } + return(total); +} + +static int +get_w_damage(const object *obj) +{ + char new_damage[32]; + int tmp_to_hit, tmp_damage; + int i = 0; + + if ((!obj) || (obj->what_is != WEAPON)) { + return(-1); + } + tmp_to_hit = get_number(obj->damage) + obj->hit_enchant; + while ((obj->damage[i] != 'd') && obj->damage[i]) { + i++; + } + if (obj->damage[i] == 'd') { + i++; + } + tmp_damage = get_number(obj->damage + i) + obj->d_enchant; + + snprintf(new_damage, sizeof(new_damage), "%dd%d", + tmp_to_hit, tmp_damage); + + return(get_damage(new_damage, 1)); +} + +int +get_number(const char *s) +{ + int i = 0; + int total = 0; + + while ((s[i] >= '0') && (s[i] <= '9')) { + total = (10 * total) + (s[i] - '0'); + i++; + } + return(total); +} + +long +lget_number(const char *s) +{ + short i = 0; + long total = 0; + + while ((s[i] >= '0') && (s[i] <= '9')) { + total = (10 * total) + (s[i] - '0'); + i++; + } + return(total); +} + +static int +to_hit(const object *obj) +{ + if (!obj) { + return(1); + } + return(get_number(obj->damage) + obj->hit_enchant); +} + +static int +damage_for_strength(void) +{ + short strength; + + strength = rogue.str_current + add_strength; + + if (strength <= 6) { + return(strength-5); + } + if (strength <= 14) { + return(1); + } + if (strength <= 17) { + return(3); + } + if (strength <= 18) { + return(4); + } + if (strength <= 20) { + return(5); + } + if (strength <= 21) { + return(6); + } + if (strength <= 30) { + return(7); + } + return(8); +} + +int +mon_damage(object *monster, short damage) +{ + const char *mn; + short row, col; + + monster->hp_to_kill -= damage; + + if (monster->hp_to_kill <= 0) { + row = monster->row; + col = monster->col; + dungeon[row][col] &= ~MONSTER; + mvaddch(row, col, get_dungeon_char(row, col)); + + fight_monster = 0; + cough_up(monster); + mn = mon_name(monster); + messagef(1, "%sdefeated the %s", hit_message, mn); + hit_message[0] = 0; + add_exp(monster->kill_exp, 1); + take_from_pack(monster, &level_monsters); + + if (monster->m_flags & HOLDS) { + being_held = 0; + } + free_object(monster); + return(0); + } + return(1); +} + +void +fight(boolean to_the_death) +{ + short ch, c, d; + short row, col; + boolean first_miss = 1; + short possible_damage; + object *monster; + + ch = 0; + while (!is_direction(ch = rgetchar(), &d)) { + sound_bell(); + if (first_miss) { + messagef(0, "direction?"); + first_miss = 0; + } + } + check_message(); + if (ch == CANCEL) { + return; + } + row = rogue.row; col = rogue.col; + get_dir_rc(d, &row, &col, 0); + + c = mvinch(row, col); + if (((c < 'A') || (c > 'Z')) || + (!can_move(rogue.row, rogue.col, row, col))) { + messagef(0, "I see no monster there"); + return; + } + if (!(fight_monster = object_at(&level_monsters, row, col))) { + return; + } + if (!(fight_monster->m_flags & STATIONARY)) { + possible_damage = ((get_damage(fight_monster->m_damage, 0) * 2) / 3); + } else { + possible_damage = fight_monster->stationary_damage - 1; + } + while (fight_monster) { + (void)one_move_rogue(ch, 0); + if (((!to_the_death) && (rogue.hp_current <= possible_damage)) || + interrupted || (!(dungeon[row][col] & MONSTER))) { + fight_monster = 0; + } else { + monster = object_at(&level_monsters, row, col); + if (monster != fight_monster) { + fight_monster = 0; + } + } + } +} + +void +get_dir_rc(short dir, short *row, short *col, short allow_off_screen) +{ + switch(dir) { + case LEFT: + if (allow_off_screen || (*col > 0)) { + (*col)--; + } + break; + case DOWN: + if (allow_off_screen || (*row < (DROWS-2))) { + (*row)++; + } + break; + case UPWARD: + if (allow_off_screen || (*row > MIN_ROW)) { + (*row)--; + } + break; + case RIGHT: + if (allow_off_screen || (*col < (DCOLS-1))) { + (*col)++; + } + break; + case UPLEFT: + if (allow_off_screen || ((*row > MIN_ROW) && (*col > 0))) { + (*row)--; + (*col)--; + } + break; + case UPRIGHT: + if (allow_off_screen || ((*row > MIN_ROW) && (*col < (DCOLS-1)))) { + (*row)--; + (*col)++; + } + break; + case DOWNRIGHT: + if (allow_off_screen || ((*row < (DROWS-2)) && (*col < (DCOLS-1)))) { + (*row)++; + (*col)++; + } + break; + case DOWNLEFT: + if (allow_off_screen || ((*row < (DROWS-2)) && (*col > 0))) { + (*row)++; + (*col)--; + } + break; + } +} + +int +get_hit_chance(const object *weapon) +{ + short hit_chance; + + hit_chance = 40; + hit_chance += 3 * to_hit(weapon); + hit_chance += (((2 * rogue.exp) + (2 * ring_exp)) - r_rings); + return(hit_chance); +} + +int +get_weapon_damage(const object *weapon) +{ + short damage; + + damage = get_w_damage(weapon); + damage += damage_for_strength(); + damage += ((((rogue.exp + ring_exp) - r_rings) + 1) / 2); + return(damage); +} + +void +s_con_mon(object *monster) +{ + if (con_mon) { + monster->m_flags |= CONFUSED; + monster->moves_confused += get_rand(12, 22); + messagef(0, "the monster appears confused"); + con_mon = 0; + } +} diff --git a/rogue/init.c b/rogue/init.c new file mode 100644 index 0000000..04ccbb7 --- /dev/null +++ b/rogue/init.c @@ -0,0 +1,366 @@ +/* $NetBSD: init.c,v 1.18 2008/08/08 16:10:47 drochner Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Timothy C. Stoehr. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)init.c 8.1 (Berkeley) 5/31/93"; +#else +__RCSID("$NetBSD: init.c,v 1.18 2008/08/08 16:10:47 drochner Exp $"); +#endif +#endif /* not lint */ + +/* + * init.c + * + * This source herein may be modified and/or distributed by anybody who + * so desires, with the following restrictions: + * 1.) No portion of this notice shall be removed. + * 2.) Credit shall not be taken for the creation of this source. + * 3.) This code is not to be traded, sold, or used for personal + * gain or profit. + * + */ + +#include <stdlib.h> +#include <fcntl.h> + +#include "rogue.h" + +static void do_args(int, char **); +static void do_opts(void); +static void env_get_value(char **, char *, boolean); +static void init_str(char **, const char *); +static void player_init(void); + +static char *rest_file = NULL; +static boolean init_curses = 0; + +char login_name[MAX_OPT_LEN]; +char *nick_name = NULL; +boolean cant_int = 0; +boolean did_int = 0; +boolean score_only; +boolean save_is_interactive = 1; +boolean ask_quit = 1; +boolean no_skull = 0; +boolean passgo = 0; +const char *error_file = "rogue.esave"; +const char *byebye_string = "Okay, bye bye!"; +gid_t gid, egid; + +int +init(int argc, char *argv[]) +{ + const char *pn; + int seed; + int fd; + + gid = getgid(); + egid = getegid(); + setegid(gid); + /* Check for dirty tricks with closed fds 0, 1, 2 */ + fd = open("/dev/null", O_RDONLY); + if (fd < 3) + exit(1); + close(fd); + + seed = 0; + pn = md_gln(); + if ((!pn) || (strlen(pn) >= MAX_OPT_LEN)) { + clean_up("Hey! Who are you?"); + } + /* LOGIN_NAME_SIZE == MAX_OPT_LEN now, but just in case... */ + (void)strlcpy(login_name, pn, sizeof(login_name)); + + do_args(argc, argv); + do_opts(); + + if (!score_only && !rest_file) { + printf("Hello %s, just a moment while I dig the dungeon...", + nick_name); + fflush(stdout); + } + + if (!initscr()) { + fprintf(stderr, "couldn't initialize screen\n"); + exit (0); + } + if ((LINES < DROWS) || (COLS < DCOLS)) { + clean_up("must be played on at least 80 x 24 screen"); + } + start_window(); + init_curses = 1; + + md_heed_signals(); + + if (score_only) { + put_scores(NULL, 0); + } + seed = md_gseed(); + (void)srrandom(seed); + if (rest_file) { + restore(rest_file); + return(1); + } + mix_colors(); + get_wand_and_ring_materials(); + make_scroll_titles(); + + level_objects.next_object = NULL; + level_monsters.next_monster = NULL; + player_init(); + ring_stats(0); + return(0); +} + +static void +player_init(void) +{ + object *obj; + + rogue.pack.next_object = NULL; + + obj = alloc_object(); + get_food(obj, 1); + (void)add_to_pack(obj, &rogue.pack, 1); + + obj = alloc_object(); /* initial armor */ + obj->what_is = ARMOR; + obj->which_kind = RINGMAIL; + obj->class = RINGMAIL+2; + obj->is_protected = 0; + obj->d_enchant = 1; + (void)add_to_pack(obj, &rogue.pack, 1); + do_wear(obj); + + obj = alloc_object(); /* initial weapons */ + obj->what_is = WEAPON; + obj->which_kind = MACE; + obj->damage = "2d3"; + obj->hit_enchant = obj->d_enchant = 1; + obj->identified = 1; + (void)add_to_pack(obj, &rogue.pack, 1); + do_wield(obj); + + obj = alloc_object(); + obj->what_is = WEAPON; + obj->which_kind = BOW; + obj->damage = "1d2"; + obj->hit_enchant = 1; + obj->d_enchant = 0; + obj->identified = 1; + (void)add_to_pack(obj, &rogue.pack, 1); + + obj = alloc_object(); + obj->what_is = WEAPON; + obj->which_kind = ARROW; + obj->quantity = get_rand(25, 35); + obj->damage = "1d2"; + obj->hit_enchant = 0; + obj->d_enchant = 0; + obj->identified = 1; + (void)add_to_pack(obj, &rogue.pack, 1); +} + +void +clean_up(const char *estr) +{ + if (save_is_interactive) { + if (init_curses) { + move(DROWS-1, 0); + refresh(); + stop_window(); + } + printf("\n%s\n", estr); + } + md_exit(0); +} + +void +start_window(void) +{ + cbreak(); + noecho(); +#ifndef BAD_NONL + nonl(); +#endif +} + +void +stop_window(void) +{ + endwin(); +} + +void +byebye(int dummy __attribute__((unused))) +{ + md_ignore_signals(); + if (ask_quit) { + quit(1); + } else { + clean_up(byebye_string); + } + md_heed_signals(); +} + +void +onintr(int dummy __attribute__((unused))) +{ + md_ignore_signals(); + if (cant_int) { + did_int = 1; + } else { + check_message(); + messagef(1, "interrupt"); + } + md_heed_signals(); +} + +void +error_save(int dummy __attribute__((unused))) +{ + save_is_interactive = 0; + save_into_file(error_file); + clean_up(""); +} + +static void +do_args(int argc, char *argv[]) +{ + int i, j; + + for (i = 1; i < argc; i++) { + if (argv[i][0] == '-') { + for (j = 1; argv[i][j]; j++) { + switch(argv[i][j]) { + case 's': + score_only = 1; + break; + } + } + } else { + rest_file = argv[i]; + } + } +} + +static void +do_opts(void) +{ + char *eptr; + + if ((eptr = md_getenv("ROGUEOPTS")) != NULL) { + for (;;) { + while ((*eptr) == ' ') { + eptr++; + } + if (!(*eptr)) { + break; + } + if (!strncmp(eptr, "fruit=", 6)) { + eptr += 6; + env_get_value(&fruit, eptr, 1); + } else if (!strncmp(eptr, "file=", 5)) { + eptr += 5; + env_get_value(&save_file, eptr, 0); + } else if (!strncmp(eptr, "jump", 4)) { + jump = 1; + } else if (!strncmp(eptr, "name=", 5)) { + eptr += 5; + env_get_value(&nick_name, eptr, 0); + } else if (!strncmp(eptr, "noaskquit", 9)) { + ask_quit = 0; + } else if (!strncmp(eptr, "noskull", 5) || + !strncmp(eptr,"notomb", 6)) { + no_skull = 1; + } else if (!strncmp(eptr, "passgo", 5)) { + passgo = 1; + } + while ((*eptr) && (*eptr != ',')) { + eptr++; + } + if (!(*(eptr++))) { + break; + } + } + } + /* If some strings have not been set through ROGUEOPTS, assign defaults + * to them so that the options editor has data to work with. + */ + init_str(&nick_name, login_name); + init_str(&save_file, "rogue.save"); + init_str(&fruit, "slime-mold"); +} + +static void +env_get_value(char **s, char *e, boolean add_blank) +{ + short i = 0; + const char *t; + + t = e; + + while ((*e) && (*e != ',')) { + if (*e == ':') { + *e = ';'; /* ':' reserved for score file purposes */ + } + e++; + if (++i >= MAX_OPT_LEN) { + break; + } + } + /* note: edit_opts() in room.c depends on this being the right size */ + *s = md_malloc(MAX_OPT_LEN + 2); + if (*s == NULL) + clean_up("out of memory"); + (void)strncpy(*s, t, i); + if (add_blank) { + (*s)[i++] = ' '; + } + (*s)[i] = '\0'; +} + +static void +init_str(char **str, const char *dflt) +{ + if (!(*str)) { + /* note: edit_opts() in room.c depends on this size */ + *str = md_malloc(MAX_OPT_LEN + 2); + if (*str == NULL) + clean_up("out of memory"); + (void)strlcpy(*str, dflt, MAX_OPT_LEN + 2); + } +} diff --git a/rogue/inventory.c b/rogue/inventory.c new file mode 100644 index 0000000..934e8aa --- /dev/null +++ b/rogue/inventory.c @@ -0,0 +1,841 @@ +/* $NetBSD: inventory.c,v 1.15 2011/08/26 06:18:17 dholland Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Timothy C. Stoehr. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)inventory.c 8.1 (Berkeley) 5/31/93"; +#else +__RCSID("$NetBSD: inventory.c,v 1.15 2011/08/26 06:18:17 dholland Exp $"); +#endif +#endif /* not lint */ + +/* + * inventory.c + * + * This source herein may be modified and/or distributed by anybody who + * so desires, with the following restrictions: + * 1.) No portion of this notice shall be removed. + * 2.) Credit shall not be taken for the creation of this source. + * 3.) This code is not to be traded, sold, or used for personal + * gain or profit. + * + */ + +#include <stdarg.h> +#include "rogue.h" + +boolean is_wood[WANDS]; +const char *press_space = " --press space to continue--"; + +static const char *const wand_materials[WAND_MATERIALS] = { + "steel ", + "bronze ", + "gold ", + "silver ", + "copper ", + "nickel ", + "cobalt ", + "tin ", + "iron ", + "magnesium ", + "chrome ", + "carbon ", + "platinum ", + "silicon ", + "titanium ", + + "teak ", + "oak ", + "cherry ", + "birch ", + "pine ", + "cedar ", + "redwood ", + "balsa ", + "ivory ", + "walnut ", + "maple ", + "mahogany ", + "elm ", + "palm ", + "wooden " +}; + +static const char *const gems[GEMS] = { + "diamond ", + "stibotantalite ", + "lapi-lazuli ", + "ruby ", + "emerald ", + "sapphire ", + "amethyst ", + "quartz ", + "tiger-eye ", + "opal ", + "agate ", + "turquoise ", + "pearl ", + "garnet " +}; + +static const char *const syllables[MAXSYLLABLES] = { + "blech ", + "foo ", + "barf ", + "rech ", + "bar ", + "blech ", + "quo ", + "bloto ", + "oh ", + "caca ", + "blorp ", + "erp ", + "festr ", + "rot ", + "slie ", + "snorf ", + "iky ", + "yuky ", + "ooze ", + "ah ", + "bahl ", + "zep ", + "druhl ", + "flem ", + "behil ", + "arek ", + "mep ", + "zihr ", + "grit ", + "kona ", + "kini ", + "ichi ", + "tims ", + "ogr ", + "oo ", + "ighr ", + "coph ", + "swerr ", + "mihln ", + "poxi " +}; + +#define COMS 48 + +struct id_com_s { + short com_char; + const char *com_desc; +}; + +static const struct id_com_s com_id_tab[COMS] = { + {'?', "? prints help"}, + {'r', "r read scroll"}, + {'/', "/ identify object"}, + {'e', "e eat food"}, + {'h', "h left "}, + {'w', "w wield a weapon"}, + {'j', "j down"}, + {'W', "W wear armor"}, + {'k', "k up"}, + {'T', "T take armor off"}, + {'l', "l right"}, + {'P', "P put on ring"}, + {'y', "y up & left"}, + {'R', "R remove ring"}, + {'u', "u up & right"}, + {'d', "d drop object"}, + {'b', "b down & left"}, + {'c', "c call object"}, + {'n', "n down & right"}, + {'\0', "<SHIFT><dir>: run that way"}, + {')', ") print current weapon"}, + {'\0', "<CTRL><dir>: run till adjacent"}, + {']', "] print current armor"}, + {'f', "f<dir> fight till death or near death"}, + {'=', "= print current rings"}, + {'t', "t<dir> throw something"}, + {'\001', "^A print Hp-raise average"}, + {'m', "m<dir> move onto without picking up"}, + {'z', "z<dir> zap a wand in a direction"}, + {'o', "o examine/set options"}, + {'^', "^<dir> identify trap type"}, + {'\022', "^R redraw screen"}, + {'&', "& save screen into 'rogue.screen'"}, + {'s', "s search for trap/secret door"}, + {'\020', "^P repeat last message"}, + {'>', "> go down a staircase"}, + {'\033', "^[ cancel command"}, + {'<', "< go up a staircase"}, + {'S', "S save game"}, + {'.', ". rest for a turn"}, + {'Q', "Q quit"}, + {',', ", pick something up"}, + {'!', "! shell escape"}, + {'i', "i inventory"}, + {'F', "F<dir> fight till either of you dies"}, + {'I', "I inventory single item"}, + {'v', "v print version number"}, + {'q', "q quaff potion" } +}; + +static int get_com_id(int *, short); +static int pr_com_id(int); +static int pr_motion_char(int); + +void +inventory(const object *pack, unsigned short mask) +{ + object *obj; + short i = 0, j; + size_t maxlen = 0, n; + short row, col; + + struct { + short letter; + short sepchar; + char desc[DCOLS]; + char savebuf[DCOLS+8]; + } descs[MAX_PACK_COUNT+1]; + + + obj = pack->next_object; + + if (!obj) { + messagef(0, "your pack is empty"); + return; + } + while (obj) { + if (obj->what_is & mask) { + descs[i].letter = obj->ichar; + descs[i].sepchar = ((obj->what_is & ARMOR) && obj->is_protected) + ? '}' : ')'; + get_desc(obj, descs[i].desc, sizeof(descs[i].desc)); + n = strlen(descs[i].desc) + 4; + if (n > maxlen) { + maxlen = n; + } + i++; + /*assert(i<=MAX_PACK_COUNT);*/ + } + obj = obj->next_object; + } + if (maxlen < 27) maxlen = 27; + if (maxlen > DCOLS-2) maxlen = DCOLS-2; + col = DCOLS - (maxlen + 2); + + for (row = 0; ((row <= i) && (row < DROWS)); row++) { + for (j = col; j < DCOLS; j++) { + descs[row].savebuf[j-col] = mvinch(row, j); + } + descs[row].savebuf[j-col] = 0; + if (row < i) { + mvprintw(row, col, " %c%c %s", + descs[row].letter, descs[row].sepchar, + descs[row].desc); + } + else { + mvaddstr(row, col, press_space); + } + clrtoeol(); + } + refresh(); + wait_for_ack(); + + move(0, 0); + clrtoeol(); + + for (j = 1; ((j <= i) && (j < DROWS)); j++) { + mvaddstr(j, col, descs[j].savebuf); + } +} + +void +id_com(void) +{ + int ch = 0; + short i, j, k; + + while (ch != CANCEL) { + check_message(); + messagef(0, "Character you want help for (* for all):"); + + refresh(); + ch = getchar(); + + switch(ch) { + case LIST: + { + char save[(((COMS / 2) + (COMS % 2)) + 1)][DCOLS]; + short rows = (((COMS / 2) + (COMS % 2)) + 1); + boolean need_two_screens = FALSE; + + if (rows > LINES) { + need_two_screens = 1; + rows = LINES; + } + k = 0; + + for (i = 0; i < rows; i++) { + for (j = 0; j < DCOLS; j++) { + save[i][j] = mvinch(i, j); + } + } +MORE: + for (i = 0; i < rows; i++) { + move(i, 0); + clrtoeol(); + } + for (i = 0; i < (rows-1); i++) { + if (i < (LINES-1)) { + if (((i + i) < COMS) && ((i+i+k) < COMS)) { + mvaddstr(i, 0, com_id_tab[i+i+k].com_desc); + } + if (((i + i + 1) < COMS) && ((i+i+k+1) < COMS)) { + mvaddstr(i, (DCOLS/2), + com_id_tab[i+i+k+1].com_desc); + } + } + } + mvaddstr(rows - 1, 0, need_two_screens ? more : press_space); + refresh(); + wait_for_ack(); + + if (need_two_screens) { + k += ((rows-1) * 2); + need_two_screens = 0; + goto MORE; + } + for (i = 0; i < rows; i++) { + move(i, 0); + for (j = 0; j < DCOLS; j++) { + addch(save[i][j]); + } + } + } + break; + default: + if (!pr_com_id(ch)) { + if (!pr_motion_char(ch)) { + check_message(); + messagef(0, "unknown character"); + } + } + ch = CANCEL; + break; + } + } +} + +static int +pr_com_id(int ch) +{ + int i; + + if (!get_com_id(&i, ch)) { + return(0); + } + check_message(); + messagef(0, "%s", com_id_tab[i].com_desc); + return(1); +} + +static int +get_com_id(int *indexp, short ch) +{ + short i; + + for (i = 0; i < COMS; i++) { + if (com_id_tab[i].com_char == ch) { + *indexp = i; + return(1); + } + } + return(0); +} + +static int +pr_motion_char(int ch) +{ + if ( (ch == 'J') || + (ch == 'K') || + (ch == 'L') || + (ch == 'H') || + (ch == 'Y') || + (ch == 'U') || + (ch == 'N') || + (ch == 'B') || + (ch == '\012') || + (ch == '\013') || + (ch == '\010') || + (ch == '\014') || + (ch == '\025') || + (ch == '\031') || + (ch == '\016') || + (ch == '\002')) { + const char *until; + int n = 0; /* XXX: GCC */ + if (ch <= '\031') { + ch += 96; + until = " until adjacent"; + } else { + ch += 32; + until = ""; + } + (void)get_com_id(&n, ch); + check_message(); + messagef(0, "run %s%s", com_id_tab[n].com_desc + 8, until); + return(1); + } else { + return(0); + } +} + +void +mix_colors(void) +{ + short i, j, k; + char t[MAX_ID_TITLE_LEN]; + + for (i = 0; i <= 32; i++) { + j = get_rand(0, (POTIONS - 1)); + k = get_rand(0, (POTIONS - 1)); + strlcpy(t, id_potions[j].title, sizeof(t)); + strlcpy(id_potions[j].title, id_potions[k].title, + sizeof(id_potions[j].title)); + strlcpy(id_potions[k].title, t, sizeof(id_potions[k].title)); + } +} + +void +make_scroll_titles(void) +{ + short i, j, n; + short sylls, s; + size_t maxlen = sizeof(id_scrolls[0].title); + + for (i = 0; i < SCROLS; i++) { + sylls = get_rand(2, 5); + (void)strlcpy(id_scrolls[i].title, "'", maxlen); + + for (j = 0; j < sylls; j++) { + s = get_rand(1, (MAXSYLLABLES-1)); + (void)strlcat(id_scrolls[i].title, syllables[s], + maxlen); + } + /* trim trailing space */ + n = strlen(id_scrolls[i].title); + id_scrolls[i].title[n-1] = 0; + + (void)strlcat(id_scrolls[i].title, "' ", maxlen); + } +} + +struct sbuf { + char *buf; + size_t maxlen; +}; + +static void sbuf_init(struct sbuf *s, char *buf, size_t maxlen); +static void sbuf_addstr(struct sbuf *s, const char *str); +static void sbuf_addf(struct sbuf *s, const char *fmt, ...) __printflike(2,3); +static void desc_count(struct sbuf *s, int n); +static void desc_called(struct sbuf *s, const object *); + +static +void +sbuf_init(struct sbuf *s, char *buf, size_t maxlen) +{ + s->buf = buf; + s->maxlen = maxlen; + /*assert(maxlen>0);*/ + s->buf[0] = 0; +} + +static +void +sbuf_addstr(struct sbuf *s, const char *str) +{ + strlcat(s->buf, str, s->maxlen); +} + +static +void +sbuf_addf(struct sbuf *s, const char *fmt, ...) +{ + va_list ap; + size_t initlen; + + initlen = strlen(s->buf); + va_start(ap, fmt); + vsnprintf(s->buf+initlen, s->maxlen-initlen, fmt, ap); + va_end(ap); +} + +static +void +desc_count(struct sbuf *s, int n) +{ + if (n == 1) { + sbuf_addstr(s, "an "); + } else { + sbuf_addf(s, "%d ", n); + } +} + +static +void +desc_called(struct sbuf *s, const object *obj) +{ + struct id *id_table; + + id_table = get_id_table(obj); + sbuf_addstr(s, name_of(obj)); + sbuf_addstr(s, "called "); + sbuf_addstr(s, id_table[obj->which_kind].title); +} + +void +get_desc(const object *obj, char *desc, size_t desclen) +{ + const char *item_name; + struct id *id_table; + struct sbuf db; + unsigned short objtype_id_status; + + if (obj->what_is == AMULET) { + (void)strlcpy(desc, "the amulet of Yendor ", desclen); + return; + } + + if (obj->what_is == GOLD) { + snprintf(desc, desclen, "%d pieces of gold", obj->quantity); + return; + } + + item_name = name_of(obj); + id_table = get_id_table(obj); + if (wizard || id_table == NULL) { + objtype_id_status = IDENTIFIED; + } + else { + objtype_id_status = id_table[obj->which_kind].id_status; + } + if (obj->what_is & (WEAPON | ARMOR | WAND | RING)) { + if (obj->identified) { + objtype_id_status = IDENTIFIED; + } + } + + sbuf_init(&db, desc, desclen); + + switch (obj->what_is) { + case FOOD: + if (obj->which_kind == RATION) { + if (obj->quantity > 1) { + sbuf_addf(&db, + "%d rations of %s", obj->quantity, + item_name); + } else { + sbuf_addf(&db, "some %s", item_name); + } + } else { + sbuf_addf(&db, "an %s", item_name); + } + break; + case SCROL: + desc_count(&db, obj->quantity); + if (objtype_id_status==UNIDENTIFIED) { + sbuf_addstr(&db, item_name); + sbuf_addstr(&db, "entitled: "); + sbuf_addstr(&db, id_table[obj->which_kind].title); + } else if (objtype_id_status==CALLED) { + desc_called(&db, obj); + } else { + sbuf_addstr(&db, item_name); + sbuf_addstr(&db, id_table[obj->which_kind].real); + } + break; + case POTION: + desc_count(&db, obj->quantity); + if (objtype_id_status==UNIDENTIFIED) { + sbuf_addstr(&db, id_table[obj->which_kind].title); + sbuf_addstr(&db, item_name); + } else if (objtype_id_status==CALLED) { + desc_called(&db, obj); + } else { + sbuf_addstr(&db, item_name); + sbuf_addstr(&db, id_table[obj->which_kind].real); + } + break; + case WAND: + desc_count(&db, obj->quantity); + if (objtype_id_status==UNIDENTIFIED) { + sbuf_addstr(&db, id_table[obj->which_kind].title); + sbuf_addstr(&db, item_name); + } else if (objtype_id_status==CALLED) { + desc_called(&db, obj); + } else { + sbuf_addstr(&db, item_name); + sbuf_addstr(&db, id_table[obj->which_kind].real); + if (wizard || obj->identified) { + sbuf_addf(&db, "[%d]", obj->class); + } + } + break; + case RING: + desc_count(&db, obj->quantity); + if (objtype_id_status==UNIDENTIFIED) { + sbuf_addstr(&db, id_table[obj->which_kind].title); + sbuf_addstr(&db, item_name); + } else if (objtype_id_status==CALLED) { + desc_called(&db, obj); + } else { + if ((wizard || obj->identified) && + (obj->which_kind == DEXTERITY || + obj->which_kind == ADD_STRENGTH)) { + sbuf_addf(&db, "%+d ", obj->class); + } + sbuf_addstr(&db, item_name); + sbuf_addstr(&db, id_table[obj->which_kind].real); + } + break; + case ARMOR: + /* no desc_count() */ + if (objtype_id_status==UNIDENTIFIED) { + sbuf_addstr(&db, id_table[obj->which_kind].title); + } else { + sbuf_addf(&db, "%+d %s[%d] ", obj->d_enchant, + id_table[obj->which_kind].title, + get_armor_class(obj)); + } + break; + case WEAPON: + desc_count(&db, obj->quantity); + if (objtype_id_status==UNIDENTIFIED) { + sbuf_addstr(&db, name_of(obj)); + } else { + sbuf_addf(&db, "%+d,%+d %s", + obj->hit_enchant, obj->d_enchant, + name_of(obj)); + } + break; + } + + if (obj->in_use_flags & BEING_WIELDED) { + sbuf_addstr(&db, "in hand"); + } else if (obj->in_use_flags & BEING_WORN) { + sbuf_addstr(&db, "being worn"); + } else if (obj->in_use_flags & ON_LEFT_HAND) { + sbuf_addstr(&db, "on left hand"); + } else if (obj->in_use_flags & ON_RIGHT_HAND) { + sbuf_addstr(&db, "on right hand"); + } + + if (!strncmp(db.buf, "an ", 3)) { + if (!is_vowel(db.buf[3])) { + memmove(db.buf+2, db.buf+3, strlen(db.buf+3)+1); + db.buf[1] = ' '; + } + } +} + +void +get_wand_and_ring_materials(void) +{ + short i, j; + boolean used[WAND_MATERIALS]; + + for (i = 0; i < WAND_MATERIALS; i++) { + used[i] = 0; + } + for (i = 0; i < WANDS; i++) { + do { + j = get_rand(0, WAND_MATERIALS-1); + } while (used[j]); + used[j] = 1; + (void)strlcpy(id_wands[i].title, wand_materials[j], + sizeof(id_wands[i].title)); + is_wood[i] = (j > MAX_METAL); + } + for (i = 0; i < GEMS; i++) { + used[i] = 0; + } + for (i = 0; i < RINGS; i++) { + do { + j = get_rand(0, GEMS-1); + } while (used[j]); + used[j] = 1; + (void)strlcpy(id_rings[i].title, gems[j], + sizeof(id_rings[i].title)); + } +} + +void +single_inv(short ichar) +{ + short ch, ch2; + char desc[DCOLS]; + object *obj; + + ch = ichar ? ichar : pack_letter("inventory what?", ALL_OBJECTS); + + if (ch == CANCEL) { + return; + } + if (!(obj = get_letter_object(ch))) { + messagef(0, "no such item."); + return; + } + ch2 = ((obj->what_is & ARMOR) && obj->is_protected) ? '}' : ')'; + get_desc(obj, desc, sizeof(desc)); + messagef(0, "%c%c %s", ch, ch2, desc); +} + +struct id * +get_id_table(const object *obj) +{ + switch(obj->what_is) { + case SCROL: + return(id_scrolls); + case POTION: + return(id_potions); + case WAND: + return(id_wands); + case RING: + return(id_rings); + case WEAPON: + return(id_weapons); + case ARMOR: + return(id_armors); + } + return((struct id *)0); +} + +void +inv_armor_weapon(boolean is_weapon) +{ + if (is_weapon) { + if (rogue.weapon) { + single_inv(rogue.weapon->ichar); + } else { + messagef(0, "not wielding anything"); + } + } else { + if (rogue.armor) { + single_inv(rogue.armor->ichar); + } else { + messagef(0, "not wearing anything"); + } + } +} + +void +id_type(void) +{ + const char *id; + int ch; + + messagef(0, "what do you want identified?"); + + ch = rgetchar(); + + if ((ch >= 'A') && (ch <= 'Z')) { + id = m_names[ch-'A']; + } else if (ch < 32) { + check_message(); + return; + } else { + switch(ch) { + case '@': + id = "you"; + break; + case '%': + id = "staircase"; + break; + case '^': + id = "trap"; + break; + case '+': + id = "door"; + break; + case '-': + case '|': + id = "wall of a room"; + break; + case '.': + id = "floor"; + break; + case '#': + id = "passage"; + break; + case ' ': + id = "solid rock"; + break; + case '=': + id = "ring"; + break; + case '?': + id = "scroll"; + break; + case '!': + id = "potion"; + break; + case '/': + id = "wand or staff"; + break; + case ')': + id = "weapon"; + break; + case ']': + id = "armor"; + break; + case '*': + id = "gold"; + break; + case ':': + id = "food"; + break; + case ',': + id = "the Amulet of Yendor"; + break; + default: + id = "unknown character"; + break; + } + } + check_message(); + messagef(0, "'%c': %s", ch, id); +} diff --git a/rogue/level.c b/rogue/level.c new file mode 100644 index 0000000..4b95277 --- /dev/null +++ b/rogue/level.c @@ -0,0 +1,900 @@ +/* $NetBSD: level.c,v 1.10 2008/01/14 03:50:01 dholland Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Timothy C. Stoehr. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)level.c 8.1 (Berkeley) 5/31/93"; +#else +__RCSID("$NetBSD: level.c,v 1.10 2008/01/14 03:50:01 dholland Exp $"); +#endif +#endif /* not lint */ + +/* + * level.c + * + * This source herein may be modified and/or distributed by anybody who + * so desires, with the following restrictions: + * 1.) No portion of this notice shall be removed. + * 2.) Credit shall not be taken for the creation of this source. + * 3.) This code is not to be traded, sold, or used for personal + * gain or profit. + * + */ + +#include "rogue.h" + +#define SWAP(x,y) (t = (x), (x) = (y), (y) = t) + +static void add_mazes(void); +static int connect_rooms(short, short); +static void draw_simple_passage(short, short, short, short, short); +static void fill_it(int, boolean); +static void fill_out_level(void); +static int get_exp_level(long); +static void hide_boxed_passage(short, short, short, short, short); +static void make_maze(short, short, short, short, short, short); +static void make_room(short, short, short, short); +static boolean mask_room(short, short *, short *, unsigned short); +static void mix_random_rooms(void); +static void put_door(room *, short, short *, short *); +static void recursive_deadend(short, const short *, short, short); +static int same_col(int, int); +static int same_row(int, int); + +short cur_level = 0; +short max_level = 1; +short cur_room; +const char *new_level_message = NULL; +short party_room = NO_ROOM; + +static short r_de; + +const long level_points[MAX_EXP_LEVEL] = { + 10L, + 20L, + 40L, + 80L, + 160L, + 320L, + 640L, + 1300L, + 2600L, + 5200L, + 10000L, + 20000L, + 40000L, + 80000L, + 160000L, + 320000L, + 1000000L, + 3333333L, + 6666666L, + MAX_EXP, + 99900000L +}; + +static short random_rooms[MAXROOMS] = {3, 7, 5, 2, 0, 6, 1, 4, 8}; + +void +make_level(void) +{ + short i, j; + short must_1, must_2, must_3; + boolean big_room; + + must_2 = must_3 = 0; + if (cur_level < LAST_DUNGEON) { + cur_level++; + } + if (cur_level > max_level) { + max_level = cur_level; + } + must_1 = get_rand(0, 5); + + switch(must_1) { + case 0: + must_1 = 0; + must_2 = 1; + must_3 = 2; + break; + case 1: + must_1 = 3; + must_2 = 4; + must_3 = 5; + break; + case 2: + must_1 = 6; + must_2 = 7; + must_3 = 8; + break; + case 3: + must_1 = 0; + must_2 = 3; + must_3 = 6; + break; + case 4: + must_1 = 1; + must_2 = 4; + must_3 = 7; + break; + case 5: + must_1 = 2; + must_2 = 5; + must_3 = 8; + break; + } + if (rand_percent(8)) { + party_room = 0; + } + big_room = ((party_room != NO_ROOM) && rand_percent(1)); + if (big_room) { + make_room(BIG_ROOM, 0, 0, 0); + } else { + for (i = 0; i < MAXROOMS; i++) { + make_room(i, must_1, must_2, must_3); + } + } + if (!big_room) { + add_mazes(); + + mix_random_rooms(); + + for (j = 0; j < MAXROOMS; j++) { + + i = random_rooms[j]; + + if (i < (MAXROOMS-1)) { + (void)connect_rooms(i, i+1); + } + if (i < (MAXROOMS-3)) { + (void)connect_rooms(i, i+3); + } + if (i < (MAXROOMS-2)) { + if (rooms[i+1].is_room & R_NOTHING) { + if (connect_rooms(i, i+2)) { + rooms[i+1].is_room = R_CROSS; + } + } + } + if (i < (MAXROOMS-6)) { + if (rooms[i+3].is_room & R_NOTHING) { + if (connect_rooms(i, i+6)) { + rooms[i+3].is_room = R_CROSS; + } + } + } + if (is_all_connected()) { + break; + } + } + fill_out_level(); + } + if (!has_amulet() && (cur_level >= AMULET_LEVEL)) { + put_amulet(); + } +} + +static void +make_room(short rn, short r1, short r2, short r3) +{ + short left_col, right_col, top_row, bottom_row; + short width, height; + short row_offset, col_offset; + short i, j, ch; + + left_col = right_col = top_row = bottom_row = 0; + switch(rn) { + case 0: + left_col = 0; + right_col = COL1-1; + top_row = MIN_ROW; + bottom_row = ROW1-1; + break; + case 1: + left_col = COL1+1; + right_col = COL2-1; + top_row = MIN_ROW; + bottom_row = ROW1-1; + break; + case 2: + left_col = COL2+1; + right_col = DCOLS-1; + top_row = MIN_ROW; + bottom_row = ROW1-1; + break; + case 3: + left_col = 0; + right_col = COL1-1; + top_row = ROW1+1; + bottom_row = ROW2-1; + break; + case 4: + left_col = COL1+1; + right_col = COL2-1; + top_row = ROW1+1; + bottom_row = ROW2-1; + break; + case 5: + left_col = COL2+1; + right_col = DCOLS-1; + top_row = ROW1+1; + bottom_row = ROW2-1; + break; + case 6: + left_col = 0; + right_col = COL1-1; + top_row = ROW2+1; + bottom_row = DROWS - 2; + break; + case 7: + left_col = COL1+1; + right_col = COL2-1; + top_row = ROW2+1; + bottom_row = DROWS - 2; + break; + case 8: + left_col = COL2+1; + right_col = DCOLS-1; + top_row = ROW2+1; + bottom_row = DROWS - 2; + break; + case BIG_ROOM: + top_row = get_rand(MIN_ROW, MIN_ROW+5); + bottom_row = get_rand(DROWS-7, DROWS-2); + left_col = get_rand(0, 10); + right_col = get_rand(DCOLS-11, DCOLS-1); + rn = 0; + goto B; + } + height = get_rand(4, (bottom_row - top_row + 1)); + width = get_rand(7, (right_col - left_col - 2)); + + row_offset = get_rand(0, ((bottom_row - top_row) - height + 1)); + col_offset = get_rand(0, ((right_col - left_col) - width + 1)); + + top_row += row_offset; + bottom_row = top_row + height - 1; + + left_col += col_offset; + right_col = left_col + width - 1; + + if ((rn != r1) && (rn != r2) && (rn != r3) && rand_percent(40)) { + goto END; + } +B: + rooms[rn].is_room = R_ROOM; + + for (i = top_row; i <= bottom_row; i++) { + for (j = left_col; j <= right_col; j++) { + if ((i == top_row) || (i == bottom_row)) { + ch = HORWALL; + } else if ( ((i != top_row) && (i != bottom_row)) && + ((j == left_col) || (j == right_col))) { + ch = VERTWALL; + } else { + ch = FLOOR; + } + dungeon[i][j] = ch; + } + } +END: + rooms[rn].top_row = top_row; + rooms[rn].bottom_row = bottom_row; + rooms[rn].left_col = left_col; + rooms[rn].right_col = right_col; +} + +static int +connect_rooms(short room1, short room2) +{ + short row1, col1, row2, col2, dir; + + if ((!(rooms[room1].is_room & (R_ROOM | R_MAZE))) || + (!(rooms[room2].is_room & (R_ROOM | R_MAZE)))) { + return(0); + } + if (same_row(room1, room2) && + (rooms[room1].left_col > rooms[room2].right_col)) { + put_door(&rooms[room1], LEFT, &row1, &col1); + put_door(&rooms[room2], RIGHT, &row2, &col2); + dir = LEFT; + } else if (same_row(room1, room2) && + (rooms[room2].left_col > rooms[room1].right_col)) { + put_door(&rooms[room1], RIGHT, &row1, &col1); + put_door(&rooms[room2], LEFT, &row2, &col2); + dir = RIGHT; + } else if (same_col(room1, room2) && + (rooms[room1].top_row > rooms[room2].bottom_row)) { + put_door(&rooms[room1], UPWARD, &row1, &col1); + put_door(&rooms[room2], DOWN, &row2, &col2); + dir = UPWARD; + } else if (same_col(room1, room2) && + (rooms[room2].top_row > rooms[room1].bottom_row)) { + put_door(&rooms[room1], DOWN, &row1, &col1); + put_door(&rooms[room2], UPWARD, &row2, &col2); + dir = DOWN; + } else { + return(0); + } + + do { + draw_simple_passage(row1, col1, row2, col2, dir); + } while (rand_percent(4)); + + rooms[room1].doors[dir/2].oth_room = room2; + rooms[room1].doors[dir/2].oth_row = row2; + rooms[room1].doors[dir/2].oth_col = col2; + + rooms[room2].doors[(((dir+4)%DIRS)/2)].oth_room = room1; + rooms[room2].doors[(((dir+4)%DIRS)/2)].oth_row = row1; + rooms[room2].doors[(((dir+4)%DIRS)/2)].oth_col = col1; + return(1); +} + +void +clear_level(void) +{ + short i, j; + + for (i = 0; i < MAXROOMS; i++) { + rooms[i].is_room = R_NOTHING; + for (j = 0; j < 4; j++) { + rooms[i].doors[j].oth_room = NO_ROOM; + } + } + + for (i = 0; i < MAX_TRAPS; i++) { + traps[i].trap_type = NO_TRAP; + } + for (i = 0; i < DROWS; i++) { + for (j = 0; j < DCOLS; j++) { + dungeon[i][j] = NOTHING; + } + } + detect_monster = see_invisible = 0; + being_held = bear_trap = 0; + party_room = NO_ROOM; + rogue.row = rogue.col = -1; + clear(); +} + +static void +put_door(room *rm, short dir, short *row, short *col) +{ + short wall_width; + + wall_width = (rm->is_room & R_MAZE) ? 0 : 1; + + switch(dir) { + case UPWARD: + case DOWN: + *row = ((dir == UPWARD) ? rm->top_row : rm->bottom_row); + do { + *col = get_rand(rm->left_col+wall_width, + rm->right_col-wall_width); + } while (!(dungeon[*row][*col] & (HORWALL | TUNNEL))); + break; + case RIGHT: + case LEFT: + *col = (dir == LEFT) ? rm->left_col : rm->right_col; + do { + *row = get_rand(rm->top_row+wall_width, + rm->bottom_row-wall_width); + } while (!(dungeon[*row][*col] & (VERTWALL | TUNNEL))); + break; + } + if (rm->is_room & R_ROOM) { + dungeon[*row][*col] = DOOR; + } + if ((cur_level > 2) && rand_percent(HIDE_PERCENT)) { + dungeon[*row][*col] |= HIDDEN; + } + rm->doors[dir/2].door_row = *row; + rm->doors[dir/2].door_col = *col; +} + +static void +draw_simple_passage(short row1, short col1, short row2, short col2, short dir) +{ + short i, middle, t; + + if ((dir == LEFT) || (dir == RIGHT)) { + if (col1 > col2) { + SWAP(row1, row2); + SWAP(col1, col2); + } + middle = get_rand(col1+1, col2-1); + for (i = col1+1; i != middle; i++) { + dungeon[row1][i] = TUNNEL; + } + for (i = row1; i != row2; i += (row1 > row2) ? -1 : 1) { + dungeon[i][middle] = TUNNEL; + } + for (i = middle; i != col2; i++) { + dungeon[row2][i] = TUNNEL; + } + } else { + if (row1 > row2) { + SWAP(row1, row2); + SWAP(col1, col2); + } + middle = get_rand(row1+1, row2-1); + for (i = row1+1; i != middle; i++) { + dungeon[i][col1] = TUNNEL; + } + for (i = col1; i != col2; i += (col1 > col2) ? -1 : 1) { + dungeon[middle][i] = TUNNEL; + } + for (i = middle; i != row2; i++) { + dungeon[i][col2] = TUNNEL; + } + } + if (rand_percent(HIDE_PERCENT)) { + hide_boxed_passage(row1, col1, row2, col2, 1); + } +} + +static int +same_row(int room1, int room2) +{ + return((room1 / 3) == (room2 / 3)); +} + +static int +same_col(int room1, int room2) +{ + return((room1 % 3) == (room2 % 3)); +} + +static void +add_mazes(void) +{ + short i, j; + short start; + short maze_percent; + + if (cur_level > 1) { + start = get_rand(0, (MAXROOMS-1)); + maze_percent = (cur_level * 5) / 4; + + if (cur_level > 15) { + maze_percent += cur_level; + } + for (i = 0; i < MAXROOMS; i++) { + j = ((start + i) % MAXROOMS); + if (rooms[j].is_room & R_NOTHING) { + if (rand_percent(maze_percent)) { + rooms[j].is_room = R_MAZE; + make_maze(get_rand(rooms[j].top_row+1, rooms[j].bottom_row-1), + get_rand(rooms[j].left_col+1, rooms[j].right_col-1), + rooms[j].top_row, rooms[j].bottom_row, + rooms[j].left_col, rooms[j].right_col); + hide_boxed_passage(rooms[j].top_row, rooms[j].left_col, + rooms[j].bottom_row, rooms[j].right_col, + get_rand(0, 2)); + } + } + } + } +} + +static void +fill_out_level(void) +{ + short i, rn; + + mix_random_rooms(); + + r_de = NO_ROOM; + + for (i = 0; i < MAXROOMS; i++) { + rn = random_rooms[i]; + if ((rooms[rn].is_room & R_NOTHING) || + ((rooms[rn].is_room & R_CROSS) && coin_toss())) { + fill_it(rn, 1); + } + } + if (r_de != NO_ROOM) { + fill_it(r_de, 0); + } +} + +static void +fill_it(int rn, boolean do_rec_de) +{ + short i, tunnel_dir, door_dir, drow, dcol; + short target_room, rooms_found = 0; + short srow, scol, t; + static short offsets[4] = {-1, 1, 3, -3}; + boolean did_this = 0; + + for (i = 0; i < 10; i++) { + srow = get_rand(0, 3); + scol = get_rand(0, 3); + t = offsets[srow]; + offsets[srow] = offsets[scol]; + offsets[scol] = t; + } + for (i = 0; i < 4; i++) { + + target_room = rn + offsets[i]; + + if (((target_room < 0) || (target_room >= MAXROOMS)) || + (!(same_row(rn,target_room) || same_col(rn,target_room))) || + (!(rooms[target_room].is_room & (R_ROOM | R_MAZE)))) { + continue; + } + if (same_row(rn, target_room)) { + tunnel_dir = (rooms[rn].left_col < rooms[target_room].left_col) ? + RIGHT : LEFT; + } else { + tunnel_dir = (rooms[rn].top_row < rooms[target_room].top_row) ? + DOWN : UPWARD; + } + door_dir = ((tunnel_dir + 4) % DIRS); + if (rooms[target_room].doors[door_dir/2].oth_room != NO_ROOM) { + continue; + } + if (((!do_rec_de) || did_this) || + (!mask_room(rn, &srow, &scol, TUNNEL))) { + srow = (rooms[rn].top_row + rooms[rn].bottom_row) / 2; + scol = (rooms[rn].left_col + rooms[rn].right_col) / 2; + } + put_door(&rooms[target_room], door_dir, &drow, &dcol); + rooms_found++; + draw_simple_passage(srow, scol, drow, dcol, tunnel_dir); + rooms[rn].is_room = R_DEADEND; + dungeon[srow][scol] = TUNNEL; + + if ((i < 3) && (!did_this)) { + did_this = 1; + if (coin_toss()) { + continue; + } + } + if ((rooms_found < 2) && do_rec_de) { + recursive_deadend(rn, offsets, srow, scol); + } + break; + } +} + +static void +recursive_deadend(short rn, const short *offsets, short srow, short scol) +{ + short i, de; + short drow, dcol, tunnel_dir; + + rooms[rn].is_room = R_DEADEND; + dungeon[srow][scol] = TUNNEL; + + for (i = 0; i < 4; i++) { + de = rn + offsets[i]; + if (((de < 0) || (de >= MAXROOMS)) || + (!(same_row(rn, de) || same_col(rn, de)))) { + continue; + } + if (!(rooms[de].is_room & R_NOTHING)) { + continue; + } + drow = (rooms[de].top_row + rooms[de].bottom_row) / 2; + dcol = (rooms[de].left_col + rooms[de].right_col) / 2; + if (same_row(rn, de)) { + tunnel_dir = (rooms[rn].left_col < rooms[de].left_col) ? + RIGHT : LEFT; + } else { + tunnel_dir = (rooms[rn].top_row < rooms[de].top_row) ? + DOWN : UPWARD; + } + draw_simple_passage(srow, scol, drow, dcol, tunnel_dir); + r_de = de; + recursive_deadend(de, offsets, drow, dcol); + } +} + +static boolean +mask_room(short rn, short *row, short *col, unsigned short mask) +{ + short i, j; + + for (i = rooms[rn].top_row; i <= rooms[rn].bottom_row; i++) { + for (j = rooms[rn].left_col; j <= rooms[rn].right_col; j++) { + if (dungeon[i][j] & mask) { + *row = i; + *col = j; + return(1); + } + } + } + return(0); +} + +static void +make_maze(short r, short c, short tr, short br, short lc, short rc) +{ + char dirs[4]; + short i, t; + + dirs[0] = UPWARD; + dirs[1] = DOWN; + dirs[2] = LEFT; + dirs[3] = RIGHT; + + dungeon[r][c] = TUNNEL; + + if (rand_percent(20)) { + for (i = 0; i < 10; i++) { + short t1, t2; + + t1 = get_rand(0, 3); + t2 = get_rand(0, 3); + + SWAP(dirs[t1], dirs[t2]); + } + } + for (i = 0; i < 4; i++) { + switch(dirs[i]) { + case UPWARD: + if (((r-1) >= tr) && + (dungeon[r-1][c] != TUNNEL) && + (dungeon[r-1][c-1] != TUNNEL) && + (dungeon[r-1][c+1] != TUNNEL) && + (dungeon[r-2][c] != TUNNEL)) { + make_maze((r-1), c, tr, br, lc, rc); + } + break; + case DOWN: + if (((r+1) <= br) && + (dungeon[r+1][c] != TUNNEL) && + (dungeon[r+1][c-1] != TUNNEL) && + (dungeon[r+1][c+1] != TUNNEL) && + (dungeon[r+2][c] != TUNNEL)) { + make_maze((r+1), c, tr, br, lc, rc); + } + break; + case LEFT: + if (((c-1) >= lc) && + (dungeon[r][c-1] != TUNNEL) && + (dungeon[r-1][c-1] != TUNNEL) && + (dungeon[r+1][c-1] != TUNNEL) && + (dungeon[r][c-2] != TUNNEL)) { + make_maze(r, (c-1), tr, br, lc, rc); + } + break; + case RIGHT: + if (((c+1) <= rc) && + (dungeon[r][c+1] != TUNNEL) && + (dungeon[r-1][c+1] != TUNNEL) && + (dungeon[r+1][c+1] != TUNNEL) && + (dungeon[r][c+2] != TUNNEL)) { + make_maze(r, (c+1), tr, br, lc, rc); + } + break; + } + } +} + +static void +hide_boxed_passage(short row1, short col1, short row2, short col2, short n) +{ + short i, j, t; + short row, col, row_cut, col_cut; + short h, w; + + if (cur_level > 2) { + if (row1 > row2) { + SWAP(row1, row2); + } + if (col1 > col2) { + SWAP(col1, col2); + } + h = row2 - row1; + w = col2 - col1; + + if ((w >= 5) || (h >= 5)) { + row_cut = ((h >= 2) ? 1 : 0); + col_cut = ((w >= 2) ? 1 : 0); + + for (i = 0; i < n; i++) { + for (j = 0; j < 10; j++) { + row = get_rand(row1 + row_cut, row2 - row_cut); + col = get_rand(col1 + col_cut, col2 - col_cut); + if (dungeon[row][col] == TUNNEL) { + dungeon[row][col] |= HIDDEN; + break; + } + } + } + } + } +} + +/* + * try not to put in room NR + */ +void +put_player(short nr) +{ + short rn = nr, misses; + short row, col; + + for (misses = 0; ((misses < 2) && (rn == nr)); misses++) { + gr_row_col(&row, &col, (FLOOR | TUNNEL | OBJECT | STAIRS)); + rn = get_room_number(row, col); + } + rogue.row = row; + rogue.col = col; + + if (dungeon[rogue.row][rogue.col] & TUNNEL) { + cur_room = PASSAGE; + } else { + cur_room = rn; + } + if (cur_room != PASSAGE) { + light_up_room(cur_room); + } else { + light_passage(rogue.row, rogue.col); + } + rn = get_room_number(rogue.row, rogue.col); + wake_room(rn, 1, rogue.row, rogue.col); + if (new_level_message) { + messagef(0, "%s", new_level_message); + new_level_message = NULL; + } + mvaddch(rogue.row, rogue.col, rogue.fchar); +} + +int +drop_check(void) +{ + if (wizard) { + return(1); + } + if (dungeon[rogue.row][rogue.col] & STAIRS) { + if (levitate) { + messagef(0, "you're floating in the air!"); + return(0); + } + return(1); + } + messagef(0, "I see no way down"); + return(0); +} + +int +check_up(void) +{ + if (!wizard) { + if (!(dungeon[rogue.row][rogue.col] & STAIRS)) { + messagef(0, "I see no way up"); + return(0); + } + if (!has_amulet()) { + messagef(0, "your way is magically blocked"); + return(0); + } + } + new_level_message = "you feel a wrenching sensation in your gut"; + if (cur_level == 1) { + win(); + } else { + cur_level -= 2; + return(1); + } + return(0); +} + +void +add_exp(int e, boolean promotion) +{ + short new_exp; + short i, hp; + + rogue.exp_points += e; + + if (rogue.exp_points >= level_points[rogue.exp-1]) { + new_exp = get_exp_level(rogue.exp_points); + if (rogue.exp_points > MAX_EXP) { + rogue.exp_points = MAX_EXP + 1; + } + for (i = rogue.exp+1; i <= new_exp; i++) { + messagef(0, "welcome to level %d", i); + if (promotion) { + hp = hp_raise(); + rogue.hp_current += hp; + rogue.hp_max += hp; + } + rogue.exp = i; + print_stats(STAT_HP | STAT_EXP); + } + } else { + print_stats(STAT_EXP); + } +} + +static int +get_exp_level(long e) +{ + short i; + + for (i = 0; i < (MAX_EXP_LEVEL - 1); i++) { + if (level_points[i] > e) { + break; + } + } + return(i+1); +} + +int +hp_raise(void) +{ + int hp; + + hp = (wizard ? 10 : get_rand(3, 10)); + return(hp); +} + +void +show_average_hp(void) +{ + float real_average; + float effective_average; + + if (rogue.exp == 1) { + real_average = effective_average = 0.00; + } else { + real_average = (float) + ((rogue.hp_max - extra_hp - INIT_HP) + less_hp) / (rogue.exp - 1); + effective_average = (float)(rogue.hp_max - INIT_HP) / (rogue.exp - 1); + + } + messagef(0, "R-Hp: %.2f, E-Hp: %.2f (!: %d, V: %d)", real_average, + effective_average, extra_hp, less_hp); +} + +static void +mix_random_rooms(void) +{ + short i, t; + short x, y; + + for (i = 0; i < (3 * MAXROOMS); i++) { + do { + x = get_rand(0, (MAXROOMS-1)); + y = get_rand(0, (MAXROOMS-1)); + } while (x == y); + SWAP(random_rooms[x], random_rooms[y]); + } +} diff --git a/rogue/machdep.c b/rogue/machdep.c new file mode 100644 index 0000000..7f5b261 --- /dev/null +++ b/rogue/machdep.c @@ -0,0 +1,493 @@ +/* $NetBSD: machdep.c,v 1.20 2012/12/01 11:37:27 mbalmer Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Timothy C. Stoehr. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#include <time.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)machdep.c 8.1 (Berkeley) 5/31/93"; +#else +__RCSID("$NetBSD: machdep.c,v 1.20 2012/12/01 11:37:27 mbalmer Exp $"); +#endif +#endif /* not lint */ + +/* + * machdep.c + * + * This source herein may be modified and/or distributed by anybody who + * so desires, with the following restrictions: + * 1.) No portion of this notice shall be removed. + * 2.) Credit shall not be taken for the creation of this source. + * 3.) This code is not to be traded, sold, or used for personal + * gain or profit. + * + */ + +/* Included in this file are all system dependent routines. Extensive use + * of #ifdef's will be used to compile the appropriate code on each system: + * + * UNIX: all UNIX systems. + * UNIX_BSD4_2: UNIX BSD 4.2 and later, UTEK, (4.1 BSD too?) + * UNIX_SYSV: UNIX system V + * UNIX_V7: UNIX version 7 + * + * All UNIX code should be included between the single "#ifdef UNIX" at the + * top of this file, and the "#endif" at the bottom. + * + * To change a routine to include a new UNIX system, simply #ifdef the + * existing routine, as in the following example: + * + * To make a routine compatible with UNIX system 5, change the first + * function to the second: + * + * md_function() + * { + * code; + * } + * + * md_function() + * { + * #ifdef UNIX_SYSV + * sys5code; + * #else + * code; + * #endif + * } + * + * Appropriate variations of this are of course acceptable. + * The use of "#elseif" is discouraged because of non-portability. + * If the correct #define doesn't exist, "UNIX_SYSV" in this case, make it up + * and insert it in the list at the top of the file. Alter the CFLAGS + * in you Makefile appropriately. + * + */ + +#ifdef UNIX + +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <pwd.h> + +#ifdef UNIX_BSD4_2 +#include <sys/time.h> +#endif + +#ifdef UNIX_SYSV +#include <time.h> +#endif + +#include <signal.h> +#include <stdlib.h> +#include <termios.h> +#include <unistd.h> +#include "rogue.h" +#include "pathnames.h" + +/* md_slurp: + * + * This routine throws away all keyboard input that has not + * yet been read. It is used to get rid of input that the user may have + * typed-ahead. + * + * This function is not necessary, so it may be stubbed. The might cause + * message-line output to flash by because the game has continued to read + * input without waiting for the user to read the message. Not such a + * big deal. + */ + +void +md_slurp(void) +{ + (void)fpurge(stdin); +} + +/* md_heed_signals(): + * + * This routine tells the program to call particular routines when + * certain interrupts/events occur: + * + * SIGINT: call onintr() to interrupt fight with monster or long rest. + * SIGQUIT: call byebye() to check for game termination. + * SIGHUP: call error_save() to save game when terminal hangs up. + * + * On VMS, SIGINT and SIGQUIT correspond to ^C and ^Y. + * + * This routine is not strictly necessary and can be stubbed. This will + * mean that the game cannot be interrupted properly with keyboard + * input, this is not usually critical. + */ + +void +md_heed_signals(void) +{ + signal(SIGINT, onintr); + signal(SIGQUIT, byebye); + signal(SIGHUP, error_save); +} + +/* md_ignore_signals(): + * + * This routine tells the program to completely ignore the events mentioned + * in md_heed_signals() above. The event handlers will later be turned on + * by a future call to md_heed_signals(), so md_heed_signals() and + * md_ignore_signals() need to work together. + * + * This function should be implemented or the user risks interrupting + * critical sections of code, which could cause score file, or saved-game + * file, corruption. + */ + +void +md_ignore_signals(void) +{ + signal(SIGQUIT, SIG_IGN); + signal(SIGINT, SIG_IGN); + signal(SIGHUP, SIG_IGN); +} + +/* md_get_file_id(): + * + * This function returns an integer that uniquely identifies the specified + * file. It need not check for the file's existence. In UNIX, the inode + * number is used. + * + * This function is used to identify saved-game files. + */ + +int +md_get_file_id(const char *fname) +{ + struct stat sbuf; + + if (stat(fname, &sbuf)) { + return(-1); + } + return((int)sbuf.st_ino); +} + +/* md_link_count(): + * + * This routine returns the number of hard links to the specified file. + * + * This function is not strictly necessary. On systems without hard links + * this routine can be stubbed by just returning 1. + */ + +int +md_link_count(const char *fname) +{ + struct stat sbuf; + + stat(fname, &sbuf); + return((int)sbuf.st_nlink); +} + +/* md_gct(): (Get Current Time) + * + * This function returns the current year, month(1-12), day(1-31), hour(0-23), + * minute(0-59), and second(0-59). This is used for identifying the time + * at which a game is saved. + * + * This function is not strictly necessary. It can be stubbed by returning + * zeros instead of the correct year, month, etc. If your operating + * system doesn't provide all of the time units requested here, then you + * can provide only those that it does, and return zeros for the others. + * If you cannot provide good time values, then users may be able to copy + * saved-game files and play them. + */ + +void +md_gct(struct rogue_time *rt_buf) +{ + struct tm *t; + time_t seconds; + + time(&seconds); + t = localtime(&seconds); + + rt_buf->year = t->tm_year; + rt_buf->month = t->tm_mon + 1; + rt_buf->day = t->tm_mday; + rt_buf->hour = t->tm_hour; + rt_buf->minute = t->tm_min; + rt_buf->second = t->tm_sec; +} + +/* md_gfmt: (Get File Modification Time) + * + * This routine returns a file's date of last modification in the same format + * as md_gct() above. + * + * This function is not strictly necessary. It is used to see if saved-game + * files have been modified since they were saved. If you have stubbed the + * routine md_gct() above by returning constant values, then you may do + * exactly the same here. + * Or if md_gct() is implemented correctly, but your system does not provide + * file modification dates, you may return some date far in the past so + * that the program will never know that a saved-game file being modified. + * You may also do this if you wish to be able to restore games from + * saved-games that have been modified. + */ + +void +md_gfmt(const char *fname, struct rogue_time *rt_buf) +{ + struct stat sbuf; + time_t seconds; + struct tm *t; + + stat(fname, &sbuf); + seconds = sbuf.st_mtime; + t = localtime(&seconds); + + rt_buf->year = t->tm_year; + rt_buf->month = t->tm_mon + 1; + rt_buf->day = t->tm_mday; + rt_buf->hour = t->tm_hour; + rt_buf->minute = t->tm_min; + rt_buf->second = t->tm_sec; +} + +/* md_df: (Delete File) + * + * This function deletes the specified file, and returns true (1) if the + * operation was successful. This is used to delete saved-game files + * after restoring games from them. + * + * Again, this function is not strictly necessary, and can be stubbed + * by simply returning 1. In this case, saved-game files will not be + * deleted and can be replayed. + */ + +boolean +md_df(const char *fname) +{ + if (unlink(fname)) { + return(0); + } + return(1); +} + +/* md_gln: (Get login name) + * + * This routine returns the login name of the user. This string is + * used mainly for identifying users in score files. + * + * A dummy string may be returned if you are unable to implement this + * function, but then the score file would only have one name in it. + */ + +const char * +md_gln(void) +{ + struct passwd *p; + + if (!(p = getpwuid(getuid()))) + return NULL; + return p->pw_name; +} + +/* md_sleep: + * + * This routine causes the game to pause for the specified number of + * seconds. + * + * This routine is not particularly necessary at all. It is used for + * delaying execution, which is useful to this program at some times. + */ + +void +md_sleep(int nsecs) +{ + (void)sleep(nsecs); +} + +/* md_getenv() + * + * This routine gets certain values from the user's environment. These + * values are strings, and each string is identified by a name. The names + * of the values needed, and their use, is as follows: + * + * ROGUEOPTS + * A string containing the various game options. This need not be + * defined. + * HOME + * The user's home directory. This is only used when the user specifies + * '~' as the first character of a saved-game file. This string need + * not be defined. + * SHELL + * The user's favorite shell. If not found, "/bin/sh" is assumed. + * + * If your system does not provide a means of searching for these values, + * you will have to do it yourself. None of the values above really need + * to be defined; you can get by with simply always returning zero. + * Returning zero indicates that their is no defined value for the + * given string. + */ + +char * +md_getenv(const char *name) +{ + char *value; + + value = getenv(name); + + return(value); +} + +/* md_malloc() + * + * This routine allocates, and returns a pointer to, the specified number + * of bytes. This routines absolutely MUST be implemented for your + * particular system or the program will not run at all. Return zero + * when no more memory can be allocated. + */ + +void * +md_malloc(size_t n) +{ + char *t; + + t = malloc(n); + return(t); +} + +/* md_gseed() (Get Seed) + * + * This function returns a seed for the random number generator (RNG). This + * seed causes the RNG to begin generating numbers at some point in its + * sequence. Without a random seed, the RNG will generate the same set + * of numbers, and every game will start out exactly the same way. A good + * number to use is the process id, given by getpid() on most UNIX systems. + * + * You need to find some single random integer, such as: + * process id. + * current time (minutes + seconds) returned from md_gct(), if implemented. + * + * It will not help to return "get_rand()" or "rand()" or the return value of + * any pseudo-RNG. If you don't have a random number, you can just return 1, + * but this means your games will ALWAYS start the same way, and will play + * exactly the same way given the same input. + */ + +int +md_gseed(void) +{ + time_t seconds; + + time(&seconds); + return((int)seconds); +} + +/* md_exit(): + * + * This function causes the program to discontinue execution and exit. + * This function must be implemented or the program will continue to + * hang when it should quit. + */ + +void +md_exit(int status) +{ + exit(status); +} + +/* md_lock(): + * + * This function is intended to give the user exclusive access to the score + * file. It does so by flock'ing the score file. The full path name of the + * score file should be defined for any particular site in rogue.h. The + * constants _PATH_SCOREFILE defines this file name. + * + * When the parameter 'l' is non-zero (true), a lock is requested. Otherwise + * the lock is released. + */ + +void +md_lock(boolean l) +{ + static int fd = -1; + short tries; + + if (l) { + setegid(egid); + if ((fd = open(_PATH_SCOREFILE, O_RDONLY)) < 1) { + setegid(gid); + messagef(0, "cannot lock score file"); + return; + } + setegid(gid); + for (tries = 0; tries < 5; tries++) + if (!flock(fd, LOCK_EX|LOCK_NB)) + return; + } else { + (void)flock(fd, LOCK_UN|LOCK_NB); + (void)close(fd); + } +} + +/* md_shell(): + * + * This function spawns a shell for the user to use. When this shell is + * terminated, the game continues. + * + * It is important that the game not give the shell the privileges the + * game uses to access the scores file. This version of the game runs + * with privileges low by default; only the saved gid (if setgid) or uid + * (if setuid) will be privileged, but that privilege is discarded by + * exec(). + */ + +void +md_shell(const char *shell) +{ + int w; + pid_t pid; + + pid = fork(); + switch (pid) { + case -1: + break; + case 0: + execl(shell, shell, (char *)NULL); + _exit(255); + default: + waitpid(pid, &w, 0); + break; + } +} + +#endif /* UNIX */ diff --git a/rogue/main.c b/rogue/main.c new file mode 100644 index 0000000..fb88436 --- /dev/null +++ b/rogue/main.c @@ -0,0 +1,84 @@ +/* $NetBSD: main.c,v 1.9 2008/07/20 01:03:22 lukem Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Timothy C. Stoehr. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1988, 1993\ + The Regents of the University of California. All rights reserved."); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 5/31/93"; +#else +__RCSID("$NetBSD: main.c,v 1.9 2008/07/20 01:03:22 lukem Exp $"); +#endif +#endif /* not lint */ + +/* + * main.c + * + * This source herein may be modified and/or distributed by anybody who + * so desires, with the following restrictions: + * 1.) No portion of this notice shall be removed. + * 2.) Credit shall not be taken for the creation of this source. + * 3.) This code is not to be traded, sold, or used for personal + * gain or profit. + * + */ + +#include "rogue.h" + +int +main(int argc, char *argv[]) +{ + if (init(argc, argv)) { /* restored game */ + goto PL; + } + + for (;;) { + clear_level(); + make_level(); + put_objects(); + put_stairs(); + add_traps(); + put_mons(); + put_player(party_room); + print_stats(STAT_ALL); +PL: + play_level(); + free_stuff(&level_objects); + free_stuff(&level_monsters); + } +} diff --git a/rogue/message.c b/rogue/message.c new file mode 100644 index 0000000..e38377d --- /dev/null +++ b/rogue/message.c @@ -0,0 +1,378 @@ +/* $NetBSD: message.c,v 1.14 2009/08/12 08:44:45 dholland Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Timothy C. Stoehr. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)message.c 8.1 (Berkeley) 5/31/93"; +#else +__RCSID("$NetBSD: message.c,v 1.14 2009/08/12 08:44:45 dholland Exp $"); +#endif +#endif /* not lint */ + +/* + * message.c + * + * This source herein may be modified and/or distributed by anybody who + * so desires, with the following restrictions: + * 1.) No portion of this notice shall be removed. + * 2.) Credit shall not be taken for the creation of this source. + * 3.) This code is not to be traded, sold, or used for personal + * gain or profit. + * + */ + +#include <signal.h> +#include <termios.h> +#include <stdarg.h> +#include "rogue.h" +#include "pathnames.h" + +static char msgs[NMESSAGES][DCOLS] = {"", "", "", "", ""}; +static short msg_col = 0, imsg = -1; +static boolean rmsg = 0; + +boolean msg_cleared = 1; +char hunger_str[HUNGER_STR_LEN] = ""; +const char *more = "-more-"; + +static void save_screen(void); + +static +void +message(const char *msg, boolean intrpt) +{ + cant_int = 1; + + if (!save_is_interactive) { + return; + } + if (intrpt) { + interrupted = 1; + md_slurp(); + } + + if (!msg_cleared) { + mvaddstr(MIN_ROW-1, msg_col, more); + refresh(); + wait_for_ack(); + check_message(); + } + if (!rmsg) { + imsg = (imsg + 1) % NMESSAGES; + (void)strlcpy(msgs[imsg], msg, sizeof(msgs[imsg])); + } + mvaddstr(MIN_ROW-1, 0, msg); + addch(' '); + refresh(); + msg_cleared = 0; + msg_col = strlen(msg); + + cant_int = 0; + + if (did_int) { + did_int = 0; + onintr(0); + } +} + +void +messagef(boolean intrpt, const char *fmt, ...) +{ + va_list ap; + char buf[DCOLS]; + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + + message(buf, intrpt); +} + +void +remessage(short c) +{ + if (imsg != -1) { + check_message(); + rmsg = 1; + while (c > imsg) { + c -= NMESSAGES; + } + message(msgs[((imsg - c) % NMESSAGES)], 0); + rmsg = 0; + move(rogue.row, rogue.col); + refresh(); + } +} + +void +check_message(void) +{ + if (msg_cleared) { + return; + } + move(MIN_ROW-1, 0); + clrtoeol(); + refresh(); + msg_cleared = 1; +} + +int +get_input_line(const char *prompt, const char *insert, + char *buf, size_t buflen, + const char *if_cancelled, + boolean add_blank, boolean do_echo) +{ + short ch; + size_t i = 0, n; + + message(prompt, 0); + n = strlen(prompt); + + if (insert[0]) { + mvaddstr(0, n + 1, insert); + (void)strlcpy(buf, insert, buflen); + i = strlen(buf); + move(0, (n + i + 1)); + refresh(); + } + + while (((ch = rgetchar()) != '\r') && (ch != '\n') && (ch != CANCEL)) { + if ((ch >= ' ') && (ch <= '~') && (i < buflen-2)) { + if ((ch != ' ') || (i > 0)) { + buf[i++] = ch; + if (do_echo) { + addch(ch); + } + } + } + if ((ch == '\b') && (i > 0)) { + if (do_echo) { + mvaddch(0, i + n, ' '); + move(MIN_ROW-1, i+n); + } + i--; + } + refresh(); + } + check_message(); + if (add_blank) { + buf[i++] = ' '; + } else { + while ((i > 0) && (buf[i-1] == ' ')) { + i--; + } + } + + buf[i] = 0; + + if ((ch == CANCEL) || (i == 0) || ((i == 1) && add_blank)) { + if (if_cancelled) { + message(if_cancelled, 0); + } + return(0); + } + return(i); +} + +int +rgetchar(void) +{ + int ch; + + for(;;) { + ch = getchar(); + + switch(ch) { + case '\022': /* ^R */ + wrefresh(curscr); + break; +#ifdef UNIX_BSD4_2 + case '\032': /* ^Z */ + printf("%s", CL); + fflush(stdout); + tstp(); + break; +#endif + case '&': + save_screen(); + break; + default: + return(ch); + } + } +} + +/* +Level: 99 Gold: 999999 Hp: 999(999) Str: 99(99) Arm: 99 Exp: 21/10000000 Hungry +0 5 1 5 2 5 3 5 4 5 5 5 6 5 7 5 +*/ + +void +print_stats(int stat_mask) +{ + char buf[16]; + boolean label; + int row = DROWS - 1; + + label = (stat_mask & STAT_LABEL) ? 1 : 0; + + if (stat_mask & STAT_LEVEL) { + if (label) { + mvaddstr(row, 0, "Level: "); + } + /* max level taken care of in make_level() */ + mvprintw(row, 7, "%-2d", cur_level); + } + if (stat_mask & STAT_GOLD) { + if (label) { + mvaddstr(row, 10, "Gold: "); + } + if (rogue.gold > MAX_GOLD) { + rogue.gold = MAX_GOLD; + } + mvprintw(row, 16, "%-6ld", rogue.gold); + } + if (stat_mask & STAT_HP) { + if (label) { + mvaddstr(row, 23, "Hp: "); + } + if (rogue.hp_max > MAX_HP) { + rogue.hp_current -= (rogue.hp_max - MAX_HP); + rogue.hp_max = MAX_HP; + } + snprintf(buf, sizeof(buf), "%d(%d)", + rogue.hp_current, rogue.hp_max); + mvprintw(row, 27, "%-8s", buf); + } + if (stat_mask & STAT_STRENGTH) { + if (label) { + mvaddstr(row, 36, "Str: "); + } + if (rogue.str_max > MAX_STRENGTH) { + rogue.str_current -= (rogue.str_max - MAX_STRENGTH); + rogue.str_max = MAX_STRENGTH; + } + snprintf(buf, sizeof(buf), "%d(%d)", + (rogue.str_current + add_strength), rogue.str_max); + mvprintw(row, 41, "%-6s", buf); + } + if (stat_mask & STAT_ARMOR) { + if (label) { + mvaddstr(row, 48, "Arm: "); + } + if (rogue.armor && (rogue.armor->d_enchant > MAX_ARMOR)) { + rogue.armor->d_enchant = MAX_ARMOR; + } + mvprintw(row, 53, "%-2d", get_armor_class(rogue.armor)); + } + if (stat_mask & STAT_EXP) { + if (label) { + mvaddstr(row, 56, "Exp: "); + } + if (rogue.exp_points > MAX_EXP) { + rogue.exp_points = MAX_EXP; + } + if (rogue.exp > MAX_EXP_LEVEL) { + rogue.exp = MAX_EXP_LEVEL; + } + snprintf(buf, sizeof(buf), "%d/%ld", + rogue.exp, rogue.exp_points); + mvprintw(row, 61, "%-11s", buf); + } + if (stat_mask & STAT_HUNGER) { + mvaddstr(row, 73, hunger_str); + clrtoeol(); + } + refresh(); +} + +static void +save_screen(void) +{ + FILE *fp; + short i, j; + char buf[DCOLS+2]; + + if ((fp = fopen(_PATH_SCREENDUMP, "w")) != NULL) { + for (i = 0; i < DROWS; i++) { + for (j=0; j<DCOLS; j++) { + buf[j] = mvinch(i, j); + } + /*buf[DCOLS] = 0; -- redundant */ + for (j=DCOLS; j>0 && buf[j-1]==' '; j--); + buf[j] = 0; + + fputs(buf, fp); + putc('\n', fp); + } + fclose(fp); + } else { + sound_bell(); + } +} + +void +sound_bell(void) +{ + putchar(7); + fflush(stdout); +} + +boolean +is_digit(int ch) +{ + return((ch >= '0') && (ch <= '9')); +} + +int +r_index(const char *str, int ch, boolean last) +{ + int i = 0; + + if (last) { + for (i = strlen(str) - 1; i >= 0; i--) { + if (str[i] == ch) { + return(i); + } + } + } else { + for (i = 0; str[i]; i++) { + if (str[i] == ch) { + return(i); + } + } + } + return(-1); +} diff --git a/rogue/monster.c b/rogue/monster.c new file mode 100644 index 0000000..1d644f6 --- /dev/null +++ b/rogue/monster.c @@ -0,0 +1,886 @@ +/* $NetBSD: monster.c,v 1.17 2013/08/11 03:44:27 dholland Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Timothy C. Stoehr. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)monster.c 8.1 (Berkeley) 5/31/93"; +#else +__RCSID("$NetBSD: monster.c,v 1.17 2013/08/11 03:44:27 dholland Exp $"); +#endif +#endif /* not lint */ + +/* + * monster.c + * + * This source herein may be modified and/or distributed by anybody who + * so desires, with the following restrictions: + * 1.) No portion of this notice shall be removed. + * 2.) Credit shall not be taken for the creation of this source. + * 3.) This code is not to be traded, sold, or used for personal + * gain or profit. + * + */ + +#include "rogue.h" + +object level_monsters; +boolean mon_disappeared; + +const char *const m_names[] = { + "aquator", + "bat", + "centaur", + "dragon", + "emu", + "venus fly-trap", + "griffin", + "hobgoblin", + "ice monster", + "jabberwock", + "kestrel", + "leprechaun", + "medusa", + "nymph", + "orc", + "phantom", + "quagga", + "rattlesnake", + "snake", + "troll", + "black unicorn", + "vampire", + "wraith", + "xeroc", + "yeti", + "zombie" +}; + +#define FILL 0,0,0,0,0,0,0,0,0,0,0,0,0,NULL + +static object mon_tab[MONSTERS] = { + {(ASLEEP|WAKENS|WANDERS|RUSTS),"0d0",25,'A',20,9,18,100,0,0, FILL}, + {(ASLEEP|WANDERS|FLITS|FLIES),"1d3",10,'B',2,1,8,60,0,0, FILL}, + {(ASLEEP|WANDERS),"3d3/2d5",32,'C',15,7,16,85,0,10, FILL}, + {(ASLEEP|WAKENS|FLAMES),"4d6/4d9",145,'D',5000,21,126,100,0,90, FILL}, + {(ASLEEP|WAKENS),"1d3",11,'E',2,1,7,65,0,0, FILL}, + {(HOLDS|STATIONARY),"5d5",73,'F',91,12,126,80,0,0, FILL}, + {(ASLEEP|WAKENS|WANDERS|FLIES),"5d5/5d5",115,'G', + 2000,20,126,85,0,10, FILL}, + {(ASLEEP|WAKENS|WANDERS),"1d3/1d2",15,'H',3,1,10,67,0,0, FILL}, + {(ASLEEP|FREEZES),"0d0",15,'I',5,2,11,68,0,0, FILL}, + {(ASLEEP|WANDERS),"3d10/4d5",132,'J',3000,21,126,100,0,0, FILL}, + {(ASLEEP|WAKENS|WANDERS|FLIES),"1d4",10,'K',2,1,6,60,0,0, FILL}, + {(ASLEEP|STEALS_GOLD),"0d0",25,'L',21,6,16,75,0,0, FILL}, + {(ASLEEP|WAKENS|WANDERS|CONFUSES),"4d4/3d7",97,'M', + 250,18,126,85,0,25, FILL}, + {(ASLEEP|STEALS_ITEM),"0d0",25,'N',39,10,19,75,0,100, FILL}, + {(ASLEEP|WANDERS|WAKENS|SEEKS_GOLD),"1d6",25,'O',5,4,13,70,0,10, FILL}, + {(ASLEEP|INVISIBLE|WANDERS|FLITS),"5d4",76,'P',120,15,24,80,0,50, FILL}, + {(ASLEEP|WAKENS|WANDERS),"3d5",30,'Q',20,8,17,78,0,20, FILL}, + {(ASLEEP|WAKENS|WANDERS|STINGS),"2d5",19,'R',10,3,12,70,0,0, FILL}, + {(ASLEEP|WAKENS|WANDERS),"1d3",8,'S',2,1,9,50,0,0, FILL}, + {(ASLEEP|WAKENS|WANDERS),"4d6/1d4",75,'T',125,13,22,75,0,33, FILL}, + {(ASLEEP|WAKENS|WANDERS),"4d10",90,'U', + 200,17,26,85,0,33, FILL}, + {(ASLEEP|WAKENS|WANDERS|DRAINS_LIFE),"1d14/1d4",55,'V', + 350,19,126,85,0,18, FILL}, + {(ASLEEP|WANDERS|DROPS_LEVEL),"2d8",45,'W',55,14,23,75,0,0, FILL}, + {(ASLEEP|IMITATES),"4d6",42,'X',110,16,25,75,0,0, FILL}, + {(ASLEEP|WANDERS),"3d6",35,'Y',50,11,20,80,0,20, FILL}, + {(ASLEEP|WAKENS|WANDERS),"1d7",21,'Z',8,5,14,69,0,0, FILL} +}; + +static void aim_monster(object *); +static int flit(object *); +static int move_confused(object *); +static int mtry(object *, short, short); +static int no_room_for_monster(int); +static void put_m_at(short, short, object *); +static int rogue_is_around(int, int); + +void +put_mons(void) +{ + short i; + short n; + object *monster; + short row, col; + + n = get_rand(4, 6); + + for (i = 0; i < n; i++) { + monster = gr_monster(NULL, 0); + if ((monster->m_flags & WANDERS) && coin_toss()) { + wake_up(monster); + } + gr_row_col(&row, &col, (FLOOR | TUNNEL | STAIRS | OBJECT)); + put_m_at(row, col, monster); + } +} + +object * +gr_monster(object *monster, int mn) +{ + if (!monster) { + monster = alloc_object(); + + for (;;) { + mn = get_rand(0, MONSTERS-1); + if ((cur_level >= mon_tab[mn].first_level) && + (cur_level <= mon_tab[mn].last_level)) { + break; + } + } + } + *monster = mon_tab[mn]; + if (monster->m_flags & IMITATES) { + monster->disguise = gr_obj_char(); + } + if (cur_level > (AMULET_LEVEL + 2)) { + monster->m_flags |= HASTED; + } + monster->trow = NO_ROOM; + return(monster); +} + +void +mv_mons(void) +{ + object *monster, *next_monster, *test_mons; + boolean flew; + + if (haste_self % 2) { + return; + } + + monster = level_monsters.next_monster; + + while (monster) { + next_monster = monster->next_monster; + mon_disappeared = 0; + if (monster->m_flags & HASTED) { + mv_1_monster(monster, rogue.row, rogue.col); + if (mon_disappeared) { + goto NM; + } + } else if (monster->m_flags & SLOWED) { + monster->slowed_toggle = !monster->slowed_toggle; + if (monster->slowed_toggle) { + goto NM; + } + } + if ((monster->m_flags & CONFUSED) && move_confused(monster)) { + goto NM; + } + flew = 0; + if ( (monster->m_flags & FLIES) && + !(monster->m_flags & NAPPING) && + !mon_can_go(monster, rogue.row, rogue.col)) { + flew = 1; + mv_1_monster(monster, rogue.row, rogue.col); + if (mon_disappeared) { + goto NM; + } + } + if (!(flew && mon_can_go(monster, rogue.row, rogue.col))) { + mv_1_monster(monster, rogue.row, rogue.col); + } +NM: test_mons = level_monsters.next_monster; + monster = NULL; + while (test_mons) + { + if (next_monster == test_mons) + { + monster = next_monster; + break; + } + test_mons = test_mons -> next_monster; + } + } +} + +void +party_monsters(int rn, int n) +{ + short i, j; + short row, col; + object *monster; + boolean found; + + row = col = 0; + n += n; + + for (i = 0; i < MONSTERS; i++) { + mon_tab[i].first_level -= (cur_level % 3); + } + for (i = 0; i < n; i++) { + if (no_room_for_monster(rn)) { + break; + } + for (j = found = 0; ((!found) && (j < 250)); j++) { + row = get_rand(rooms[rn].top_row+1, + rooms[rn].bottom_row-1); + col = get_rand(rooms[rn].left_col+1, + rooms[rn].right_col-1); + if ((!(dungeon[row][col] & MONSTER)) && + (dungeon[row][col] & (FLOOR | TUNNEL))) { + found = 1; + } + } + if (found) { + monster = gr_monster((object *)0, 0); + if (!(monster->m_flags & IMITATES)) { + monster->m_flags |= WAKENS; + } + put_m_at(row, col, monster); + } + } + for (i = 0; i < MONSTERS; i++) { + mon_tab[i].first_level += (cur_level % 3); + } +} + +char +gmc_row_col(int row, int col) +{ + object *monster; + + if ((monster = object_at(&level_monsters, row, col)) != NULL) { + if ((!(detect_monster || see_invisible || r_see_invisible) && + (monster->m_flags & INVISIBLE)) || blind) { + return(monster->trail_char); + } + if (monster->m_flags & IMITATES) { + return(monster->disguise); + } + return(monster->m_char); + } else { + return('&'); /* BUG if this ever happens */ + } +} + +char +gmc(object *monster) +{ + if ((!(detect_monster || see_invisible || r_see_invisible) && + (monster->m_flags & INVISIBLE)) + || blind) { + return(monster->trail_char); + } + if (monster->m_flags & IMITATES) { + return(monster->disguise); + } + return(monster->m_char); +} + +void +mv_1_monster(object *monster, short row, short col) +{ + short i, n; + boolean tried[6]; + + if (monster->m_flags & ASLEEP) { + if (monster->m_flags & NAPPING) { + if (--monster->nap_length <= 0) { + monster->m_flags &= (~(NAPPING | ASLEEP)); + } + return; + } + if ((monster->m_flags & WAKENS) && + rogue_is_around(monster->row, monster->col) && + rand_percent(((stealthy > 0) ? + (WAKE_PERCENT / (STEALTH_FACTOR + stealthy)) : + WAKE_PERCENT))) { + wake_up(monster); + } + return; + } else if (monster->m_flags & ALREADY_MOVED) { + monster->m_flags &= (~ALREADY_MOVED); + return; + } + if ((monster->m_flags & FLITS) && flit(monster)) { + return; + } + if ((monster->m_flags & STATIONARY) && + (!mon_can_go(monster, rogue.row, rogue.col))) { + return; + } + if (monster->m_flags & FREEZING_ROGUE) { + return; + } + if ((monster->m_flags & CONFUSES) && m_confuse(monster)) { + return; + } + if (mon_can_go(monster, rogue.row, rogue.col)) { + mon_hit(monster); + return; + } + if ((monster->m_flags & FLAMES) && flame_broil(monster)) { + return; + } + if ((monster->m_flags & SEEKS_GOLD) && seek_gold(monster)) { + return; + } + if ((monster->trow == monster->row) && + (monster->tcol == monster->col)) { + monster->trow = NO_ROOM; + } else if (monster->trow != NO_ROOM) { + row = monster->trow; + col = monster->tcol; + } + if (monster->row > row) { + row = monster->row - 1; + } else if (monster->row < row) { + row = monster->row + 1; + } + if ((dungeon[row][monster->col] & DOOR) && + mtry(monster, row, monster->col)) { + return; + } + if (monster->col > col) { + col = monster->col - 1; + } else if (monster->col < col) { + col = monster->col + 1; + } + if ((dungeon[monster->row][col] & DOOR) && + mtry(monster, monster->row, col)) { + return; + } + if (mtry(monster, row, col)) { + return; + } + + for (i = 0; i <= 5; i++) tried[i] = 0; + + for (i = 0; i < 6; i++) { +NEXT_TRY: n = get_rand(0, 5); + switch(n) { + case 0: + if (!tried[n] && mtry(monster, row, monster->col-1)) { + goto O; + } + break; + case 1: + if (!tried[n] && mtry(monster, row, monster->col)) { + goto O; + } + break; + case 2: + if (!tried[n] && mtry(monster, row, monster->col+1)) { + goto O; + } + break; + case 3: + if (!tried[n] && mtry(monster, monster->row-1, col)) { + goto O; + } + break; + case 4: + if (!tried[n] && mtry(monster, monster->row, col)) { + goto O; + } + break; + case 5: + if (!tried[n] && mtry(monster, monster->row+1, col)) { + goto O; + } + break; + } + if (!tried[n]) { + tried[n] = 1; + } else { + goto NEXT_TRY; + } + } +O: + if ((monster->row == monster->o_row) && (monster->col == monster->o_col)) { + if (++(monster->o) > 4) { + if ((monster->trow == NO_ROOM) && + (!mon_sees(monster, rogue.row, rogue.col))) { + monster->trow = get_rand(1, (DROWS - 2)); + monster->tcol = get_rand(0, (DCOLS - 1)); + } else { + monster->trow = NO_ROOM; + monster->o = 0; + } + } + } else { + monster->o_row = monster->row; + monster->o_col = monster->col; + monster->o = 0; + } +} + +static int +mtry(object *monster, short row, short col) +{ + if (mon_can_go(monster, row, col)) { + move_mon_to(monster, row, col); + return(1); + } + return(0); +} + +void +move_mon_to(object *monster, short row, short col) +{ + short c; + int mrow, mcol; + + mrow = monster->row; + mcol = monster->col; + + dungeon[mrow][mcol] &= ~MONSTER; + dungeon[row][col] |= MONSTER; + + c = mvinch(mrow, mcol); + + if ((c >= 'A') && (c <= 'Z')) { + if (!detect_monster) { + mvaddch(mrow, mcol, monster->trail_char); + } else { + if (rogue_can_see(mrow, mcol)) { + mvaddch(mrow, mcol, monster->trail_char); + } else { + if (monster->trail_char == '.') { + monster->trail_char = ' '; + } + mvaddch(mrow, mcol, monster->trail_char); + } + } + } + monster->trail_char = mvinch(row, col); + if (!blind && (detect_monster || rogue_can_see(row, col))) { + if ((!(monster->m_flags & INVISIBLE) || + (detect_monster || see_invisible || r_see_invisible))) { + mvaddch(row, col, gmc(monster)); + } + } + if ((dungeon[row][col] & DOOR) && + (get_room_number(row, col) != cur_room) && + (dungeon[mrow][mcol] == FLOOR) && !blind) { + mvaddch(mrow, mcol, ' '); + } + if (dungeon[row][col] & DOOR) { + dr_course(monster, ((dungeon[mrow][mcol] & TUNNEL) ? 1 : 0), + row, col); + } else { + monster->row = row; + monster->col = col; + } +} + +int +mon_can_go(const object *monster, short row, short col) +{ + object *obj; + short dr, dc; + + dr = monster->row - row; /* check if move distance > 1 */ + if ((dr >= 2) || (dr <= -2)) { + return(0); + } + dc = monster->col - col; + if ((dc >= 2) || (dc <= -2)) { + return(0); + } + if ((!dungeon[monster->row][col]) || (!dungeon[row][monster->col])) { + return(0); + } + if ((!is_passable(row, col)) || (dungeon[row][col] & MONSTER)) { + return(0); + } + if ((monster->row!=row)&&(monster->col!=col)&&((dungeon[row][col]&DOOR) || + (dungeon[monster->row][monster->col]&DOOR))) { + return(0); + } + if (!(monster->m_flags & (FLITS | CONFUSED | CAN_FLIT)) && + (monster->trow == NO_ROOM)) { + if ((monster->row < rogue.row) && (row < monster->row)) return(0); + if ((monster->row > rogue.row) && (row > monster->row)) return(0); + if ((monster->col < rogue.col) && (col < monster->col)) return(0); + if ((monster->col > rogue.col) && (col > monster->col)) return(0); + } + if (dungeon[row][col] & OBJECT) { + obj = object_at(&level_objects, row, col); + if ((obj->what_is == SCROL) && (obj->which_kind == SCARE_MONSTER)) { + return(0); + } + } + return(1); +} + +void +wake_up(object *monster) +{ + if (!(monster->m_flags & NAPPING)) { + monster->m_flags &= (~(ASLEEP | IMITATES | WAKENS)); + } +} + +void +wake_room(short rn, boolean entering, short row, short col) +{ + object *monster; + short wake_percent; + boolean in_room; + + wake_percent = (rn == party_room) ? PARTY_WAKE_PERCENT : WAKE_PERCENT; + if (stealthy > 0) { + wake_percent /= (STEALTH_FACTOR + stealthy); + } + + monster = level_monsters.next_monster; + + while (monster) { + in_room = (rn == get_room_number(monster->row, monster->col)); + if (in_room) { + if (entering) { + monster->trow = NO_ROOM; + } else { + monster->trow = row; + monster->tcol = col; + } + } + if ((monster->m_flags & WAKENS) && + (rn == get_room_number(monster->row, monster->col))) { + if (rand_percent(wake_percent)) { + wake_up(monster); + } + } + monster = monster->next_monster; + } +} + +const char * +mon_name(const object *monster) +{ + short ch; + + if (blind || ((monster->m_flags & INVISIBLE) && + !(detect_monster || see_invisible || r_see_invisible))) { + return("something"); + } + if (halluc) { + ch = get_rand('A', 'Z') - 'A'; + return(m_names[ch]); + } + ch = monster->m_char - 'A'; + return(m_names[ch]); +} + +static int +rogue_is_around(int row, int col) +{ + short rdif, cdif, retval; + + rdif = row - rogue.row; + cdif = col - rogue.col; + + retval = (rdif >= -1) && (rdif <= 1) && (cdif >= -1) && (cdif <= 1); + return(retval); +} + +void +wanderer(void) +{ + object *monster; + short row, col, i; + boolean found = 0; + + monster = NULL; /* XXXGCC -Wuninitialized [powerpc] */ + + for (i = 0; ((i < 15) && (!found)); i++) { + monster = gr_monster(NULL, 0); + if (!(monster->m_flags & (WAKENS | WANDERS))) { + free_object(monster); + } else { + found = 1; + } + } + if (found) { + found = 0; + wake_up(monster); + for (i = 0; ((i < 25) && (!found)); i++) { + gr_row_col(&row, &col, (FLOOR | TUNNEL | STAIRS | OBJECT)); + if (!rogue_can_see(row, col)) { + put_m_at(row, col, monster); + found = 1; + } + } + if (!found) { + free_object(monster); + } + } +} + +void +show_monsters(void) +{ + object *monster; + + detect_monster = 1; + + if (blind) { + return; + } + monster = level_monsters.next_monster; + + while (monster) { + mvaddch(monster->row, monster->col, monster->m_char); + if (monster->m_flags & IMITATES) { + monster->m_flags &= (~IMITATES); + monster->m_flags |= WAKENS; + } + monster = monster->next_monster; + } +} + +void +create_monster(void) +{ + short row, col; + short i; + boolean found = 0; + object *monster; + + row = rogue.row; + col = rogue.col; + + for (i = 0; i < 9; i++) { + rand_around(i, &row, &col); + if (((row == rogue.row) && (col == rogue.col)) || + (row < MIN_ROW) || (row > (DROWS-2)) || + (col < 0) || (col > (DCOLS-1))) { + continue; + } + if ((!(dungeon[row][col] & MONSTER)) && + (dungeon[row][col] & (FLOOR|TUNNEL|STAIRS|DOOR))) { + found = 1; + break; + } + } + if (found) { + monster = gr_monster((object *)0, 0); + put_m_at(row, col, monster); + mvaddch(row, col, gmc(monster)); + if (monster->m_flags & (WANDERS | WAKENS)) { + wake_up(monster); + } + } else { + messagef(0, "you hear a faint cry of anguish in the distance"); + } +} + +static void +put_m_at(short row, short col, object *monster) +{ + monster->row = row; + monster->col = col; + dungeon[row][col] |= MONSTER; + monster->trail_char = mvinch(row, col); + (void)add_to_pack(monster, &level_monsters, 0); + aim_monster(monster); +} + +static void +aim_monster(object *monster) +{ + short i, rn, d, r; + + rn = get_room_number(monster->row, monster->col); + if (rn == NO_ROOM) + clean_up("aim_monster: monster not in room"); + r = get_rand(0, 12); + + for (i = 0; i < 4; i++) { + d = (r + i) % 4; + if (rooms[rn].doors[d].oth_room != NO_ROOM) { + monster->trow = rooms[rn].doors[d].door_row; + monster->tcol = rooms[rn].doors[d].door_col; + break; + } + } +} + +int +rogue_can_see(int row, int col) +{ + int retval; + + retval = !blind && + (((get_room_number(row, col) == cur_room) && + !(rooms[cur_room].is_room & R_MAZE)) || + rogue_is_around(row, col)); + + return(retval); +} + +static int +move_confused(object *monster) +{ + short i, row, col; + + if (!(monster->m_flags & ASLEEP)) { + if (--monster->moves_confused <= 0) { + monster->m_flags &= (~CONFUSED); + } + if (monster->m_flags & STATIONARY) { + return(coin_toss() ? 1 : 0); + } else if (rand_percent(15)) { + return(1); + } + row = monster->row; + col = monster->col; + + for (i = 0; i < 9; i++) { + rand_around(i, &row, &col); + if ((row == rogue.row) && (col == rogue.col)) { + return(0); + } + if (mtry(monster, row, col)) { + return(1); + } + } + } + return(0); +} + +static int +flit(object *monster) +{ + short i, row, col; + + if (!rand_percent(FLIT_PERCENT + ((monster->m_flags & FLIES) ? 20 : 0))) { + return(0); + } + if (rand_percent(10)) { + return(1); + } + row = monster->row; + col = monster->col; + + for (i = 0; i < 9; i++) { + rand_around(i, &row, &col); + if ((row == rogue.row) && (col == rogue.col)) { + continue; + } + if (mtry(monster, row, col)) { + return(1); + } + } + return(1); +} + +char +gr_obj_char(void) +{ + short r; + const char *rs = "%!?]=/):*"; + + r = get_rand(0, 8); + + return(rs[r]); +} + +static int +no_room_for_monster(int rn) +{ + short i, j; + + for (i = rooms[rn].top_row+1; i < rooms[rn].bottom_row; i++) { + for (j = rooms[rn].left_col+1; j < rooms[rn].right_col; j++) { + if (!(dungeon[i][j] & MONSTER)) { + return(0); + } + } + } + return(1); +} + +void +aggravate(void) +{ + object *monster; + + messagef(0, "you hear a high pitched humming noise"); + + monster = level_monsters.next_monster; + + while (monster) { + wake_up(monster); + monster->m_flags &= (~IMITATES); + if (rogue_can_see(monster->row, monster->col)) { + mvaddch(monster->row, monster->col, monster->m_char); + } + monster = monster->next_monster; + } +} + +boolean +mon_sees(const object *monster, int row, int col) +{ + short rn, rdif, cdif, retval; + + rn = get_room_number(row, col); + + if ( (rn != NO_ROOM) && + (rn == get_room_number(monster->row, monster->col)) && + !(rooms[rn].is_room & R_MAZE)) { + return(1); + } + rdif = row - monster->row; + cdif = col - monster->col; + + retval = (rdif >= -1) && (rdif <= 1) && (cdif >= -1) && (cdif <= 1); + return(retval); +} + +void +mv_aquatars(void) +{ + object *monster; + + monster = level_monsters.next_monster; + + while (monster) { + if ((monster->m_char == 'A') && + mon_can_go(monster, rogue.row, rogue.col)) { + mv_1_monster(monster, rogue.row, rogue.col); + monster->m_flags |= ALREADY_MOVED; + } + monster = monster->next_monster; + } +} diff --git a/rogue/move.c b/rogue/move.c new file mode 100644 index 0000000..8378654 --- /dev/null +++ b/rogue/move.c @@ -0,0 +1,649 @@ +/* $NetBSD: move.c,v 1.13 2011/05/23 23:01:17 joerg Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Timothy C. Stoehr. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)move.c 8.1 (Berkeley) 5/31/93"; +#else +__RCSID("$NetBSD: move.c,v 1.13 2011/05/23 23:01:17 joerg Exp $"); +#endif +#endif /* not lint */ + +/* + * move.c + * + * This source herein may be modified and/or distributed by anybody who + * so desires, with the following restrictions: + * 1.) No portion of this notice shall be removed. + * 2.) Credit shall not be taken for the creation of this source. + * 3.) This code is not to be traded, sold, or used for personal + * gain or profit. + * + */ + +#include "rogue.h" + +short m_moves = 0; +boolean jump = 0; +const char you_can_move_again[] = "you can move again"; + +static boolean can_turn(short, short); +static boolean check_hunger(boolean); +static char gr_dir(void); +static void heal(void); +static boolean next_to_something(int, int); +static void turn_passage(short, boolean); + +int +one_move_rogue(short dirch, short pickup) +{ + short row, col; + object *obj; + char desc[DCOLS]; + short status, d = 0; /* XXX: GCC */ + + row = rogue.row; + col = rogue.col; + + if (confused) { + dirch = gr_dir(); + } + (void)is_direction(dirch, &d); + get_dir_rc(d, &row, &col, 1); + + if (!can_move(rogue.row, rogue.col, row, col)) { + return(MOVE_FAILED); + } + if (being_held || bear_trap) { + if (!(dungeon[row][col] & MONSTER)) { + if (being_held) { + messagef(1, "you are being held"); + } else { + messagef(0, "you are still stuck in the bear trap"); + (void)reg_move(); + } + return(MOVE_FAILED); + } + } + if (r_teleport) { + if (rand_percent(R_TELE_PERCENT)) { + tele(); + return(STOPPED_ON_SOMETHING); + } + } + if (dungeon[row][col] & MONSTER) { + rogue_hit(object_at(&level_monsters, row, col), 0); + (void)reg_move(); + return(MOVE_FAILED); + } + if (dungeon[row][col] & DOOR) { + if (cur_room == PASSAGE) { + cur_room = get_room_number(row, col); + if (cur_room == NO_ROOM) + clean_up("one_move_rogue: door to nowhere"); + light_up_room(cur_room); + wake_room(cur_room, 1, row, col); + } else { + light_passage(row, col); + } + } else if ((dungeon[rogue.row][rogue.col] & DOOR) && + (dungeon[row][col] & TUNNEL)) { + light_passage(row, col); + wake_room(cur_room, 0, rogue.row, rogue.col); + darken_room(cur_room); + cur_room = PASSAGE; + } else if (dungeon[row][col] & TUNNEL) { + light_passage(row, col); + } + mvaddch(rogue.row, rogue.col, get_dungeon_char(rogue.row, rogue.col)); + mvaddch(row, col, rogue.fchar); + + if (!jump) { + refresh(); + } + rogue.row = row; + rogue.col = col; + if (dungeon[row][col] & OBJECT) { + if (levitate && pickup) { + return(STOPPED_ON_SOMETHING); + } + if (pickup && !levitate) { + if ((obj = pick_up(row, col, &status)) != NULL) { + get_desc(obj, desc, sizeof(desc)); + if (obj->what_is == GOLD) { + free_object(obj); + messagef(1, "%s", desc); + goto NOT_IN_PACK; + } + } else if (!status) { + goto MVED; + } else { + goto MOVE_ON; + } + } else { +MOVE_ON: + obj = object_at(&level_objects, row, col); + get_desc(obj, desc, sizeof(desc)); + messagef(1, "moved onto %s", desc); + goto NOT_IN_PACK; + } + messagef(1, "%s(%c)", desc, obj->ichar); +NOT_IN_PACK: + (void)reg_move(); + return(STOPPED_ON_SOMETHING); + } + if (dungeon[row][col] & (DOOR | STAIRS | TRAP)) { + if ((!levitate) && (dungeon[row][col] & TRAP)) { + trap_player(row, col); + } + (void)reg_move(); + return(STOPPED_ON_SOMETHING); + } +MVED: if (reg_move()) { /* fainted from hunger */ + return(STOPPED_ON_SOMETHING); + } + return((confused ? STOPPED_ON_SOMETHING : MOVED)); +} + +void +multiple_move_rogue(short dirch) +{ + short row, col; + short m; + + switch(dirch) { + case '\010': + case '\012': + case '\013': + case '\014': + case '\031': + case '\025': + case '\016': + case '\002': + do { + row = rogue.row; + col = rogue.col; + if (((m = one_move_rogue((dirch + 96), 1)) == MOVE_FAILED) || + (m == STOPPED_ON_SOMETHING) || + interrupted) { + break; + } + } while (!next_to_something(row, col)); + if ( (!interrupted) && passgo && (m == MOVE_FAILED) && + (dungeon[rogue.row][rogue.col] & TUNNEL)) { + turn_passage(dirch + 96, 0); + } + break; + case 'H': + case 'J': + case 'K': + case 'L': + case 'B': + case 'Y': + case 'U': + case 'N': + while ((!interrupted) && (one_move_rogue((dirch + 32), 1) == MOVED)) + ; + + if ( (!interrupted) && passgo && + (dungeon[rogue.row][rogue.col] & TUNNEL)) { + turn_passage(dirch + 32, 1); + } + break; + } +} + +boolean +is_passable(int row, int col) +{ + if ((row < MIN_ROW) || (row > (DROWS - 2)) || (col < 0) || + (col > (DCOLS-1))) { + return(0); + } + if (dungeon[row][col] & HIDDEN) { + return((dungeon[row][col] & TRAP) ? 1 : 0); + } + return(dungeon[row][col] & (FLOOR | TUNNEL | DOOR | STAIRS | TRAP)); +} + +static boolean +next_to_something(int drow, int dcol) +{ + short i, j, i_end, j_end, row, col; + short pass_count = 0; + unsigned short s; + + if (confused) { + return(1); + } + if (blind) { + return(0); + } + i_end = (rogue.row < (DROWS-2)) ? 1 : 0; + j_end = (rogue.col < (DCOLS-1)) ? 1 : 0; + + for (i = ((rogue.row > MIN_ROW) ? -1 : 0); i <= i_end; i++) { + for (j = ((rogue.col > 0) ? -1 : 0); j <= j_end; j++) { + if ((i == 0) && (j == 0)) { + continue; + } + if (((rogue.row+i) == drow) && ((rogue.col+j) == dcol)) { + continue; + } + row = rogue.row + i; + col = rogue.col + j; + s = dungeon[row][col]; + if (s & HIDDEN) { + continue; + } + /* If the rogue used to be right, up, left, down, or right of + * row,col, and now isn't, then don't stop */ + if (s & (MONSTER | OBJECT | STAIRS)) { + if (((row == drow) || (col == dcol)) && + (!((row == rogue.row) || (col == rogue.col)))) { + continue; + } + return(1); + } + if (s & TRAP) { + if (!(s & HIDDEN)) { + if (((row == drow) || (col == dcol)) && + (!((row == rogue.row) || (col == rogue.col)))) { + continue; + } + return(1); + } + } + if ((((i - j) == 1) || ((i - j) == -1)) && (s & TUNNEL)) { + if (++pass_count > 1) { + return(1); + } + } + if ((s & DOOR) && ((i == 0) || (j == 0))) { + return(1); + } + } + } + return(0); +} + +boolean +can_move(int row1, int col1, int row2, int col2) +{ + if (!is_passable(row2, col2)) { + return(0); + } + if ((row1 != row2) && (col1 != col2)) { + if ((dungeon[row1][col1] & DOOR) || (dungeon[row2][col2] & DOOR)) { + return(0); + } + if ((!dungeon[row1][col2]) || (!dungeon[row2][col1])) { + return(0); + } + } + return(1); +} + +void +move_onto(void) +{ + short ch, d; + boolean first_miss = 1; + + while (!is_direction(ch = rgetchar(), &d)) { + sound_bell(); + if (first_miss) { + messagef(0, "direction? "); + first_miss = 0; + } + } + check_message(); + if (ch != CANCEL) { + (void)one_move_rogue(ch, 0); + } +} + +boolean +is_direction(short c, short *d) +{ + switch(c) { + case 'h': + *d = LEFT; + break; + case 'j': + *d = DOWN; + break; + case 'k': + *d = UPWARD; + break; + case 'l': + *d = RIGHT; + break; + case 'b': + *d = DOWNLEFT; + break; + case 'y': + *d = UPLEFT; + break; + case 'u': + *d = UPRIGHT; + break; + case 'n': + *d = DOWNRIGHT; + break; + case CANCEL: + break; + default: + return(0); + } + return(1); +} + +static boolean +check_hunger(boolean msg_only) +{ + short i, n; + boolean fainted = 0; + + if (rogue.moves_left == HUNGRY) { + (void)strlcpy(hunger_str, "hungry", sizeof(hunger_str)); + messagef(0, "%s", hunger_str); + print_stats(STAT_HUNGER); + } + if (rogue.moves_left == WEAK) { + (void)strlcpy(hunger_str, "weak", sizeof(hunger_str)); + messagef(1, "%s", hunger_str); + print_stats(STAT_HUNGER); + } + if (rogue.moves_left <= FAINT) { + if (rogue.moves_left == FAINT) { + (void)strlcpy(hunger_str, "faint", sizeof(hunger_str)); + messagef(1, "%s", hunger_str); + print_stats(STAT_HUNGER); + } + n = get_rand(0, (FAINT - rogue.moves_left)); + if (n > 0) { + fainted = 1; + if (rand_percent(40)) { + rogue.moves_left++; + } + messagef(1, "you faint"); + for (i = 0; i < n; i++) { + if (coin_toss()) { + mv_mons(); + } + } + messagef(1, "%s", you_can_move_again); + } + } + if (msg_only) { + return(fainted); + } + if (rogue.moves_left <= STARVE) { + killed_by(NULL, STARVATION); + } + + switch(e_rings) { + /*case -2: + Subtract 0, i.e. do nothing. + break;*/ + case -1: + rogue.moves_left -= (rogue.moves_left % 2); + break; + case 0: + rogue.moves_left--; + break; + case 1: + rogue.moves_left--; + (void)check_hunger(1); + rogue.moves_left -= (rogue.moves_left % 2); + break; + case 2: + rogue.moves_left--; + (void)check_hunger(1); + rogue.moves_left--; + break; + } + return(fainted); +} + +boolean +reg_move(void) +{ + boolean fainted; + + if ((rogue.moves_left <= HUNGRY) || (cur_level >= max_level)) { + fainted = check_hunger(0); + } else { + fainted = 0; + } + + mv_mons(); + + if (++m_moves >= 120) { + m_moves = 0; + wanderer(); + } + if (halluc) { + if (!(--halluc)) { + unhallucinate(); + } else { + hallucinate(); + } + } + if (blind) { + if (!(--blind)) { + unblind(); + } + } + if (confused) { + if (!(--confused)) { + unconfuse(); + } + } + if (bear_trap) { + bear_trap--; + } + if (levitate) { + if (!(--levitate)) { + messagef(1, "you float gently to the ground"); + if (dungeon[rogue.row][rogue.col] & TRAP) { + trap_player(rogue.row, rogue.col); + } + } + } + if (haste_self) { + if (!(--haste_self)) { + messagef(0, "you feel yourself slowing down"); + } + } + heal(); + if (auto_search > 0) { + search(auto_search, auto_search); + } + return(fainted); +} + +void +rest(int count) +{ + int i; + + interrupted = 0; + + for (i = 0; i < count; i++) { + if (interrupted) { + break; + } + (void)reg_move(); + } +} + +static char +gr_dir(void) +{ + short d; + + d = get_rand(1, 8); + + switch(d) { + case 1: + d = 'j'; + break; + case 2: + d = 'k'; + break; + case 3: + d = 'l'; + break; + case 4: + d = 'h'; + break; + case 5: + d = 'y'; + break; + case 6: + d = 'u'; + break; + case 7: + d = 'b'; + break; + case 8: + d = 'n'; + break; + } + return(d); +} + +static void +heal(void) +{ + static short heal_exp = -1, n, c = 0; + static boolean alt; + + if (rogue.hp_current == rogue.hp_max) { + c = 0; + return; + } + if (rogue.exp != heal_exp) { + heal_exp = rogue.exp; + + switch(heal_exp) { + case 1: + n = 20; + break; + case 2: + n = 18; + break; + case 3: + n = 17; + break; + case 4: + n = 14; + break; + case 5: + n = 13; + break; + case 6: + n = 10; + break; + case 7: + n = 9; + break; + case 8: + n = 8; + break; + case 9: + n = 7; + break; + case 10: + n = 4; + break; + case 11: + n = 3; + break; + case 12: + default: + n = 2; + } + } + if (++c >= n) { + c = 0; + rogue.hp_current++; + if ((alt = !alt) != 0) { + rogue.hp_current++; + } + if ((rogue.hp_current += regeneration) > rogue.hp_max) { + rogue.hp_current = rogue.hp_max; + } + print_stats(STAT_HP); + } +} + +static boolean +can_turn(short nrow, short ncol) +{ + if ((dungeon[nrow][ncol] & TUNNEL) && is_passable(nrow, ncol)) { + return(1); + } + return(0); +} + +static void +turn_passage(short dir, boolean fast) +{ + short crow = rogue.row, ccol = rogue.col, turns = 0; + short ndir = 0; + + if ((dir != 'h') && can_turn(crow, ccol + 1)) { + turns++; + ndir = 'l'; + } + if ((dir != 'l') && can_turn(crow, ccol - 1)) { + turns++; + ndir = 'h'; + } + if ((dir != 'k') && can_turn(crow + 1, ccol)) { + turns++; + ndir = 'j'; + } + if ((dir != 'j') && can_turn(crow - 1, ccol)) { + turns++; + ndir = 'k'; + } + if (turns == 1) { + multiple_move_rogue(ndir - (fast ? 32 : 96)); + } +} diff --git a/rogue/object.c b/rogue/object.c new file mode 100644 index 0000000..9897ec6 --- /dev/null +++ b/rogue/object.c @@ -0,0 +1,802 @@ +/* $NetBSD: object.c,v 1.14 2009/08/12 08:44:45 dholland Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Timothy C. Stoehr. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)object.c 8.1 (Berkeley) 5/31/93"; +#else +__RCSID("$NetBSD: object.c,v 1.14 2009/08/12 08:44:45 dholland Exp $"); +#endif +#endif /* not lint */ + +/* + * object.c + * + * This source herein may be modified and/or distributed by anybody who + * so desires, with the following restrictions: + * 1.) No portion of this notice shall be removed. + * 2.) Credit shall not be taken for the creation of this source. + * 3.) This code is not to be traded, sold, or used for personal + * gain or profit. + * + */ + +#include "rogue.h" + +object level_objects; +unsigned short dungeon[DROWS][DCOLS]; +short foods = 0; +char *fruit = NULL; + +static object *free_list = NULL; + +fighter rogue = { + INIT_AW, /* armor */ + INIT_AW, /* weapon */ + INIT_RINGS, /* left ring */ + INIT_RINGS, /* right ring */ + INIT_HP, /* Hp current */ + INIT_HP, /* Hp max */ + INIT_STR, /* Str current */ + INIT_STR, /* Str max */ + INIT_PACK, /* pack */ + INIT_GOLD, /* gold */ + INIT_EXPLEVEL, /* exp level */ + INIT_EXP, /* exp points */ + 0, 0, /* row, col */ + INIT_CHAR, /* char */ + INIT_MOVES /* moves */ +}; + +struct id id_potions[POTIONS] = { +{100, "blue ", "of increase strength ", 0}, +{250, "red ", "of restore strength ", 0}, +{100, "green ", "of healing ", 0}, +{200, "grey ", "of extra healing ", 0}, + {10, "brown ", "of poison ", 0}, +{300, "clear ", "of raise level ", 0}, + {10, "pink ", "of blindness ", 0}, + {25, "white ", "of hallucination ", 0}, +{100, "purple ", "of detect monster ", 0}, +{100, "black ", "of detect things ", 0}, + {10, "yellow ", "of confusion ", 0}, + {80, "plaid ", "of levitation ", 0}, +{150, "burgundy ", "of haste self ", 0}, +{145, "beige ", "of see invisible ", 0} +}; + +struct id id_scrolls[SCROLS] = { +{505, "", "of protect armor ", 0}, +{200, "", "of hold monster ", 0}, +{235, "", "of enchant weapon ", 0}, +{235, "", "of enchant armor ", 0}, +{175, "", "of identify ", 0}, +{190, "", "of teleportation ", 0}, + {25, "", "of sleep ", 0}, +{610, "", "of scare monster ", 0}, +{210, "", "of remove curse ", 0}, + {80, "", "of create monster ",0}, + {25, "", "of aggravate monster ",0}, +{180, "", "of magic mapping ", 0}, + {90, "", "of confuse monster ", 0} +}; + +struct id id_weapons[WEAPONS] = { + {150, "short bow ", "", 0}, + {8, "darts ", "", 0}, + {15, "arrows ", "", 0}, + {27, "daggers ", "", 0}, + {35, "shurikens ", "", 0}, + {360, "mace ", "", 0}, + {470, "long sword ", "", 0}, + {580, "two-handed sword ", "", 0} +}; + +struct id id_armors[ARMORS] = { + {300, "leather armor ", "", (UNIDENTIFIED)}, + {300, "ring mail ", "", (UNIDENTIFIED)}, + {400, "scale mail ", "", (UNIDENTIFIED)}, + {500, "chain mail ", "", (UNIDENTIFIED)}, + {600, "banded mail ", "", (UNIDENTIFIED)}, + {600, "splint mail ", "", (UNIDENTIFIED)}, + {700, "plate mail ", "", (UNIDENTIFIED)} +}; + +struct id id_wands[WANDS] = { + {25, "", "of teleport away ",0}, + {50, "", "of slow monster ", 0}, + {8, "", "of invisibility ",0}, + {55, "", "of polymorph ",0}, + {2, "", "of haste monster ",0}, + {20, "", "of magic missile ",0}, + {20, "", "of cancellation ",0}, + {0, "", "of do nothing ",0}, + {35, "", "of drain life ",0}, + {20, "", "of cold ",0}, + {20, "", "of fire ",0} +}; + +struct id id_rings[RINGS] = { + {250, "", "of stealth ",0}, + {100, "", "of teleportation ", 0}, + {255, "", "of regeneration ",0}, + {295, "", "of slow digestion ",0}, + {200, "", "of add strength ",0}, + {250, "", "of sustain strength ",0}, + {250, "", "of dexterity ",0}, + {25, "", "of adornment ",0}, + {300, "", "of see invisible ",0}, + {290, "", "of maintain armor ",0}, + {270, "", "of searching ",0}, +}; + +static void gr_armor(object *); +static void gr_potion(object *); +static void gr_scroll(object *); +static void gr_wand(object *); +static void gr_weapon(object *, int); +static unsigned short gr_what_is(void); +static void make_party(void); +static void plant_gold(short, short, boolean); +static void put_gold(void); +static void rand_place(object *); + +void +put_objects(void) +{ + short i, n; + object *obj; + + if (cur_level < max_level) { + return; + } + n = coin_toss() ? get_rand(2, 4) : get_rand(3, 5); + while (rand_percent(33)) { + n++; + } + if (party_room != NO_ROOM) { + make_party(); + } + for (i = 0; i < n; i++) { + obj = gr_object(); + rand_place(obj); + } + put_gold(); +} + +static void +put_gold(void) +{ + short i, j; + short row,col; + boolean is_maze, is_room; + + for (i = 0; i < MAXROOMS; i++) { + is_maze = (rooms[i].is_room & R_MAZE) ? 1 : 0; + is_room = (rooms[i].is_room & R_ROOM) ? 1 : 0; + + if (!(is_room || is_maze)) { + continue; + } + if (is_maze || rand_percent(GOLD_PERCENT)) { + for (j = 0; j < 50; j++) { + row = get_rand(rooms[i].top_row+1, + rooms[i].bottom_row-1); + col = get_rand(rooms[i].left_col+1, + rooms[i].right_col-1); + if ((dungeon[row][col] == FLOOR) || + (dungeon[row][col] == TUNNEL)) { + plant_gold(row, col, is_maze); + break; + } + } + } + } +} + +static void +plant_gold(short row, short col, boolean is_maze) +{ + object *obj; + + obj = alloc_object(); + obj->row = row; obj->col = col; + obj->what_is = GOLD; + obj->quantity = get_rand((2 * cur_level), (16 * cur_level)); + if (is_maze) { + obj->quantity += obj->quantity / 2; + } + dungeon[row][col] |= OBJECT; + (void)add_to_pack(obj, &level_objects, 0); +} + +void +place_at(object *obj, int row, int col) +{ + obj->row = row; + obj->col = col; + dungeon[row][col] |= OBJECT; + (void)add_to_pack(obj, &level_objects, 0); +} + +object * +object_at(object *pack, short row, short col) +{ + object *obj = NULL; + + if (dungeon[row][col] & (MONSTER | OBJECT)) { + obj = pack->next_object; + + while (obj && ((obj->row != row) || (obj->col != col))) { + obj = obj->next_object; + } + if (!obj) { + messagef(1, "object_at(): inconsistent"); + } + } + return(obj); +} + +object * +get_letter_object(int ch) +{ + object *obj; + + obj = rogue.pack.next_object; + + while (obj && (obj->ichar != ch)) { + obj = obj->next_object; + } + return(obj); +} + +void +free_stuff(object *objlist) +{ + object *obj; + + while (objlist->next_object) { + obj = objlist->next_object; + objlist->next_object = + objlist->next_object->next_object; + free_object(obj); + } +} + +const char * +name_of(const object *obj) +{ + const char *retstring; + + switch(obj->what_is) { + case SCROL: + retstring = obj->quantity > 1 ? "scrolls " : "scroll "; + break; + case POTION: + retstring = obj->quantity > 1 ? "potions " : "potion "; + break; + case FOOD: + if (obj->which_kind == RATION) { + retstring = "food "; + } else { + retstring = fruit; + } + break; + case WAND: + retstring = is_wood[obj->which_kind] ? "staff " : "wand "; + break; + case WEAPON: + switch(obj->which_kind) { + case DART: + retstring=obj->quantity > 1 ? "darts " : "dart "; + break; + case ARROW: + retstring=obj->quantity > 1 ? "arrows " : "arrow "; + break; + case DAGGER: + retstring=obj->quantity > 1 ? "daggers " : "dagger "; + break; + case SHURIKEN: + retstring=obj->quantity > 1?"shurikens ":"shuriken "; + break; + default: + retstring = id_weapons[obj->which_kind].title; + } + break; + case ARMOR: + retstring = "armor "; + break; + case RING: + retstring = "ring "; + break; + case AMULET: + retstring = "amulet "; + break; + default: + retstring = "unknown "; + break; + } + return(retstring); +} + +object * +gr_object(void) +{ + object *obj; + + obj = alloc_object(); + + if (foods < (cur_level / 3)) { + obj->what_is = FOOD; + foods++; + } else { + obj->what_is = gr_what_is(); + } + switch(obj->what_is) { + case SCROL: + gr_scroll(obj); + break; + case POTION: + gr_potion(obj); + break; + case WEAPON: + gr_weapon(obj, 1); + break; + case ARMOR: + gr_armor(obj); + break; + case WAND: + gr_wand(obj); + break; + case FOOD: + get_food(obj, 0); + break; + case RING: + gr_ring(obj, 1); + break; + } + return(obj); +} + +static unsigned short +gr_what_is(void) +{ + short percent; + unsigned short what_is; + + percent = get_rand(1, 91); + + if (percent <= 30) { + what_is = SCROL; + } else if (percent <= 60) { + what_is = POTION; + } else if (percent <= 64) { + what_is = WAND; + } else if (percent <= 74) { + what_is = WEAPON; + } else if (percent <= 83) { + what_is = ARMOR; + } else if (percent <= 88) { + what_is = FOOD; + } else { + what_is = RING; + } + return(what_is); +} + +static void +gr_scroll(object *obj) +{ + short percent; + + percent = get_rand(0, 91); + + obj->what_is = SCROL; + + if (percent <= 5) { + obj->which_kind = PROTECT_ARMOR; + } else if (percent <= 10) { + obj->which_kind = HOLD_MONSTER; + } else if (percent <= 20) { + obj->which_kind = CREATE_MONSTER; + } else if (percent <= 35) { + obj->which_kind = IDENTIFY; + } else if (percent <= 43) { + obj->which_kind = TELEPORT; + } else if (percent <= 50) { + obj->which_kind = SLEEP; + } else if (percent <= 55) { + obj->which_kind = SCARE_MONSTER; + } else if (percent <= 64) { + obj->which_kind = REMOVE_CURSE; + } else if (percent <= 69) { + obj->which_kind = ENCH_ARMOR; + } else if (percent <= 74) { + obj->which_kind = ENCH_WEAPON; + } else if (percent <= 80) { + obj->which_kind = AGGRAVATE_MONSTER; + } else if (percent <= 86) { + obj->which_kind = CON_MON; + } else { + obj->which_kind = MAGIC_MAPPING; + } +} + +static void +gr_potion(object *obj) +{ + short percent; + + percent = get_rand(1, 118); + + obj->what_is = POTION; + + if (percent <= 5) { + obj->which_kind = RAISE_LEVEL; + } else if (percent <= 15) { + obj->which_kind = DETECT_OBJECTS; + } else if (percent <= 25) { + obj->which_kind = DETECT_MONSTER; + } else if (percent <= 35) { + obj->which_kind = INCREASE_STRENGTH; + } else if (percent <= 45) { + obj->which_kind = RESTORE_STRENGTH; + } else if (percent <= 55) { + obj->which_kind = HEALING; + } else if (percent <= 65) { + obj->which_kind = EXTRA_HEALING; + } else if (percent <= 75) { + obj->which_kind = BLINDNESS; + } else if (percent <= 85) { + obj->which_kind = HALLUCINATION; + } else if (percent <= 95) { + obj->which_kind = CONFUSION; + } else if (percent <= 105) { + obj->which_kind = POISON; + } else if (percent <= 110) { + obj->which_kind = LEVITATION; + } else if (percent <= 114) { + obj->which_kind = HASTE_SELF; + } else { + obj->which_kind = SEE_INVISIBLE; + } +} + +static void +gr_weapon(object *obj, int assign_wk) +{ + short percent; + short i; + short blessing, increment; + + obj->what_is = WEAPON; + if (assign_wk) { + obj->which_kind = get_rand(0, (WEAPONS - 1)); + } + if ((obj->which_kind == ARROW) || (obj->which_kind == DAGGER) || + (obj->which_kind == SHURIKEN) || (obj->which_kind == DART)) { + obj->quantity = get_rand(3, 15); + obj->quiver = get_rand(0, 126); + } else { + obj->quantity = 1; + } + obj->hit_enchant = obj->d_enchant = 0; + + percent = get_rand(1, 96); + blessing = get_rand(1, 3); + + if (percent <= 32) { + if (percent <= 16) { + increment = 1; + } else { + increment = -1; + obj->is_cursed = 1; + } + for (i = 0; i < blessing; i++) { + if (coin_toss()) { + obj->hit_enchant += increment; + } else { + obj->d_enchant += increment; + } + } + } + switch(obj->which_kind) { + case BOW: + case DART: + obj->damage = "1d1"; + break; + case ARROW: + obj->damage = "1d2"; + break; + case DAGGER: + obj->damage = "1d3"; + break; + case SHURIKEN: + obj->damage = "1d4"; + break; + case MACE: + obj->damage = "2d3"; + break; + case LONG_SWORD: + obj->damage = "3d4"; + break; + case TWO_HANDED_SWORD: + obj->damage = "4d5"; + break; + } +} + +static void +gr_armor(object *obj) +{ + short percent; + short blessing; + + obj->what_is = ARMOR; + obj->which_kind = get_rand(0, (ARMORS - 1)); + obj->class = obj->which_kind + 2; + if ((obj->which_kind == PLATE) || (obj->which_kind == SPLINT)) { + obj->class--; + } + obj->is_protected = 0; + obj->d_enchant = 0; + + percent = get_rand(1, 100); + blessing = get_rand(1, 3); + + if (percent <= 16) { + obj->is_cursed = 1; + obj->d_enchant -= blessing; + } else if (percent <= 33) { + obj->d_enchant += blessing; + } +} + +static void +gr_wand(object *obj) +{ + obj->what_is = WAND; + obj->which_kind = get_rand(0, (WANDS - 1)); + obj->class = get_rand(3, 7); +} + +void +get_food(object *obj, boolean force_ration) +{ + obj->what_is = FOOD; + + if (force_ration || rand_percent(80)) { + obj->which_kind = RATION; + } else { + obj->which_kind = FRUIT; + } +} + +void +put_stairs(void) +{ + short row, col; + + gr_row_col(&row, &col, (FLOOR | TUNNEL)); + dungeon[row][col] |= STAIRS; +} + +int +get_armor_class(const object *obj) +{ + if (obj) { + return(obj->class + obj->d_enchant); + } + return(0); +} + +object * +alloc_object(void) +{ + object *obj; + + if (free_list) { + obj = free_list; + free_list = free_list->next_object; + } else if (!(obj = md_malloc(sizeof(object)))) { + messagef(0, "cannot allocate object, saving game"); + save_into_file(error_file); + clean_up("alloc_object: save failed"); + } + obj->quantity = 1; + obj->ichar = 'L'; + obj->picked_up = obj->is_cursed = 0; + obj->in_use_flags = NOT_USED; + obj->identified = UNIDENTIFIED; + obj->damage = "1d1"; + return(obj); +} + +void +free_object(object *obj) +{ + obj->next_object = free_list; + free_list = obj; +} + +static void +make_party(void) +{ + short n; + + party_room = gr_room(); + + n = rand_percent(99) ? party_objects(party_room) : 11; + if (rand_percent(99)) { + party_monsters(party_room, n); + } +} + +void +show_objects(void) +{ + object *obj; + short mc, rc, row, col; + object *monster; + + obj = level_objects.next_object; + + while (obj) { + row = obj->row; + col = obj->col; + + rc = get_mask_char(obj->what_is); + + if (dungeon[row][col] & MONSTER) { + if ((monster = + object_at(&level_monsters, row, col)) != NULL) { + monster->trail_char = rc; + } + } + mc = mvinch(row, col); + if (((mc < 'A') || (mc > 'Z')) && + ((row != rogue.row) || (col != rogue.col))) { + mvaddch(row, col, rc); + } + obj = obj->next_object; + } + + monster = level_monsters.next_object; + + while (monster) { + if (monster->m_flags & IMITATES) { + mvaddch(monster->row, monster->col, (int)monster->disguise); + } + monster = monster->next_monster; + } +} + +void +put_amulet(void) +{ + object *obj; + + obj = alloc_object(); + obj->what_is = AMULET; + rand_place(obj); +} + +static void +rand_place(object *obj) +{ + short row, col; + + gr_row_col(&row, &col, (FLOOR | TUNNEL)); + place_at(obj, row, col); +} + +void +c_object_for_wizard(void) +{ + short ch, max, wk; + object *obj; + char buf[80]; + + max = 0; + if (pack_count(NULL) >= MAX_PACK_COUNT) { + messagef(0, "pack full"); + return; + } + messagef(0, "type of object?"); + + while (r_index("!?:)]=/,\033", (ch = rgetchar()), 0) == -1) { + sound_bell(); + } + check_message(); + + if (ch == '\033') { + return; + } + obj = alloc_object(); + + switch(ch) { + case '!': + obj->what_is = POTION; + max = POTIONS - 1; + break; + case '?': + obj->what_is = SCROL; + max = SCROLS - 1; + break; + case ',': + obj->what_is = AMULET; + break; + case ':': + get_food(obj, 0); + break; + case ')': + gr_weapon(obj, 0); + max = WEAPONS - 1; + break; + case ']': + gr_armor(obj); + max = ARMORS - 1; + break; + case '/': + gr_wand(obj); + max = WANDS - 1; + break; + case '=': + max = RINGS - 1; + obj->what_is = RING; + break; + } + if ((ch != ',') && (ch != ':')) { +GIL: + if (get_input_line("which kind?", "", buf, sizeof(buf), "", 0, 1)) { + wk = get_number(buf); + if ((wk >= 0) && (wk <= max)) { + obj->which_kind = wk; + if (obj->what_is == RING) { + gr_ring(obj, 0); + } + } else { + sound_bell(); + goto GIL; + } + } else { + free_object(obj); + return; + } + } + get_desc(obj, buf, sizeof(buf)); + messagef(0, "%s", buf); + (void)add_to_pack(obj, &rogue.pack, 1); +} diff --git a/rogue/pack.c b/rogue/pack.c new file mode 100644 index 0000000..f8aed6e --- /dev/null +++ b/rogue/pack.c @@ -0,0 +1,574 @@ +/* $NetBSD: pack.c,v 1.12 2011/05/23 23:01:17 joerg Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Timothy C. Stoehr. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)pack.c 8.1 (Berkeley) 5/31/93"; +#else +__RCSID("$NetBSD: pack.c,v 1.12 2011/05/23 23:01:17 joerg Exp $"); +#endif +#endif /* not lint */ + +/* + * pack.c + * + * This source herein may be modified and/or distributed by anybody who + * so desires, with the following restrictions: + * 1.) No portion of this notice shall be removed. + * 2.) Credit shall not be taken for the creation of this source. + * 3.) This code is not to be traded, sold, or used for personal + * gain or profit. + * + */ + +#include "rogue.h" + +const char curse_message[] = "you can't, it appears to be cursed"; + +static object *check_duplicate(object *, object *); +static boolean is_pack_letter(short *, unsigned short *); +static boolean mask_pack(const object *, unsigned short); +static short next_avail_ichar(void); + +object * +add_to_pack(object *obj, object *pack, int condense) +{ + object *op; + + if (condense) { + if ((op = check_duplicate(obj, pack)) != NULL) { + free_object(obj); + return(op); + } else { + obj->ichar = next_avail_ichar(); + } + } + if (pack->next_object == 0) { + pack->next_object = obj; + } else { + op = pack->next_object; + + while (op->next_object) { + op = op->next_object; + } + op->next_object = obj; + } + obj->next_object = 0; + return(obj); +} + +void +take_from_pack(object *obj, object *pack) +{ + while (pack->next_object != obj) { + pack = pack->next_object; + } + pack->next_object = pack->next_object->next_object; +} + +/* Note: *status is set to 0 if the rogue attempts to pick up a scroll + * of scare-monster and it turns to dust. *status is otherwise set to 1. + */ + +object * +pick_up(int row, int col, short *status) +{ + object *obj; + + *status = 1; + + if (levitate) { + messagef(0, "you're floating in the air!"); + return NULL; + } + obj = object_at(&level_objects, row, col); + if (!obj) { + messagef(1, "pick_up(): inconsistent"); + return(obj); + } + if ( (obj->what_is == SCROL) && + (obj->which_kind == SCARE_MONSTER) && + obj->picked_up) { + messagef(0, "the scroll turns to dust as you pick it up"); + dungeon[row][col] &= (~OBJECT); + vanish(obj, 0, &level_objects); + *status = 0; + if (id_scrolls[SCARE_MONSTER].id_status == UNIDENTIFIED) { + id_scrolls[SCARE_MONSTER].id_status = IDENTIFIED; + } + return NULL; + } + if (obj->what_is == GOLD) { + rogue.gold += obj->quantity; + dungeon[row][col] &= ~(OBJECT); + take_from_pack(obj, &level_objects); + print_stats(STAT_GOLD); + return(obj); /* obj will be free_object()ed in caller */ + } + if (pack_count(obj) >= MAX_PACK_COUNT) { + messagef(1, "pack too full"); + return NULL; + } + dungeon[row][col] &= ~(OBJECT); + take_from_pack(obj, &level_objects); + obj = add_to_pack(obj, &rogue.pack, 1); + obj->picked_up = 1; + return(obj); +} + +void +drop(void) +{ + object *obj, *new; + short ch; + char desc[DCOLS]; + + if (dungeon[rogue.row][rogue.col] & (OBJECT | STAIRS | TRAP)) { + messagef(0, "there's already something there"); + return; + } + if (!rogue.pack.next_object) { + messagef(0, "you have nothing to drop"); + return; + } + if ((ch = pack_letter("drop what?", ALL_OBJECTS)) == CANCEL) { + return; + } + if (!(obj = get_letter_object(ch))) { + messagef(0, "no such item."); + return; + } + if (obj->in_use_flags & BEING_WIELDED) { + if (obj->is_cursed) { + messagef(0, "%s", curse_message); + return; + } + unwield(rogue.weapon); + } else if (obj->in_use_flags & BEING_WORN) { + if (obj->is_cursed) { + messagef(0, "%s", curse_message); + return; + } + mv_aquatars(); + unwear(rogue.armor); + print_stats(STAT_ARMOR); + } else if (obj->in_use_flags & ON_EITHER_HAND) { + if (obj->is_cursed) { + messagef(0, "%s", curse_message); + return; + } + un_put_on(obj); + } + obj->row = rogue.row; + obj->col = rogue.col; + + if ((obj->quantity > 1) && (obj->what_is != WEAPON)) { + obj->quantity--; + new = alloc_object(); + *new = *obj; + new->quantity = 1; + obj = new; + } else { + obj->ichar = 'L'; + take_from_pack(obj, &rogue.pack); + } + place_at(obj, rogue.row, rogue.col); + get_desc(obj, desc, sizeof(desc)); + messagef(0, "dropped %s", desc); + (void)reg_move(); +} + +static object * +check_duplicate(object *obj, object *pack) +{ + object *op; + + if (!(obj->what_is & (WEAPON | FOOD | SCROL | POTION))) { + return(0); + } + if ((obj->what_is == FOOD) && (obj->which_kind == FRUIT)) { + return(0); + } + op = pack->next_object; + + while (op) { + if ((op->what_is == obj->what_is) && + (op->which_kind == obj->which_kind)) { + + if ((obj->what_is != WEAPON) || + ((obj->what_is == WEAPON) && + ((obj->which_kind == ARROW) || + (obj->which_kind == DAGGER) || + (obj->which_kind == DART) || + (obj->which_kind == SHURIKEN)) && + (obj->quiver == op->quiver))) { + op->quantity += obj->quantity; + return(op); + } + } + op = op->next_object; + } + return(0); +} + +static short +next_avail_ichar(void) +{ + object *obj; + int i; + boolean ichars[26]; + + for (i = 0; i < 26; i++) { + ichars[i] = 0; + } + obj = rogue.pack.next_object; + while (obj) { + if (obj->ichar >= 'a' && obj->ichar <= 'z') { + ichars[(obj->ichar - 'a')] = 1; + } + obj = obj->next_object; + } + for (i = 0; i < 26; i++) { + if (!ichars[i]) { + return(i + 'a'); + } + } + return('?'); +} + +void +wait_for_ack(void) +{ + while (rgetchar() != ' ') + ; +} + +short +pack_letter(const char *prompt, unsigned short mask) +{ + short ch; + unsigned short tmask = mask; + + if (!mask_pack(&rogue.pack, mask)) { + messagef(0, "nothing appropriate"); + return(CANCEL); + } + for (;;) { + + messagef(0, "%s", prompt); + + for (;;) { + ch = rgetchar(); + if (!is_pack_letter(&ch, &mask)) { + sound_bell(); + } else { + break; + } + } + + if (ch == LIST) { + check_message(); + mask = tmask; + inventory(&rogue.pack, mask); + } else { + break; + } + mask = tmask; + } + check_message(); + return(ch); +} + +void +take_off(void) +{ + char desc[DCOLS]; + object *obj; + + if (rogue.armor) { + if (rogue.armor->is_cursed) { + messagef(0, "%s", curse_message); + } else { + mv_aquatars(); + obj = rogue.armor; + unwear(rogue.armor); + get_desc(obj, desc, sizeof(desc)); + messagef(0, "was wearing %s", desc); + print_stats(STAT_ARMOR); + (void)reg_move(); + } + } else { + messagef(0, "not wearing any"); + } +} + +void +wear(void) +{ + short ch; + object *obj; + char desc[DCOLS]; + + if (rogue.armor) { + messagef(0, "you're already wearing some"); + return; + } + ch = pack_letter("wear what?", ARMOR); + + if (ch == CANCEL) { + return; + } + if (!(obj = get_letter_object(ch))) { + messagef(0, "no such item."); + return; + } + if (obj->what_is != ARMOR) { + messagef(0, "you can't wear that"); + return; + } + obj->identified = 1; + get_desc(obj, desc, sizeof(desc)); + messagef(0, "wearing %s", desc); + do_wear(obj); + print_stats(STAT_ARMOR); + (void)reg_move(); +} + +void +unwear(object *obj) +{ + if (obj) { + obj->in_use_flags &= (~BEING_WORN); + } + rogue.armor = NULL; +} + +void +do_wear(object *obj) +{ + rogue.armor = obj; + obj->in_use_flags |= BEING_WORN; + obj->identified = 1; +} + +void +wield(void) +{ + short ch; + object *obj; + char desc[DCOLS]; + + if (rogue.weapon && rogue.weapon->is_cursed) { + messagef(0, "%s", curse_message); + return; + } + ch = pack_letter("wield what?", WEAPON); + + if (ch == CANCEL) { + return; + } + if (!(obj = get_letter_object(ch))) { + messagef(0, "No such item."); + return; + } + if (obj->what_is & (ARMOR | RING)) { + messagef(0, "you can't wield %s", + ((obj->what_is == ARMOR) ? "armor" : "rings")); + return; + } + if (obj->in_use_flags & BEING_WIELDED) { + messagef(0, "in use"); + } else { + unwield(rogue.weapon); + get_desc(obj, desc, sizeof(desc)); + messagef(0, "wielding %s", desc); + do_wield(obj); + (void)reg_move(); + } +} + +void +do_wield(object *obj) +{ + rogue.weapon = obj; + obj->in_use_flags |= BEING_WIELDED; +} + +void +unwield(object *obj) +{ + if (obj) { + obj->in_use_flags &= (~BEING_WIELDED); + } + rogue.weapon = NULL; +} + +void +call_it(void) +{ + short ch; + object *obj; + struct id *id_table; + char buf[MAX_TITLE_LENGTH+2]; + + ch = pack_letter("call what?", (SCROL | POTION | WAND | RING)); + + if (ch == CANCEL) { + return; + } + if (!(obj = get_letter_object(ch))) { + messagef(0, "no such item."); + return; + } + if (!(obj->what_is & (SCROL | POTION | WAND | RING))) { + messagef(0, "surely you already know what that's called"); + return; + } + id_table = get_id_table(obj); + + if (get_input_line("call it:", "", buf, sizeof(buf), + id_table[obj->which_kind].title, 1, 1)) { + id_table[obj->which_kind].id_status = CALLED; + (void)strlcpy(id_table[obj->which_kind].title, buf, + sizeof(id_table[obj->which_kind].title)); + } +} + +short +pack_count(const object *new_obj) +{ + object *obj; + short count = 0; + + obj = rogue.pack.next_object; + + while (obj) { + if (obj->what_is != WEAPON) { + count += obj->quantity; + } else if (!new_obj) { + count++; + } else if ((new_obj->what_is != WEAPON) || + ((obj->which_kind != ARROW) && + (obj->which_kind != DAGGER) && + (obj->which_kind != DART) && + (obj->which_kind != SHURIKEN)) || + (new_obj->which_kind != obj->which_kind) || + (obj->quiver != new_obj->quiver)) { + count++; + } + obj = obj->next_object; + } + return(count); +} + +static boolean +mask_pack(const object *pack, unsigned short mask) +{ + while (pack->next_object) { + pack = pack->next_object; + if (pack->what_is & mask) { + return(1); + } + } + return(0); +} + +static boolean +is_pack_letter(short *c, unsigned short *mask) +{ + if (((*c == '?') || (*c == '!') || (*c == ':') || (*c == '=') || + (*c == ')') || (*c == ']') || (*c == '/') || (*c == ','))) { + switch(*c) { + case '?': + *mask = SCROL; + break; + case '!': + *mask = POTION; + break; + case ':': + *mask = FOOD; + break; + case ')': + *mask = WEAPON; + break; + case ']': + *mask = ARMOR; + break; + case '/': + *mask = WAND; + break; + case '=': + *mask = RING; + break; + case ',': + *mask = AMULET; + break; + } + *c = LIST; + return(1); + } + return(((*c >= 'a') && (*c <= 'z')) || (*c == CANCEL) || (*c == LIST)); +} + +boolean +has_amulet(void) +{ + return(mask_pack(&rogue.pack, AMULET)); +} + +void +kick_into_pack(void) +{ + object *obj; + char desc[DCOLS]; + short stat; + + if (!(dungeon[rogue.row][rogue.col] & OBJECT)) { + messagef(0, "nothing here"); + } else { + if ((obj = pick_up(rogue.row, rogue.col, &stat)) != NULL) { + get_desc(obj, desc, sizeof(desc)); + if (obj->what_is == GOLD) { + messagef(0, "%s", desc); + free_object(obj); + } else { + messagef(0, "%s(%c)", desc, obj->ichar); + } + } + if (obj || (!stat)) { + (void)reg_move(); + } + } +} diff --git a/rogue/pathnames.h b/rogue/pathnames.h new file mode 100644 index 0000000..d6e91a5 --- /dev/null +++ b/rogue/pathnames.h @@ -0,0 +1,35 @@ +/* $NetBSD: pathnames.h,v 1.5 2008/01/14 03:50:02 dholland Exp $ */ + +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pathnames.h 8.1 (Berkeley) 5/31/93 + */ + +#define _PATH_SCOREFILE "/var/games/rogue.scores" +#define _PATH_SCREENDUMP "rogue.screen" diff --git a/rogue/play.c b/rogue/play.c new file mode 100644 index 0000000..8824538 --- /dev/null +++ b/rogue/play.c @@ -0,0 +1,299 @@ +/* $NetBSD: play.c,v 1.9 2008/01/14 03:50:02 dholland Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Timothy C. Stoehr. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)play.c 8.1 (Berkeley) 5/31/93"; +#else +__RCSID("$NetBSD: play.c,v 1.9 2008/01/14 03:50:02 dholland Exp $"); +#endif +#endif /* not lint */ + +/* + * play.c + * + * This source herein may be modified and/or distributed by anybody who + * so desires, with the following restrictions: + * 1.) No portion of this notice shall be removed. + * 2.) Credit shall not be taken for the creation of this source. + * 3.) This code is not to be traded, sold, or used for personal + * gain or profit. + * + */ + +#include "rogue.h" + +boolean interrupted = 0; + +static const char unknown_command[] = "unknown command"; + +void +play_level(void) +{ + short ch; + int count; + + for (;;) { + interrupted = 0; + if (hit_message[0]) { + messagef(1, "%s", hit_message); + hit_message[0] = 0; + } + if (trap_door) { + trap_door = 0; + return; + } + move(rogue.row, rogue.col); + refresh(); + + ch = rgetchar(); +CMCH: + check_message(); + count = 0; +CH: + switch(ch) { + case '.': + rest((count > 0) ? count : 1); + break; + case 's': + search(((count > 0) ? count : 1), 0); + break; + case 'i': + inventory(&rogue.pack, ALL_OBJECTS); + break; + case 'f': + fight(0); + break; + case 'F': + fight(1); + break; + case 'h': + case 'j': + case 'k': + case 'l': + case 'y': + case 'u': + case 'n': + case 'b': + (void)one_move_rogue(ch, 1); + break; + case 'H': + case 'J': + case 'K': + case 'L': + case 'B': + case 'Y': + case 'U': + case 'N': + case '\010': + case '\012': + case '\013': + case '\014': + case '\031': + case '\025': + case '\016': + case '\002': + multiple_move_rogue(ch); + break; + case 'e': + eat(); + break; + case 'q': + quaff(); + break; + case 'r': + read_scroll(); + break; + case 'm': + move_onto(); + break; + case ',': + kick_into_pack(); + break; + case 'd': + drop(); + break; + case 'P': + put_on_ring(); + break; + case 'R': + remove_ring(); + break; + case '\020': + do { + remessage(count++); + ch = rgetchar(); + } while (ch == '\020'); + goto CMCH; + break; + case '\027': + wizardize(); + break; + case '>': + if (drop_check()) { + return; + } + break; + case '<': + if (check_up()) { + return; + } + break; + case ')': + case ']': + inv_armor_weapon(ch == ')'); + break; + case '=': + inv_rings(); + break; + case '^': + id_trap(); + break; + case '/': + id_type(); + break; + case '?': + id_com(); + break; + case '!': + do_shell(); + break; + case 'o': + edit_opts(); + break; + case 'I': + single_inv(0); + break; + case 'T': + take_off(); + break; + case 'W': + wear(); + break; + case 'w': + wield(); + break; + case 'c': + call_it(); + break; + case 'z': + zapp(); + break; + case 't': + throw(); + break; + case 'v': + messagef(0, "rogue-clone: Version III. (Tim Stoehr was here), tektronix!zeus!tims"); + break; + case 'Q': + quit(0); + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + move(rogue.row, rogue.col); + refresh(); + do { + if (count < 100) { + count = (10 * count) + (ch - '0'); + } + ch = rgetchar(); + } while (is_digit(ch)); + if (ch != CANCEL) { + goto CH; + } + break; + case ' ': + break; + case '\011': + if (wizard) { + inventory(&level_objects, ALL_OBJECTS); + } else { + messagef(0, "%s", unknown_command); + } + break; + case '\023': + if (wizard) { + draw_magic_map(); + } else { + messagef(0, "%s", unknown_command); + } + break; + case '\024': + if (wizard) { + show_traps(); + } else { + messagef(0, "%s", unknown_command); + } + break; + case '\017': + if (wizard) { + show_objects(); + } else { + messagef(0, "%s", unknown_command); + } + break; + case '\001': + show_average_hp(); + break; + case '\003': + if (wizard) { + c_object_for_wizard(); + } else { + messagef(0, "%s", unknown_command); + } + break; + case '\015': + if (wizard) { + show_monsters(); + } else { + messagef(0, "%s", unknown_command); + } + break; + case 'S': + save_game(); + break; + default: + messagef(0, "%s", unknown_command); + break; + } + } +} diff --git a/rogue/random.c b/rogue/random.c new file mode 100644 index 0000000..f119c0f --- /dev/null +++ b/rogue/random.c @@ -0,0 +1,146 @@ +/* $NetBSD: random.c,v 1.8 2009/08/12 08:44:45 dholland Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Timothy C. Stoehr. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)random.c 8.1 (Berkeley) 5/31/93"; +#else +__RCSID("$NetBSD: random.c,v 1.8 2009/08/12 08:44:45 dholland Exp $"); +#endif +#endif /* not lint */ + +#include "rogue.h" + +/* + * random.c + * + * This source herein may be modified and/or distributed by anybody who + * so desires, with the following restrictions: + * 1.) No portion of this notice shall be removed. + * 2.) Credit shall not be taken for the creation of this source. + * 3.) This code is not to be traded, sold, or used for personal + * gain or profit. + * + */ + +static long rntb[32] = { + 3, 0x9a319039, 0x32d9c024, 0x9b663182, 0x5da1f342, + 0xde3b81e0, 0xdf0a6fb5, 0xf103bc02, 0x48f340fb, 0x7449e56b, + 0xbeb1dbb0, 0xab5c5918, 0x946554fd, 0x8c2e680f, 0xeb3d799f, + 0xb11ee0b7, 0x2d436b86, 0xda672e2a, 0x1588ca88, 0xe369735d, + 0x904f35f7, 0xd7158fd6, 0x6fa6f051, 0x616e6b96, 0xac94efdc, + 0x36413f93, 0xc622c298, 0xf5a42ab8, 0x8a88d77b, 0xf5ad9d0e, + 0x8999220b, 0x27fb47b9 +}; + +static long *fptr = &rntb[4]; +static long *rptr = &rntb[1]; +static long *state = &rntb[1]; +static int rand_type = 3; +static int rand_deg = 31; +static int rand_sep = 3; +static long *end_ptr = &rntb[32]; + +static long rrandom(void); + +void +srrandom(int x) +{ + int i; + + state[0] = x; + if (rand_type != 0) { + for (i = 1; i < rand_deg; i++) { + state[i] = 1103515245 * state[i - 1] + 12345; + } + fptr = &state[rand_sep]; + rptr = &state[0]; + for (i = 0; i < 10 * rand_deg; i++) { + (void)rrandom(); + } + } +} + +static long +rrandom(void) +{ + long i; + + if (rand_type == 0) { + i = state[0] = (state[0]*1103515245 + 12345) & 0x7fffffff; + } else { + *fptr += *rptr; + i = (*fptr >> 1) & 0x7fffffff; + if (++fptr >= end_ptr) { + fptr = state; + ++rptr; + } else { + if (++rptr >= end_ptr) { + rptr = state; + } + } + } + return(i); +} + +int +get_rand(int x, int y) +{ + int r, t; + long lr; + + if (x > y) { + t = y; + y = x; + x = t; + } + lr = rrandom(); + lr &= 0x00003fffL; + r = (int)lr; + r = (r % ((y - x) + 1)) + x; + return(r); +} + +int +rand_percent(int percentage) +{ + return(get_rand(1, 100) <= percentage); +} + +int +coin_toss(void) +{ + return(((rrandom() & 01) ? 1 : 0)); +} diff --git a/rogue/ring.c b/rogue/ring.c new file mode 100644 index 0000000..5cdcf5f --- /dev/null +++ b/rogue/ring.c @@ -0,0 +1,338 @@ +/* $NetBSD: ring.c,v 1.9 2008/01/14 03:50:02 dholland Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Timothy C. Stoehr. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)ring.c 8.1 (Berkeley) 5/31/93"; +#else +__RCSID("$NetBSD: ring.c,v 1.9 2008/01/14 03:50:02 dholland Exp $"); +#endif +#endif /* not lint */ + +/* + * ring.c + * + * This source herein may be modified and/or distributed by anybody who + * so desires, with the following restrictions: + * 1.) No portion of this notice shall be removed. + * 2.) Credit shall not be taken for the creation of this source. + * 3.) This code is not to be traded, sold, or used for personal + * gain or profit. + * + */ + +#include "rogue.h" + +static const char left_or_right[] = "left or right hand?"; +static const char no_ring[] = "there's no ring on that hand"; + +short stealthy; +short r_rings; +short add_strength; +short e_rings; +short regeneration; +short ring_exp; +short auto_search; +boolean r_teleport; +boolean r_see_invisible; +boolean sustain_strength; +boolean maintain_armor; + +void +put_on_ring(void) +{ + short ch; + char desc[DCOLS]; + object *ring; + + if (r_rings == 2) { + messagef(0, "wearing two rings already"); + return; + } + if ((ch = pack_letter("put on what?", RING)) == CANCEL) { + return; + } + if (!(ring = get_letter_object(ch))) { + messagef(0, "no such item."); + return; + } + if (!(ring->what_is & RING)) { + messagef(0, "that's not a ring"); + return; + } + if (ring->in_use_flags & (ON_LEFT_HAND | ON_RIGHT_HAND)) { + messagef(0, "that ring is already being worn"); + return; + } + if (r_rings == 1) { + ch = (rogue.left_ring ? 'r' : 'l'); + } else { + messagef(0, "%s", left_or_right); + do { + ch = rgetchar(); + } while ((ch != CANCEL) && (ch != 'l') && (ch != 'r') && (ch != '\n') && + (ch != '\r')); + } + if ((ch != 'l') && (ch != 'r')) { + check_message(); + return; + } + if (((ch == 'l') && rogue.left_ring)||((ch == 'r') && rogue.right_ring)) { + check_message(); + messagef(0, "there's already a ring on that hand"); + return; + } + if (ch == 'l') { + do_put_on(ring, 1); + } else { + do_put_on(ring, 0); + } + ring_stats(1); + check_message(); + get_desc(ring, desc, sizeof(desc)); + messagef(0, "%s", desc); + (void)reg_move(); +} + +/* + * Do not call ring_stats() from within do_put_on(). It will cause + * serious problems when do_put_on() is called from read_pack() in restore(). + */ + +void +do_put_on(object *ring, boolean on_left) +{ + if (on_left) { + ring->in_use_flags |= ON_LEFT_HAND; + rogue.left_ring = ring; + } else { + ring->in_use_flags |= ON_RIGHT_HAND; + rogue.right_ring = ring; + } +} + +void +remove_ring(void) +{ + boolean left = 0, right = 0; + short ch; + char buf[DCOLS]; + object *ring; + + ring = NULL; + if (r_rings == 0) { + inv_rings(); + } else if (rogue.left_ring && !rogue.right_ring) { + left = 1; + } else if (!rogue.left_ring && rogue.right_ring) { + right = 1; + } else { + messagef(0, "%s", left_or_right); + do { + ch = rgetchar(); + } while ((ch != CANCEL) && (ch != 'l') && (ch != 'r') && + (ch != '\n') && (ch != '\r')); + left = (ch == 'l'); + right = (ch == 'r'); + check_message(); + } + if (left || right) { + if (left) { + if (rogue.left_ring) { + ring = rogue.left_ring; + } else { + messagef(0, "%s", no_ring); + } + } else { + if (rogue.right_ring) { + ring = rogue.right_ring; + } else { + messagef(0, "%s", no_ring); + } + } + if (ring->is_cursed) { + messagef(0, "%s", curse_message); + } else { + un_put_on(ring); + get_desc(ring, buf, sizeof(buf)); + messagef(0, "removed %s", buf); + (void)reg_move(); + } + } +} + +void +un_put_on(object *ring) +{ + if (ring && (ring->in_use_flags & ON_LEFT_HAND)) { + ring->in_use_flags &= (~ON_LEFT_HAND); + rogue.left_ring = NULL; + } else if (ring && (ring->in_use_flags & ON_RIGHT_HAND)) { + ring->in_use_flags &= (~ON_RIGHT_HAND); + rogue.right_ring = NULL; + } + ring_stats(1); +} + +void +gr_ring(object *ring, boolean assign_wk) +{ + ring->what_is = RING; + if (assign_wk) { + ring->which_kind = get_rand(0, (RINGS - 1)); + } + ring->class = 0; + + switch(ring->which_kind) { + /* + case STEALTH: + break; + case SLOW_DIGEST: + break; + case REGENERATION: + break; + case R_SEE_INVISIBLE: + break; + case SUSTAIN_STRENGTH: + break; + case R_MAINTAIN_ARMOR: + break; + case SEARCHING: + break; + */ + case R_TELEPORT: + ring->is_cursed = 1; + break; + case ADD_STRENGTH: + case DEXTERITY: + while ((ring->class = (get_rand(0, 4) - 2)) == 0) + ; + ring->is_cursed = (ring->class < 0); + break; + case ADORNMENT: + ring->is_cursed = coin_toss(); + break; + } +} + +void +inv_rings(void) +{ + char buf[DCOLS]; + + if (r_rings == 0) { + messagef(0, "not wearing any rings"); + } else { + if (rogue.left_ring) { + get_desc(rogue.left_ring, buf, sizeof(buf)); + messagef(0, "%s", buf); + } + if (rogue.right_ring) { + get_desc(rogue.right_ring, buf, sizeof(buf)); + messagef(0, "%s", buf); + } + } + if (wizard) { + messagef(0, "ste %d, r_r %d, e_r %d, r_t %d, s_s %d, a_s %d, reg %d, r_e %d, s_i %d, m_a %d, aus %d", + stealthy, r_rings, e_rings, r_teleport, sustain_strength, + add_strength, regeneration, ring_exp, r_see_invisible, + maintain_armor, auto_search); + } +} + +void +ring_stats(boolean pr) +{ + short i; + object *ring; + + stealthy = 0; + r_rings = 0; + e_rings = 0; + r_teleport = 0; + sustain_strength = 0; + add_strength = 0; + regeneration = 0; + ring_exp = 0; + r_see_invisible = 0; + maintain_armor = 0; + auto_search = 0; + + for (i = 0; i < 2; i++) { + if (!(ring = ((i == 0) ? rogue.left_ring : rogue.right_ring))) { + continue; + } + r_rings++; + e_rings++; + switch(ring->which_kind) { + case STEALTH: + stealthy++; + break; + case R_TELEPORT: + r_teleport = 1; + break; + case REGENERATION: + regeneration++; + break; + case SLOW_DIGEST: + e_rings -= 2; + break; + case ADD_STRENGTH: + add_strength += ring->class; + break; + case SUSTAIN_STRENGTH: + sustain_strength = 1; + break; + case DEXTERITY: + ring_exp += ring->class; + break; + case ADORNMENT: + break; + case R_SEE_INVISIBLE: + r_see_invisible = 1; + break; + case MAINTAIN_ARMOR: + maintain_armor = 1; + break; + case SEARCHING: + auto_search += 2; + break; + } + } + if (pr) { + print_stats(STAT_STRENGTH); + relight(); + } +} diff --git a/rogue/rogue.6 b/rogue/rogue.6 new file mode 100644 index 0000000..4598c7b --- /dev/null +++ b/rogue/rogue.6 @@ -0,0 +1,107 @@ +.\" $NetBSD: rogue.6,v 1.11 2005/09/15 02:09:41 wiz Exp $ +.\" +.\" Copyright (c) 1988, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)rogue.6 8.1 (Berkeley) 5/31/93 +.\" +.Dd May 31, 1993 +.Dt ROGUE 6 +.Os +.Sh NAME +.Nm rogue +.Nd exploring The Dungeons of Doom +.Sh SYNOPSIS +.Nm +.Op Fl s +.Op Ar save_file +.\" .Op Fl r +.\" .Op Fl d +.Sh DESCRIPTION +.Nm +is a computer fantasy game with a new twist. +It is CRT oriented and the object of the game is to survive the attacks of +various monsters and get a lot of gold, rather than the puzzle solving +orientation of most computer fantasy games. +.Pp +To get started you really only need to know two commands. +The command +.Ic \&? +will give you a list of the available commands and the command +.Ic \&/ +will identify the things you see on the screen. +.Pp +To win the game (as opposed to merely playing to beat other people's high +scores) you must locate the Amulet of Yendor which is somewhere below +the 20th level of the dungeon and get it out. +Nobody has achieved this yet and if somebody does, they will probably go +down in history as a hero among heroes. +.Pp +When the game ends, either by your death, when you quit, or if you (by +some miracle) manage to win, +.Nm +will give you a list of the top-ten scorers. +The scoring is based entirely upon how much gold you get. +There is a 10% penalty for getting yourself killed. +.Pp +If +.Ar save_file +is specified, +rogue will be restored from the specified saved game file. +.Pp +The +.Fl s +option will print out the list of scores. +.Pp +For more detailed directions, read the document +.Rs +.%T A Guide to the Dungeons of Doom +.Re +.Sh FILES +.Bl -tag -width /var/games/rogue.scores -compact +.It Pa /var/games/rogue.scores +Score file +.It Pa ~/rogue.save +Default save file +.El +.Sh SEE ALSO +.Rs +.%A Michael C. Toy +.%A Kenneth C. R. C. Arnold +.%T A guide to the Dungeons of Doom +.Re +.Sh AUTHORS +.An Timothy Stoehr +.An Michael C. Toy +.An Kenneth C. R. C. Arnold +.An Glenn Wichman +.Sh BUGS +Probably infinite, although none are known. +However, that Ice Monsters sometimes transfix you permanently is +.Em not +a bug. +It's a feature. diff --git a/rogue/rogue.h b/rogue/rogue.h new file mode 100644 index 0000000..8ce9411 --- /dev/null +++ b/rogue/rogue.h @@ -0,0 +1,702 @@ +/* $NetBSD: rogue.h,v 1.24 2013/08/11 03:44:27 dholland Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Timothy C. Stoehr. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)rogue.h 8.1 (Berkeley) 5/31/93 + */ + +/* + * rogue.h + * + * This source herein may be modified and/or distributed by anybody who + * so desires, with the following restrictions: + * 1.) This notice shall not be removed. + * 2.) Credit shall not be taken for the creation of this source. + * 3.) This code is not to be traded, sold, or used for personal + * gain or profit. + */ + +#define boolean char + +#define NOTHING ((unsigned short) 0) +#define OBJECT ((unsigned short) 01) +#define MONSTER ((unsigned short) 02) +#define STAIRS ((unsigned short) 04) +#define HORWALL ((unsigned short) 010) +#define VERTWALL ((unsigned short) 020) +#define DOOR ((unsigned short) 040) +#define FLOOR ((unsigned short) 0100) +#define TUNNEL ((unsigned short) 0200) +#define TRAP ((unsigned short) 0400) +#define HIDDEN ((unsigned short) 01000) + +#define ARMOR ((unsigned short) 01) +#define WEAPON ((unsigned short) 02) +#define SCROL ((unsigned short) 04) +#define POTION ((unsigned short) 010) +#define GOLD ((unsigned short) 020) +#define FOOD ((unsigned short) 040) +#define WAND ((unsigned short) 0100) +#define RING ((unsigned short) 0200) +#define AMULET ((unsigned short) 0400) +#define ALL_OBJECTS ((unsigned short) 0777) + +#define LEATHER 0 +#define RINGMAIL 1 +#define SCALE 2 +#define CHAIN 3 +#define BANDED 4 +#define SPLINT 5 +#define PLATE 6 +#define ARMORS 7 + +#define BOW 0 +#define DART 1 +#define ARROW 2 +#define DAGGER 3 +#define SHURIKEN 4 +#define MACE 5 +#define LONG_SWORD 6 +#define TWO_HANDED_SWORD 7 +#define WEAPONS 8 + +#define MAX_PACK_COUNT 24 + +#define PROTECT_ARMOR 0 +#define HOLD_MONSTER 1 +#define ENCH_WEAPON 2 +#define ENCH_ARMOR 3 +#define IDENTIFY 4 +#define TELEPORT 5 +#define SLEEP 6 +#define SCARE_MONSTER 7 +#define REMOVE_CURSE 8 +#define CREATE_MONSTER 9 +#define AGGRAVATE_MONSTER 10 +#define MAGIC_MAPPING 11 +#define CON_MON 12 +#define SCROLS 13 + +#define INCREASE_STRENGTH 0 +#define RESTORE_STRENGTH 1 +#define HEALING 2 +#define EXTRA_HEALING 3 +#define POISON 4 +#define RAISE_LEVEL 5 +#define BLINDNESS 6 +#define HALLUCINATION 7 +#define DETECT_MONSTER 8 +#define DETECT_OBJECTS 9 +#define CONFUSION 10 +#define LEVITATION 11 +#define HASTE_SELF 12 +#define SEE_INVISIBLE 13 +#define POTIONS 14 + +#define TELE_AWAY 0 +#define SLOW_MONSTER 1 +#define INVISIBILITY 2 +#define POLYMORPH 3 +#define HASTE_MONSTER 4 +#define MAGIC_MISSILE 5 +#define CANCELLATION 6 +#define DO_NOTHING 7 +#define DRAIN_LIFE 8 +#define COLD 9 +#define FIRE 10 +#define WANDS 11 + +#define STEALTH 0 +#define R_TELEPORT 1 +#define REGENERATION 2 +#define SLOW_DIGEST 3 +#define ADD_STRENGTH 4 +#define SUSTAIN_STRENGTH 5 +#define DEXTERITY 6 +#define ADORNMENT 7 +#define R_SEE_INVISIBLE 8 +#define MAINTAIN_ARMOR 9 +#define SEARCHING 10 +#define RINGS 11 + +#define RATION 0 +#define FRUIT 1 + +#define NOT_USED ((unsigned short) 0) +#define BEING_WIELDED ((unsigned short) 01) +#define BEING_WORN ((unsigned short) 02) +#define ON_LEFT_HAND ((unsigned short) 04) +#define ON_RIGHT_HAND ((unsigned short) 010) +#define ON_EITHER_HAND ((unsigned short) 014) +#define BEING_USED ((unsigned short) 017) + +#define NO_TRAP -1 +#define TRAP_DOOR 0 +#define BEAR_TRAP 1 +#define TELE_TRAP 2 +#define DART_TRAP 3 +#define SLEEPING_GAS_TRAP 4 +#define RUST_TRAP 5 +#define TRAPS 6 + +#define STEALTH_FACTOR 3 +#define R_TELE_PERCENT 8 + +#define UNIDENTIFIED ((unsigned short) 00) /* MUST BE ZERO! */ +#define IDENTIFIED ((unsigned short) 01) +#define CALLED ((unsigned short) 02) + +#define DROWS 24 +#define DCOLS 80 +#define NMESSAGES 5 +#define MAX_TITLE_LENGTH 30 +#define MAXSYLLABLES 40 +#define MAX_METAL 14 +#define WAND_MATERIALS 30 +#define GEMS 14 + +#define GOLD_PERCENT 46 + +#define MAX_OPT_LEN 40 + +#define MAX_ID_TITLE_LEN 64 +struct id { + short value; + char title[MAX_ID_TITLE_LEN]; + const char *real; + unsigned short id_status; +}; + +/* The following #defines provide more meaningful names for some of the + * struct object fields that are used for monsters. This, since each monster + * and object (scrolls, potions, etc) are represented by a struct object. + * Ideally, this should be handled by some kind of union structure. + */ + +#define m_damage damage +#define hp_to_kill quantity +#define m_char ichar +#define first_level is_protected +#define last_level is_cursed +#define m_hit_chance class +#define stationary_damage identified +#define drop_percent which_kind +#define trail_char d_enchant +#define slowed_toggle quiver +#define moves_confused hit_enchant +#define nap_length picked_up +#define disguise what_is +#define next_monster next_object + +struct obj { /* comment is monster meaning */ + unsigned long m_flags; /* monster flags */ + const char *damage; /* damage it does */ + short quantity; /* hit points to kill */ + short ichar; /* 'A' is for aquator */ + short kill_exp; /* exp for killing it */ + short is_protected; /* level starts */ + short is_cursed; /* level ends */ + short class; /* chance of hitting you */ + short identified; /* 'F' damage, 1,2,3... */ + unsigned short which_kind; /* item carry/drop % */ + short o_row, o_col, o; /* o is how many times stuck at o_row, o_col */ + short row, col; /* current row, col */ + short d_enchant; /* room char when detect_monster */ + short quiver; /* monster slowed toggle */ + short trow, tcol; /* target row, col */ + short hit_enchant; /* how many moves is confused */ + unsigned short what_is; /* imitator's charactor (?!%: */ + short picked_up; /* sleep from wand of sleep */ + unsigned short in_use_flags; + struct obj *next_object; /* next monster */ +}; + +typedef struct obj object; + +#define INIT_AW NULL +#define INIT_RINGS NULL +#define INIT_HP 12 +#define INIT_STR 16 +#define INIT_EXPLEVEL 1 +#define INIT_EXP 0 +#define INIT_PACK {0,NULL,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL} +#define INIT_GOLD 0 +#define INIT_CHAR '@' +#define INIT_MOVES 1250 + +struct fightr { + object *armor; + object *weapon; + object *left_ring, *right_ring; + short hp_current; + short hp_max; + short str_current; + short str_max; + object pack; + long gold; + short exp; + long exp_points; + short row, col; + short fchar; + short moves_left; +}; + +typedef struct fightr fighter; + +struct dr { + short oth_room; + short oth_row, + oth_col; + short door_row, + door_col; +}; + +typedef struct dr door; + +struct rm { + short bottom_row, right_col, left_col, top_row; + door doors[4]; + unsigned short is_room; +}; + +typedef struct rm room; + +#define MAXROOMS 9 +#define BIG_ROOM 10 + +#define NO_ROOM (-1) + +#define PASSAGE (-3) /* cur_room value */ + +#define AMULET_LEVEL 26 + +#define R_NOTHING ((unsigned short) 01) +#define R_ROOM ((unsigned short) 02) +#define R_MAZE ((unsigned short) 04) +#define R_DEADEND ((unsigned short) 010) +#define R_CROSS ((unsigned short) 020) + +#define MAX_EXP_LEVEL 21 +#define MAX_EXP 10000001L +#define MAX_GOLD 999999 +#define MAX_ARMOR 99 +#define MAX_HP 999 +#define MAX_STRENGTH 99 +#define LAST_DUNGEON 99 + +#define STAT_LEVEL 01 +#define STAT_GOLD 02 +#define STAT_HP 04 +#define STAT_STRENGTH 010 +#define STAT_ARMOR 020 +#define STAT_EXP 040 +#define STAT_HUNGER 0100 +#define STAT_LABEL 0200 +#define STAT_ALL 0377 + +#define PARTY_TIME 10 /* one party somewhere in each 10 level span */ + +#define MAX_TRAPS 10 /* maximum traps per level */ + +#define HIDE_PERCENT 12 + +struct tr { + short trap_type; + short trap_row, trap_col; +}; + +typedef struct tr trap; + +extern fighter rogue; +extern room rooms[]; +extern trap traps[]; +extern unsigned short dungeon[DROWS][DCOLS]; +extern object level_objects; + +extern struct id id_scrolls[]; +extern struct id id_potions[]; +extern struct id id_wands[]; +extern struct id id_rings[]; +extern struct id id_weapons[]; +extern struct id id_armors[]; + +extern object level_monsters; + +#define MONSTERS 26 + +#define HASTED 01L +#define SLOWED 02L +#define INVISIBLE 04L +#define ASLEEP 010L +#define WAKENS 020L +#define WANDERS 040L +#define FLIES 0100L +#define FLITS 0200L +#define CAN_FLIT 0400L /* can, but usually doesn't, flit */ +#define CONFUSED 01000L +#define RUSTS 02000L +#define HOLDS 04000L +#define FREEZES 010000L +#define STEALS_GOLD 020000L +#define STEALS_ITEM 040000L +#define STINGS 0100000L +#define DRAINS_LIFE 0200000L +#define DROPS_LEVEL 0400000L +#define SEEKS_GOLD 01000000L +#define FREEZING_ROGUE 02000000L +#define RUST_VANISHED 04000000L +#define CONFUSES 010000000L +#define IMITATES 020000000L +#define FLAMES 040000000L +#define STATIONARY 0100000000L /* damage will be 1,2,3,... */ +#define NAPPING 0200000000L /* can't wake up for a while */ +#define ALREADY_MOVED 0400000000L + +#define SPECIAL_HIT (RUSTS|HOLDS|FREEZES|STEALS_GOLD|STEALS_ITEM|STINGS|DRAINS_LIFE|DROPS_LEVEL) + +#define WAKE_PERCENT 45 +#define FLIT_PERCENT 40 +#define PARTY_WAKE_PERCENT 75 + +#define HYPOTHERMIA 1 +#define STARVATION 2 +#define POISON_DART 3 +#define QUIT 4 +#define WIN 5 +#define KFIRE 6 + +#define UPWARD 0 +#define UPRIGHT 1 +#define RIGHT 2 +#define DOWNRIGHT 3 +#define DOWN 4 +#define DOWNLEFT 5 +#define LEFT 6 +#define UPLEFT 7 +#define DIRS 8 + +#define ROW1 7 +#define ROW2 15 + +#define COL1 26 +#define COL2 52 + +#define MOVED 0 +#define MOVE_FAILED -1 +#define STOPPED_ON_SOMETHING -2 +#define CANCEL '\033' +#define LIST '*' + +#define HUNGRY 300 +#define WEAK 150 +#define FAINT 20 +#define STARVE 0 + +#define MIN_ROW 1 + +struct rogue_time { + short year; /* >= 1987 */ + short month; /* 1 - 12 */ + short day; /* 1 - 31 */ + short hour; /* 0 - 23 */ + short minute; /* 0 - 59 */ + short second; /* 0 - 59 */ +}; + +#include <curses.h> + +/* + * external routine declarations. + */ +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <unistd.h> + +object *alloc_object(void); +object *get_letter_object(int); +object *gr_monster(object *, int); +object *gr_object(void); +char *md_getenv(const char *); +const char * + md_gln(void); +void *md_malloc(size_t); +const char *mon_name(const object *); +const char *name_of(const object *); +object *object_at(object *, short, short); +object *pick_up(int, int, short *); +void add_exp(int, boolean); +void add_traps(void); +void aggravate(void); +void bounce(short, short, short, short, short); +void byebye(int); +void c_object_for_wizard(void); +void call_it(void); +boolean can_move(int, int, int, int); +void check_gold_seeker(object *); +boolean check_imitator(object *); +void check_message(void); +int check_up(void); +void clean_up(const char *) __dead; +void clear_level(void); +void cnfs(void); +int coin_toss(void); +void cough_up(object *); +void create_monster(void); +void darken_room(short); +void do_put_on(object *, boolean); +void do_shell(void); +void do_wear(object *); +void do_wield(object *); +void dr_course(object *, boolean, short, short); +void draw_magic_map(void); +void drop(void); +int drop_check(void); +void eat(void); +void edit_opts(void); +void error_save(int) __dead; +void fight(boolean); +boolean flame_broil(object *); +void free_object(object *); +void free_stuff(object *); +int get_armor_class(const object *); +int get_damage(const char *, boolean); +void get_desc(const object *, char *, size_t); +void get_dir_rc(short, short *, short *, short); +char get_dungeon_char(short, short); +void get_food(object *, boolean); +int get_hit_chance(const object *); +int get_input_line(const char *, const char *, char *, size_t, const char *, boolean, boolean); +char get_mask_char(unsigned short); +int get_number(const char *); +int get_rand(int, int); +short get_room_number(int, int); +void get_wand_and_ring_materials(void); +int get_weapon_damage(const object *); +char gmc(object *); +char gmc_row_col(int, int); +char gr_obj_char(void); +void gr_ring(object *, boolean); +short gr_room(void); +void gr_row_col(short *, short *, unsigned short); +void hallucinate(void); +boolean has_amulet(void); +int hp_raise(void); +void id_com(void); +void id_trap(void); +void id_type(void); +boolean imitating(short, short); +int init(int, char **); +void insert_score(char [][82], char [][30], const char *, short, short, const object *, int); +void inv_armor_weapon(boolean); +void inv_rings(void); +void inventory(const object *, unsigned short); +boolean is_all_connected(void); +boolean is_digit(int); +boolean is_direction(short, short *); +boolean is_passable(int, int); +boolean is_vowel(short); +void kick_into_pack(void); +void killed_by(const object *, short) __dead; +long lget_number(const char *); +void light_passage(int, int); +void light_up_room(int); +boolean m_confuse(object *); +void make_level(void); +void make_scroll_titles(void); +boolean md_df(const char *); +void md_exit(int) __dead; +void md_gct(struct rogue_time *); +int md_get_file_id(const char *); +void md_gfmt(const char *, struct rogue_time *); +int md_gseed(void); +void md_heed_signals(void); +void md_ignore_signals(void); +int md_link_count(const char *); +void md_lock(boolean); +void md_shell(const char *); +void md_sleep(int); +void md_slurp(void); +/*void message(const char *, boolean);*/ +void messagef(boolean, const char *, ...) __printflike(2, 3); +void mix_colors(void); +int mon_can_go(const object *, short, short); +int mon_damage(object *, short); +void mon_hit(object *); +boolean mon_sees(const object *, int, int); +void move_mon_to(object *, short, short); +void move_onto(void); +void multiple_move_rogue(short); +void mv_1_monster(object *, short, short); +void mv_aquatars(void); +void mv_mons(void); +int name_cmp(char *, const char *); +void nickize(char *, const char *, const char *); +int one_move_rogue(short, short); +void onintr(int); +short pack_count(const object *); +short pack_letter(const char *, unsigned short); +void pad(const char *, short); +void party_monsters(int, int); +short party_objects(int); +void place_at(object *, int, int); +void play_level(void); +void print_stats(int); +void put_amulet(void); +void put_mons(void); +void put_objects(void); +void put_on_ring(void); +void put_player(short); +void put_scores(const object *, short) __dead; +void put_stairs(void); +void quaff(void); +void quit(boolean); +int r_index(const char *, int, boolean); +void rand_around(short, short *, short *); +int rand_percent(int); +void read_scroll(void); +boolean reg_move(void); +void relight(void); +void remessage(short); +void remove_ring(void); +void rest(int); +void restore(const char *); +int rgetchar(void); +void ring_stats(boolean); +int rogue_can_see(int, int); +void rogue_damage(short, object *, short); +void rogue_hit(object *, boolean); +void rust(object *); +void s_con_mon(object *); +void save_game(void); +void save_into_file(const char *); +void search(short, boolean); +boolean seek_gold(object *); +void show_average_hp(void); +void show_monsters(void); +void show_objects(void); +void show_traps(void); +void single_inv(short); +void sound_bell(void); +void special_hit(object *); +void srrandom(int); +void start_window(void); +void stop_window(void); +void take_a_nap(void); +void take_from_pack(object *, object *); +void take_off(void); +void tele(void); +void throw(void); +void trap_player(short, short); +void un_put_on(object *); +void unblind(void); +void unconfuse(void); +void unhallucinate(void); +void unwear(object *); +void unwield(object *); +void vanish(object *, short, object *); +void wait_for_ack(void); +void wake_room(short, boolean, short, short); +void wake_up(object *); +void wanderer(void); +void wear(void); +void wield(void); +void win(void) __dead; +void wizardize(void); +long xxx(boolean); +void xxxx(char *, short); +void zapp(void); +object *add_to_pack(object *, object *, int); +struct id *get_id_table(const object *); + +extern boolean ask_quit; +extern boolean being_held; +extern boolean cant_int; +extern boolean con_mon; +extern boolean detect_monster; +extern boolean did_int; +extern boolean interrupted; +extern boolean is_wood[]; +extern boolean jump; +extern boolean maintain_armor; +extern boolean mon_disappeared; +extern boolean msg_cleared; +extern boolean no_skull; +extern boolean passgo; +extern boolean r_see_invisible; +extern boolean r_teleport; +extern boolean save_is_interactive; +extern boolean score_only; +extern boolean see_invisible; +extern boolean sustain_strength; +extern boolean trap_door; +extern boolean wizard; +#define HIT_MESSAGE_SIZE 80 +extern char hit_message[HIT_MESSAGE_SIZE]; +#define HUNGER_STR_LEN 8 +extern char hunger_str[HUNGER_STR_LEN]; +extern char login_name[MAX_OPT_LEN]; +extern const char *byebye_string; +extern const char curse_message[]; +extern const char *error_file; +extern char *fruit; +extern const char *const m_names[]; +extern const char *more; +extern const char *new_level_message; +extern char *nick_name; +extern const char *press_space; +extern char *save_file; +extern const char you_can_move_again[]; +extern const long level_points[]; +extern short add_strength; +extern short auto_search; +extern short bear_trap; +extern short blind; +extern short confused; +extern short cur_level; +extern short cur_room; +extern short e_rings; +extern short extra_hp; +extern short foods; +extern short halluc; +extern short haste_self; +extern short less_hp; +extern short levitate; +extern short m_moves; +extern short max_level; +extern short party_room; +extern short r_rings; +extern short regeneration; +extern short ring_exp; +extern short stealthy; +extern gid_t gid; +extern gid_t egid; diff --git a/rogue/room.c b/rogue/room.c new file mode 100644 index 0000000..0071489 --- /dev/null +++ b/rogue/room.c @@ -0,0 +1,671 @@ +/* $NetBSD: room.c,v 1.13 2009/08/12 08:44:45 dholland Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Timothy C. Stoehr. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)room.c 8.1 (Berkeley) 5/31/93"; +#else +__RCSID("$NetBSD: room.c,v 1.13 2009/08/12 08:44:45 dholland Exp $"); +#endif +#endif /* not lint */ + +/* + * room.c + * + * This source herein may be modified and/or distributed by anybody who + * so desires, with the following restrictions: + * 1.) No portion of this notice shall be removed. + * 2.) Credit shall not be taken for the creation of this source. + * 3.) This code is not to be traded, sold, or used for personal + * gain or profit. + * + */ + +#include "rogue.h" + +room rooms[MAXROOMS]; + +static boolean rooms_visited[MAXROOMS]; + +#define NOPTS 7 +static const struct option { + const char *prompt; + boolean is_bool; + char **strval; + boolean *bval; +} options[NOPTS] = { + { + "Show position only at end of run (\"jump\"): ", + 1, NULL, &jump + }, + { + "Follow turnings in passageways (\"passgo\"): ", + 1, NULL, &passgo + }, + { + "Don't print skull when killed (\"noskull\" or \"notombstone\"): ", + 1, NULL, &no_skull + }, + { + "Ask player before saying 'Okay, bye-bye!' (\"askquit\"): ", + 1, NULL, &ask_quit + }, + { + "Name (\"name\"): ", + 0, &nick_name, NULL + }, + { + "Fruit (\"fruit\"): ", + 0, &fruit, NULL + }, + { + "Save file (\"file\"): ", + 0, &save_file, NULL + } +}; + +static boolean get_oth_room(short, short *, short *); +static void opt_erase(int); +static void opt_go(int); +static void opt_show(int); +static void visit_rooms(int); + +void +light_up_room(int rn) +{ + short i, j; + + if (!blind) { + for (i = rooms[rn].top_row; + i <= rooms[rn].bottom_row; i++) { + for (j = rooms[rn].left_col; + j <= rooms[rn].right_col; j++) { + if (dungeon[i][j] & MONSTER) { + object *monster; + + if ((monster = object_at( + &level_monsters, i, j)) != NULL) { + dungeon[monster->row][monster->col] &= (~MONSTER); + monster->trail_char = + get_dungeon_char(monster->row, monster->col); + dungeon[monster->row][monster->col] |= MONSTER; + } + } + mvaddch(i, j, get_dungeon_char(i, j)); + } + } + mvaddch(rogue.row, rogue.col, rogue.fchar); + } +} + +void +light_passage(int row, int col) +{ + short i, j, i_end, j_end; + + if (blind) { + return; + } + i_end = (row < (DROWS-2)) ? 1 : 0; + j_end = (col < (DCOLS-1)) ? 1 : 0; + + for (i = ((row > MIN_ROW) ? -1 : 0); i <= i_end; i++) { + for (j = ((col > 0) ? -1 : 0); j <= j_end; j++) { + if (can_move(row, col, row+i, col+j)) { + mvaddch(row+i, col+j, get_dungeon_char(row+i, col+j)); + } + } + } +} + +void +darken_room(short rn) +{ + short i, j; + + for (i = rooms[rn].top_row + 1; i < rooms[rn].bottom_row; i++) { + for (j = rooms[rn].left_col + 1; j < rooms[rn].right_col; j++) { + if (blind) { + mvaddch(i, j, ' '); + } else { + if (!(dungeon[i][j] & (OBJECT | STAIRS)) && + !(detect_monster && (dungeon[i][j] & MONSTER))) { + if (!imitating(i, j)) { + mvaddch(i, j, ' '); + } + if ((dungeon[i][j] & TRAP) && (!(dungeon[i][j] & HIDDEN))) { + mvaddch(i, j, '^'); + } + } + } + } + } +} + +char +get_dungeon_char(short row, short col) +{ + unsigned short mask = dungeon[row][col]; + + if (mask & MONSTER) { + return(gmc_row_col(row, col)); + } + if (mask & OBJECT) { + object *obj; + + obj = object_at(&level_objects, row, col); + return(get_mask_char(obj->what_is)); + } + if (mask & (TUNNEL | STAIRS | HORWALL | VERTWALL | FLOOR | DOOR)) { + if ((mask & (TUNNEL| STAIRS)) && (!(mask & HIDDEN))) { + return(((mask & STAIRS) ? '%' : '#')); + } + if (mask & HORWALL) { + return('-'); + } + if (mask & VERTWALL) { + return('|'); + } + if (mask & FLOOR) { + if (mask & TRAP) { + if (!(dungeon[row][col] & HIDDEN)) { + return('^'); + } + } + return('.'); + } + if (mask & DOOR) { + if (mask & HIDDEN) { + if (((col > 0) && (dungeon[row][col-1] & HORWALL)) || + ((col < (DCOLS-1)) && (dungeon[row][col+1] & HORWALL))) { + return('-'); + } else { + return('|'); + } + } else { + return('+'); + } + } + } + return(' '); +} + +char +get_mask_char(unsigned short mask) +{ + switch(mask) { + case SCROL: + return('?'); + case POTION: + return('!'); + case GOLD: + return('*'); + case FOOD: + return(':'); + case WAND: + return('/'); + case ARMOR: + return(']'); + case WEAPON: + return(')'); + case RING: + return('='); + case AMULET: + return(','); + default: + return('~'); /* unknown, something is wrong */ + } +} + +void +gr_row_col(short *row, short *col, unsigned short mask) +{ + short rn; + short r, c; + + do { + r = get_rand(MIN_ROW, DROWS-2); + c = get_rand(0, DCOLS-1); + rn = get_room_number(r, c); + } while ((rn == NO_ROOM) || + (!(dungeon[r][c] & mask)) || + (dungeon[r][c] & (~mask)) || + (!(rooms[rn].is_room & (R_ROOM | R_MAZE))) || + ((r == rogue.row) && (c == rogue.col))); + + *row = r; + *col = c; +} + +short +gr_room(void) +{ + short i; + + do { + i = get_rand(0, MAXROOMS-1); + } while (!(rooms[i].is_room & (R_ROOM | R_MAZE))); + + return(i); +} + +short +party_objects(int rn) +{ + short i, j, nf = 0; + object *obj; + short n, N, row, col; + boolean found; + + row = col = 0; + N = ((rooms[rn].bottom_row - rooms[rn].top_row) - 1) * + ((rooms[rn].right_col - rooms[rn].left_col) - 1); + n = get_rand(5, 10); + if (n > N) { + n = N - 2; + } + for (i = 0; i < n; i++) { + for (j = found = 0; ((!found) && (j < 250)); j++) { + row = get_rand(rooms[rn].top_row+1, + rooms[rn].bottom_row-1); + col = get_rand(rooms[rn].left_col+1, + rooms[rn].right_col-1); + if ((dungeon[row][col] == FLOOR) || (dungeon[row][col] == TUNNEL)) { + found = 1; + } + } + if (found) { + obj = gr_object(); + place_at(obj, row, col); + nf++; + } + } + return(nf); +} + +short +get_room_number(int row, int col) +{ + short i; + + for (i = 0; i < MAXROOMS; i++) { + if ((row >= rooms[i].top_row) && (row <= rooms[i].bottom_row) && + (col >= rooms[i].left_col) && (col <= rooms[i].right_col)) { + return(i); + } + } + return(NO_ROOM); +} + +boolean +is_all_connected(void) +{ + short i, starting_room; + + starting_room = 0; + for (i = 0; i < MAXROOMS; i++) { + rooms_visited[i] = 0; + if (rooms[i].is_room & (R_ROOM | R_MAZE)) { + starting_room = i; + } + } + + visit_rooms(starting_room); + + for (i = 0; i < MAXROOMS; i++) { + if ((rooms[i].is_room & (R_ROOM | R_MAZE)) && (!rooms_visited[i])) { + return(0); + } + } + return(1); +} + +static void +visit_rooms(int rn) +{ + short i; + short oth_rn; + + rooms_visited[rn] = 1; + + for (i = 0; i < 4; i++) { + oth_rn = rooms[rn].doors[i].oth_room; + if ((oth_rn >= 0) && (!rooms_visited[oth_rn])) { + visit_rooms(oth_rn); + } + } +} + +void +draw_magic_map(void) +{ + short i, j, ch, och; + unsigned short mask = (HORWALL | VERTWALL | DOOR | TUNNEL | TRAP | STAIRS | + MONSTER); + unsigned short s; + + for (i = 0; i < DROWS; i++) { + for (j = 0; j < DCOLS; j++) { + s = dungeon[i][j]; + if (s & mask) { + if (((ch = mvinch(i, j)) == ' ') || + ((ch >= 'A') && (ch <= 'Z')) || (s & (TRAP | HIDDEN))) { + och = ch; + dungeon[i][j] &= (~HIDDEN); + if (s & HORWALL) { + ch = '-'; + } else if (s & VERTWALL) { + ch = '|'; + } else if (s & DOOR) { + ch = '+'; + } else if (s & TRAP) { + ch = '^'; + } else if (s & STAIRS) { + ch = '%'; + } else if (s & TUNNEL) { + ch = '#'; + } else { + continue; + } + if ((!(s & MONSTER)) || (och == ' ')) { + addch(ch); + } + if (s & MONSTER) { + object *monster; + + if ((monster = object_at( + &level_monsters, i, j)) + != NULL) { + monster->trail_char = + ch; + } + } + } + } + } + } +} + +void +dr_course(object *monster, boolean entering, short row, short col) +{ + short i, j, k, rn; + short r, rr; + + monster->row = row; + monster->col = col; + + if (mon_sees(monster, rogue.row, rogue.col)) { + monster->trow = NO_ROOM; + return; + } + rn = get_room_number(row, col); + + if (entering) { /* entering room */ + /* look for door to some other room */ + r = get_rand(0, MAXROOMS-1); + for (i = 0; i < MAXROOMS; i++) { + rr = (r + i) % MAXROOMS; + if ((!(rooms[rr].is_room & (R_ROOM | R_MAZE))) || (rr == rn)) { + continue; + } + for (k = 0; k < 4; k++) { + if (rooms[rr].doors[k].oth_room == rn) { + monster->trow = rooms[rr].doors[k].oth_row; + monster->tcol = rooms[rr].doors[k].oth_col; + if ((monster->trow == row) && + (monster->tcol == col)) { + continue; + } + return; + } + } + } + /* look for door to dead end */ + if (rn == NO_ROOM) + clean_up("dr_course: monster not in room"); + for (i = rooms[rn].top_row; i <= rooms[rn].bottom_row; i++) { + for (j = rooms[rn].left_col; j <= rooms[rn].right_col; j++) { + if ((i != monster->row) && (j != monster->col) && + (dungeon[i][j] & DOOR)) { + monster->trow = i; + monster->tcol = j; + return; + } + } + } + /* return monster to room that he came from */ + for (i = 0; i < MAXROOMS; i++) { + for (j = 0; j < 4; j++) { + if (rooms[i].doors[j].oth_room == rn) { + for (k = 0; k < 4; k++) { + if (rooms[rn].doors[k].oth_room == i) { + monster->trow = rooms[rn].doors[k].oth_row; + monster->tcol = rooms[rn].doors[k].oth_col; + return; + } + } + } + } + } + /* no place to send monster */ + monster->trow = NO_ROOM; + } else { /* exiting room */ + if (rn == NO_ROOM || !get_oth_room(rn, &row, &col)) { + monster->trow = NO_ROOM; + } else { + monster->trow = row; + monster->tcol = col; + } + } +} + +static boolean +get_oth_room(short rn, short *row, short *col) +{ + short d = -1; + + if (*row == rooms[rn].top_row) { + d = UPWARD/2; + } else if (*row == rooms[rn].bottom_row) { + d = DOWN/2; + } else if (*col == rooms[rn].left_col) { + d = LEFT/2; + } else if (*col == rooms[rn].right_col) { + d = RIGHT/2; + } + if ((d != -1) && (rooms[rn].doors[d].oth_room >= 0)) { + *row = rooms[rn].doors[d].oth_row; + *col = rooms[rn].doors[d].oth_col; + return(1); + } + return(0); +} + +void +edit_opts(void) +{ + char save[NOPTS+1][DCOLS]; + short i, j; + short ch; + boolean done = 0; + char buf[MAX_OPT_LEN + 2]; + + for (i = 0; i < NOPTS+1; i++) { + for (j = 0; j < DCOLS; j++) { + save[i][j] = mvinch(i, j); + } + if (i < NOPTS) { + opt_show(i); + } + } + opt_go(0); + i = 0; + + while (!done) { + refresh(); + ch = rgetchar(); +CH: + switch(ch) { + case '\033': + done = 1; + break; + case '\012': + case '\015': + if (i == (NOPTS - 1)) { + mvaddstr(NOPTS, 0, press_space); + refresh(); + wait_for_ack(); + done = 1; + } else { + i++; + opt_go(i); + } + break; + case '-': + if (i > 0) { + opt_go(--i); + } else { + sound_bell(); + } + break; + case 't': + case 'T': + case 'f': + case 'F': + if (options[i].is_bool) { + *(options[i].bval) = (((ch == 't') || (ch == 'T')) ? 1 : 0); + opt_show(i); + opt_go(++i); + break; + } + default: + if (options[i].is_bool) { + sound_bell(); + break; + } + j = 0; + if ((ch == '\010') || ((ch >= ' ') && (ch <= '~'))) { + opt_erase(i); + do { + if ((ch >= ' ') && (ch <= '~') && (j < MAX_OPT_LEN)) { + buf[j++] = ch; + buf[j] = '\0'; + addch(ch); + } else if ((ch == '\010') && (j > 0)) { + buf[--j] = '\0'; + move(i, j + strlen(options[i].prompt)); + addch(' '); + move(i, j + strlen(options[i].prompt)); + } + refresh(); + ch = rgetchar(); + } while ((ch != '\012') && (ch != '\015') && (ch != '\033')); + if (j != 0) { + /* + * We rely on the option string being + * allocated to hold MAX_OPT_LEN+2 + * bytes. This is arranged in init.c. + */ + (void)strcpy(*(options[i].strval), buf); + } + opt_show(i); + goto CH; + } else { + sound_bell(); + } + break; + } + } + + for (i = 0; i < NOPTS+1; i++) { + move(i, 0); + for (j = 0; j < DCOLS; j++) { + addch(save[i][j]); + } + } +} + +static void +opt_show(int i) +{ + const char *s; + const struct option *opt = &options[i]; + + opt_erase(i); + + if (opt->is_bool) { + s = *(opt->bval) ? "True" : "False"; + } else { + s = *(opt->strval); + } + addstr(s); +} + +static void +opt_erase(int i) +{ + const struct option *opt = &options[i]; + + mvaddstr(i, 0, opt->prompt); + clrtoeol(); +} + +static void +opt_go(int i) +{ + move(i, strlen(options[i].prompt)); +} + +void +do_shell(void) +{ +#ifdef UNIX + const char *sh; + + md_ignore_signals(); + if (!(sh = md_getenv("SHELL"))) { + sh = "/bin/sh"; + } + move(LINES-1, 0); + refresh(); + stop_window(); + printf("\nCreating new shell...\n"); + md_shell(sh); + start_window(); + wrefresh(curscr); + md_heed_signals(); +#endif +} diff --git a/rogue/save.c b/rogue/save.c new file mode 100644 index 0000000..4a7190a --- /dev/null +++ b/rogue/save.c @@ -0,0 +1,427 @@ +/* $NetBSD: save.c,v 1.13 2008/01/14 03:50:02 dholland Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Timothy C. Stoehr. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)save.c 8.1 (Berkeley) 5/31/93"; +#else +__RCSID("$NetBSD: save.c,v 1.13 2008/01/14 03:50:02 dholland Exp $"); +#endif +#endif /* not lint */ + +/* + * save.c + * + * This source herein may be modified and/or distributed by anybody who + * so desires, with the following restrictions: + * 1.) No portion of this notice shall be removed. + * 2.) Credit shall not be taken for the creation of this source. + * 3.) This code is not to be traded, sold, or used for personal + * gain or profit. + * + */ + +#include <stdio.h> +#include "rogue.h" + +static boolean has_been_touched(const struct rogue_time *, + const struct rogue_time *); +static void r_read(FILE *, void *, size_t); +static void r_write(FILE *, const void *, size_t); +static void read_pack(object *, FILE *, boolean); +static void read_string(char *, FILE *, size_t); +static void rw_dungeon(FILE *, boolean); +static void rw_id(struct id *, FILE *, int, boolean); +static void rw_rooms(FILE *, boolean); +static void write_pack(const object *, FILE *); +static void write_string(char *, FILE *); + +static short write_failed = 0; + +char *save_file = NULL; + +void +save_game(void) +{ + char fname[64]; + + if (!get_input_line("file name?", save_file, fname, sizeof(fname), + "game not saved", 0, 1)) { + return; + } + check_message(); + messagef(0, "%s", fname); + save_into_file(fname); +} + +void +save_into_file(const char *sfile) +{ + FILE *fp; + int file_id; + char *name_buffer; + size_t len; + char *hptr; + struct rogue_time rt_buf; + + if (sfile[0] == '~') { + if ((hptr = md_getenv("HOME")) != NULL) { + len = strlen(hptr) + strlen(sfile); + name_buffer = md_malloc(len); + if (name_buffer == NULL) { + messagef(0, + "out of memory for save file name"); + sfile = error_file; + } else { + (void)strcpy(name_buffer, hptr); + (void)strcat(name_buffer, sfile+1); + sfile = name_buffer; + } + /* + * Note: name_buffer gets leaked. But it's small, + * and in the common case we're about to exit. + */ + } + } + if (((fp = fopen(sfile, "w")) == NULL) || + ((file_id = md_get_file_id(sfile)) == -1)) { + if (fp) + fclose(fp); + messagef(0, "problem accessing the save file"); + return; + } + md_ignore_signals(); + write_failed = 0; + (void)xxx(1); + r_write(fp, &detect_monster, sizeof(detect_monster)); + r_write(fp, &cur_level, sizeof(cur_level)); + r_write(fp, &max_level, sizeof(max_level)); + write_string(hunger_str, fp); + write_string(login_name, fp); + r_write(fp, &party_room, sizeof(party_room)); + write_pack(&level_monsters, fp); + write_pack(&level_objects, fp); + r_write(fp, &file_id, sizeof(file_id)); + rw_dungeon(fp, 1); + r_write(fp, &foods, sizeof(foods)); + r_write(fp, &rogue, sizeof(fighter)); + write_pack(&rogue.pack, fp); + rw_id(id_potions, fp, POTIONS, 1); + rw_id(id_scrolls, fp, SCROLS, 1); + rw_id(id_wands, fp, WANDS, 1); + rw_id(id_rings, fp, RINGS, 1); + r_write(fp, traps, (MAX_TRAPS * sizeof(trap))); + r_write(fp, is_wood, (WANDS * sizeof(boolean))); + r_write(fp, &cur_room, sizeof(cur_room)); + rw_rooms(fp, 1); + r_write(fp, &being_held, sizeof(being_held)); + r_write(fp, &bear_trap, sizeof(bear_trap)); + r_write(fp, &halluc, sizeof(halluc)); + r_write(fp, &blind, sizeof(blind)); + r_write(fp, &confused, sizeof(confused)); + r_write(fp, &levitate, sizeof(levitate)); + r_write(fp, &haste_self, sizeof(haste_self)); + r_write(fp, &see_invisible, sizeof(see_invisible)); + r_write(fp, &detect_monster, sizeof(detect_monster)); + r_write(fp, &wizard, sizeof(wizard)); + r_write(fp, &score_only, sizeof(score_only)); + r_write(fp, &m_moves, sizeof(m_moves)); + md_gct(&rt_buf); + rt_buf.second += 10; /* allow for some processing time */ + r_write(fp, &rt_buf, sizeof(rt_buf)); + fclose(fp); + + if (write_failed) { + (void)md_df(sfile); /* delete file */ + } else { + clean_up(""); + } +} + +void +restore(const char *fname) +{ + FILE *fp; + struct rogue_time saved_time, mod_time; + char buf[4]; + char tbuf[MAX_OPT_LEN]; + int new_file_id, saved_file_id; + + fp = NULL; + if (((new_file_id = md_get_file_id(fname)) == -1) || + ((fp = fopen(fname, "r")) == NULL)) { + clean_up("cannot open file"); + } + if (md_link_count(fname) > 1) { + clean_up("file has link"); + } + (void)xxx(1); + r_read(fp, &detect_monster, sizeof(detect_monster)); + r_read(fp, &cur_level, sizeof(cur_level)); + r_read(fp, &max_level, sizeof(max_level)); + read_string(hunger_str, fp, sizeof hunger_str); + + (void)strlcpy(tbuf, login_name, sizeof tbuf); + read_string(login_name, fp, sizeof login_name); + if (strcmp(tbuf, login_name)) { + clean_up("you're not the original player"); + } + + r_read(fp, &party_room, sizeof(party_room)); + read_pack(&level_monsters, fp, 0); + read_pack(&level_objects, fp, 0); + r_read(fp, &saved_file_id, sizeof(saved_file_id)); + if (new_file_id != saved_file_id) { + clean_up("sorry, saved game is not in the same file"); + } + rw_dungeon(fp, 0); + r_read(fp, &foods, sizeof(foods)); + r_read(fp, &rogue, sizeof(fighter)); + read_pack(&rogue.pack, fp, 1); + rw_id(id_potions, fp, POTIONS, 0); + rw_id(id_scrolls, fp, SCROLS, 0); + rw_id(id_wands, fp, WANDS, 0); + rw_id(id_rings, fp, RINGS, 0); + r_read(fp, traps, (MAX_TRAPS * sizeof(trap))); + r_read(fp, is_wood, (WANDS * sizeof(boolean))); + r_read(fp, &cur_room, sizeof(cur_room)); + rw_rooms(fp, 0); + r_read(fp, &being_held, sizeof(being_held)); + r_read(fp, &bear_trap, sizeof(bear_trap)); + r_read(fp, &halluc, sizeof(halluc)); + r_read(fp, &blind, sizeof(blind)); + r_read(fp, &confused, sizeof(confused)); + r_read(fp, &levitate, sizeof(levitate)); + r_read(fp, &haste_self, sizeof(haste_self)); + r_read(fp, &see_invisible, sizeof(see_invisible)); + r_read(fp, &detect_monster, sizeof(detect_monster)); + r_read(fp, &wizard, sizeof(wizard)); + r_read(fp, &score_only, sizeof(score_only)); + r_read(fp, &m_moves, sizeof(m_moves)); + r_read(fp, &saved_time, sizeof(saved_time)); + + if (fread(buf, 1, 1, fp) > 0) { + clear(); + clean_up("extra characters in file"); + } + + md_gfmt(fname, &mod_time); /* get file modification time */ + + if (has_been_touched(&saved_time, &mod_time)) { + clear(); + clean_up("sorry, file has been touched"); + } + if ((!wizard) && !md_df(fname)) { + clean_up("cannot delete file"); + } + msg_cleared = 0; + ring_stats(0); + fclose(fp); +} + +static void +write_pack(const object *pack, FILE *fp) +{ + object t; + + while ((pack = pack->next_object) != NULL) { + r_write(fp, pack, sizeof(object)); + } + t.ichar = t.what_is = 0; + r_write(fp, &t, sizeof(object)); +} + +static void +read_pack(object *pack, FILE *fp, boolean is_rogue) +{ + object read_obj, *new_obj; + + for (;;) { + r_read(fp, &read_obj, sizeof(object)); + if (read_obj.ichar == 0) { + pack->next_object = NULL; + break; + } + new_obj = alloc_object(); + *new_obj = read_obj; + if (is_rogue) { + if (new_obj->in_use_flags & BEING_WORN) { + do_wear(new_obj); + } else if (new_obj->in_use_flags & BEING_WIELDED) { + do_wield(new_obj); + } else if (new_obj->in_use_flags & (ON_EITHER_HAND)) { + do_put_on(new_obj, + ((new_obj->in_use_flags & ON_LEFT_HAND) ? 1 : 0)); + } + } + pack->next_object = new_obj; + pack = new_obj; + } +} + +static void +rw_dungeon(FILE *fp, boolean rw) +{ + short i, j; + char buf[DCOLS]; + + for (i = 0; i < DROWS; i++) { + if (rw) { + r_write(fp, dungeon[i], (DCOLS * sizeof(dungeon[0][0]))); + for (j = 0; j < DCOLS; j++) { + buf[j] = mvinch(i, j); + } + r_write(fp, buf, DCOLS); + } else { + r_read(fp, dungeon[i], (DCOLS * sizeof(dungeon[0][0]))); + r_read(fp, buf, DCOLS); + for (j = 0; j < DCOLS; j++) { + mvaddch(i, j, buf[j]); + } + } + } +} + +static void +rw_id(struct id id_table[], FILE *fp, int n, boolean wr) +{ + int i; + + for (i = 0; i < n; i++) { + if (wr) { + r_write(fp, &id_table[i].value, sizeof(short)); + r_write(fp, &id_table[i].id_status, + sizeof(unsigned short)); + write_string(id_table[i].title, fp); + } else { + r_read(fp, &id_table[i].value, sizeof(short)); + r_read(fp, &id_table[i].id_status, + sizeof(unsigned short)); + read_string(id_table[i].title, fp, MAX_ID_TITLE_LEN); + } + } +} + +static void +write_string(char *s, FILE *fp) +{ + short n; + + n = strlen(s) + 1; + xxxx(s, n); + r_write(fp, &n, sizeof(short)); + r_write(fp, s, n); +} + +static void +read_string(char *s, FILE *fp, size_t len) +{ + short n; + + r_read(fp, &n, sizeof(short)); + if (n<=0 || (size_t)(unsigned short)n > len) { + clean_up("read_string: corrupt game file"); + } + r_read(fp, s, n); + xxxx(s, n); + /* ensure null termination */ + s[n-1] = 0; +} + +static void +rw_rooms(FILE *fp, boolean rw) +{ + short i; + + for (i = 0; i < MAXROOMS; i++) { + rw ? r_write(fp, (rooms + i), sizeof(room)) : + r_read(fp, (rooms + i), sizeof(room)); + } +} + +static void +r_read(FILE *fp, void *buf, size_t n) +{ + if (fread(buf, 1, n, fp) != n) { + clean_up("fread() failed, don't know why"); + } +} + +static void +r_write(FILE *fp, const void *buf, size_t n) +{ + if (!write_failed) { + if (fwrite(buf, 1, n, fp) != n) { + messagef(0, "write() failed, don't know why"); + sound_bell(); + write_failed = 1; + } + } +} + +static boolean +has_been_touched(const struct rogue_time *saved_time, + const struct rogue_time *mod_time) +{ + if (saved_time->year < mod_time->year) { + return(1); + } else if (saved_time->year > mod_time->year) { + return(0); + } + if (saved_time->month < mod_time->month) { + return(1); + } else if (saved_time->month > mod_time->month) { + return(0); + } + if (saved_time->day < mod_time->day) { + return(1); + } else if (saved_time->day > mod_time->day) { + return(0); + } + if (saved_time->hour < mod_time->hour) { + return(1); + } else if (saved_time->hour > mod_time->hour) { + return(0); + } + if (saved_time->minute < mod_time->minute) { + return(1); + } else if (saved_time->minute > mod_time->minute) { + return(0); + } + if (saved_time->second < mod_time->second) { + return(1); + } + return(0); +} diff --git a/rogue/score.c b/rogue/score.c new file mode 100644 index 0000000..4bcf4e3 --- /dev/null +++ b/rogue/score.c @@ -0,0 +1,674 @@ +/* $NetBSD: score.c,v 1.16 2011/08/26 06:18:17 dholland Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Timothy C. Stoehr. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)score.c 8.1 (Berkeley) 5/31/93"; +#else +__RCSID("$NetBSD: score.c,v 1.16 2011/08/26 06:18:17 dholland Exp $"); +#endif +#endif /* not lint */ + +/* + * score.c + * + * This source herein may be modified and/or distributed by anybody who + * so desires, with the following restrictions: + * 1.) No portion of this notice shall be removed. + * 2.) Credit shall not be taken for the creation of this source. + * 3.) This code is not to be traded, sold, or used for personal + * gain or profit. + * + */ + +#include <stdio.h> +#include "rogue.h" +#include "pathnames.h" + +static void center(short, const char *); +static int get_value(const object *); +static void id_all(void); +static void sell_pack(void); +static void sf_error(void) __dead; + +void +killed_by(const object *monster, short other) +{ + const char *mechanism = "killed by something unknown (?)"; + char mechanism_buf[128]; + const char *article; + char message_buf[128]; + + md_ignore_signals(); + + if (other != QUIT) { + rogue.gold = ((rogue.gold * 9) / 10); + } + + if (other) { + switch(other) { + case HYPOTHERMIA: + mechanism = "died of hypothermia"; + break; + case STARVATION: + mechanism = "died of starvation"; + break; + case POISON_DART: + mechanism = "killed by a dart"; + break; + case QUIT: + mechanism = "quit"; + break; + case KFIRE: + mechanism = "killed by fire"; + break; + } + } else { + if (is_vowel(m_names[monster->m_char - 'A'][0])) { + article = "an"; + } else { + article = "a"; + } + snprintf(mechanism_buf, sizeof(mechanism_buf), + "Killed by %s %s", + article, m_names[monster->m_char - 'A']); + mechanism = mechanism_buf; + } + snprintf(message_buf, sizeof(message_buf), + "%s with %ld gold", mechanism, rogue.gold); + + if ((!other) && (!no_skull)) { + clear(); + mvaddstr(4, 32, "__---------__"); + mvaddstr(5, 30, "_~ ~_"); + mvaddstr(6, 29, "/ \\"); + mvaddstr(7, 28, "~ ~"); + mvaddstr(8, 27, "/ \\"); + mvaddstr(9, 27, "| XXXX XXXX |"); + mvaddstr(10, 27, "| XXXX XXXX |"); + mvaddstr(11, 27, "| XXX XXX |"); + mvaddstr(12, 28, "\\ @ /"); + mvaddstr(13, 29, "--\\ @@@ /--"); + mvaddstr(14, 30, "| | @@@ | |"); + mvaddstr(15, 30, "| | | |"); + mvaddstr(16, 30, "| vvVvvvvvvvVvv |"); + mvaddstr(17, 30, "| ^^^^^^^^^^^ |"); + mvaddstr(18, 31, "\\_ _/"); + mvaddstr(19, 33, "~---------~"); + center(21, nick_name); + center(22, message_buf); + } else { + messagef(0, "%s", message_buf); + } + messagef(0, "%s", ""); /* gcc objects to just "" */ + put_scores(monster, other); +} + +void +win(void) +{ + unwield(rogue.weapon); /* disarm and relax */ + unwear(rogue.armor); + un_put_on(rogue.left_ring); + un_put_on(rogue.right_ring); + + clear(); + mvaddstr(10, 11, "@ @ @@@ @ @ @ @ @ @@@ @ @ @"); + mvaddstr(11, 11, " @ @ @ @ @ @ @ @ @ @ @ @@ @ @"); + mvaddstr(12, 11, " @ @ @ @ @ @ @ @ @ @ @ @ @ @"); + mvaddstr(13, 11, " @ @ @ @ @ @ @ @ @ @ @ @@"); + mvaddstr(14, 11, " @ @@@ @@@ @@ @@ @@@ @ @ @"); + mvaddstr(17, 11, "Congratulations, you have been admitted to the"); + mvaddstr(18, 11, "Fighters' Guild. You return home, sell all your"); + mvaddstr(19, 11, "treasures at great profit and retire into comfort."); + messagef(0, "%s", ""); /* gcc objects to just "" */ + messagef(0, "%s", ""); /* gcc objects to just "" */ + id_all(); + sell_pack(); + put_scores(NULL, WIN); +} + +void +quit(boolean from_intrpt) +{ + char buf[DCOLS]; + short i, orow, ocol; + boolean mc; + + orow = ocol = 0; + mc = FALSE; + md_ignore_signals(); + + if (from_intrpt) { + orow = rogue.row; + ocol = rogue.col; + + mc = msg_cleared; + + for (i = 0; i < DCOLS; i++) { + buf[i] = mvinch(0, i); + } + } + check_message(); + messagef(1, "really quit?"); + if (rgetchar() != 'y') { + md_heed_signals(); + check_message(); + if (from_intrpt) { + for (i = 0; i < DCOLS; i++) { + mvaddch(0, i, buf[i]); + } + msg_cleared = mc; + move(orow, ocol); + refresh(); + } + return; + } + if (from_intrpt) { + clean_up(byebye_string); + } + check_message(); + killed_by(NULL, QUIT); +} + +/* + * The score file on disk is up to ten entries of the form + * score block [80 bytes] + * nickname block [30 bytes] + * + * The score block is to be parsed as follows: + * bytes 0-1 Rank (" 1" to "10") + * bytes 2-4 space padding + * bytes 5-15 Score/gold + * byte 15 up to a ':' Login name + * past the ':' Death mechanism + * + * The nickname block is an alternate name to be printed in place of the + * login name. Both blocks are supposed to contain a null-terminator. + */ + +struct score_entry { + long gold; + char username[80]; + char death[80]; + char nickname[30]; +}; + +#define NUM_SCORE_ENTRIES 10 + +static void make_score(struct score_entry *, const object *, int); + +static +void +pad_spaces(char *str, size_t len) +{ + size_t x; + for (x=strlen(str); x<len-1; x++) { + str[x] = ' '; + } + str[len-1] = 0; +} + +static +void +unpad_spaces(char *str) +{ + size_t x; + for (x=strlen(str); x>0 && str[x-1]==' '; x--); + str[x] = 0; +} + +static +int +read_score_entry(struct score_entry *se, FILE *fp) +{ + char score_block[80]; + char nickname_block[30]; + size_t n, x; + + n = fread(score_block, 1, sizeof(score_block), fp); + if (n==0) { + /* EOF */ + return 0; + } + if (n != sizeof(score_block)) { + sf_error(); + } + + n = fread(nickname_block, 1, sizeof(nickname_block), fp); + if (n != sizeof(nickname_block)) { + sf_error(); + } + + xxxx(score_block, sizeof(score_block)); + xxxx(nickname_block, sizeof(nickname_block)); + + /* Ensure null termination */ + score_block[sizeof(score_block)-1] = 0; + nickname_block[sizeof(nickname_block)-1] = 0; + + /* If there are other nulls in the score block, file is corrupt */ + if (strlen(score_block)!=sizeof(score_block)-1) { + sf_error(); + } + /* but this is NOT true of the nickname block */ + + /* quash trailing spaces */ + unpad_spaces(score_block); + unpad_spaces(nickname_block); + + for (x=5; score_block[x] == ' '; x++); + se->gold = lget_number(score_block+x); + + for (x=15; score_block[x] != 0 && score_block[x] != ':'; x++); + if (score_block[x] == 0) { + sf_error(); + } + score_block[x++] = 0; + strlcpy(se->username, score_block+15, sizeof(se->username)); + + strlcpy(se->death, score_block+x, sizeof(se->death)); + strlcpy(se->nickname, nickname_block, sizeof(se->nickname)); + + return 1; +} + +static +void +write_score_entry(const struct score_entry *se, int rank, FILE *fp) +{ + char score_block[80]; + char nickname_block[30]; + + /* avoid writing crap to score file */ + memset(score_block, 0, sizeof(score_block)); + memset(nickname_block, 0, sizeof(nickname_block)); + + snprintf(score_block, sizeof(score_block), + "%2d %6ld %s: %s", + rank+1, se->gold, se->username, se->death); + strlcpy(nickname_block, se->nickname, sizeof(nickname_block)); + + /* pad blocks out with spaces */ + pad_spaces(score_block, sizeof(score_block)); + /*pad_spaces(nickname_block, sizeof(nickname_block)); -- wrong! */ + + xxxx(score_block, sizeof(score_block)); + xxxx(nickname_block, sizeof(nickname_block)); + + fwrite(score_block, 1, sizeof(score_block), fp); + fwrite(nickname_block, 1, sizeof(nickname_block), fp); +} + +void +put_scores(const object *monster, short other) +{ + short i, rank=-1, found_player = -1, numscores = 0; + struct score_entry scores[NUM_SCORE_ENTRIES]; + const char *name; + FILE *fp; + boolean dopause = score_only; + + md_lock(1); + + setegid(egid); + if ((fp = fopen(_PATH_SCOREFILE, "r+")) == NULL && + (fp = fopen(_PATH_SCOREFILE, "w+")) == NULL) { + setegid(gid); + messagef(0, "cannot read/write/create score file"); + sf_error(); + } + setegid(gid); + rewind(fp); + (void)xxx(1); + + for (numscores = 0; numscores < NUM_SCORE_ENTRIES; numscores++) { + if (read_score_entry(&scores[numscores], fp) == 0) { + break; + } + } + + /* Search the score list. */ + for (i=0; i<numscores; i++) { + if (!strcmp(scores[i].username, login_name)) { + /* found our score */ + if (rogue.gold < scores[i].gold) { + /* we didn't do as well as last time */ + score_only = 1; + } else { + /* we did better; mark entry for removal */ + found_player = i; + } + break; + } + } + + /* Remove a superseded entry, if any. */ + if (found_player != -1) { + numscores--; + for (i = found_player; i < numscores; i++) { + scores[i] = scores[i+1]; + } + } + + /* If we're going to insert ourselves, do it now */ + if (!score_only) { + + /* if we aren't better than anyone, add at end. */ + rank = numscores; + + /* Otherwise, find our slot. */ + for (i = 0; i < numscores; i++) { + if (rogue.gold >= scores[i].gold) { + rank = i; + break; + } + } + + if (rank < NUM_SCORE_ENTRIES) { + /* Open up a slot */ + for (i = numscores; i > rank; i--) { + scores[i] = scores[i-1]; + } + numscores++; + + /* Put our info in the slot */ + make_score(&scores[rank], monster, other); + } + + /* Now rewrite the score file */ + + md_ignore_signals(); + rewind(fp); + (void)xxx(1); + + for (i = 0; i < numscores; i++) { + write_score_entry(&scores[i], i, fp); + } + } + md_lock(0); + fclose(fp); + + /* Display the scores */ + + clear(); + mvaddstr(3, 30, "Top Ten Rogueists"); + mvaddstr(8, 0, "Rank Score Name"); + + for (i = 0; i < numscores; i++) { + if (i == rank) { + standout(); + } + + if (scores[i].nickname[0]) { + name = scores[i].nickname; + } else { + name = scores[i].username; + } + + mvprintw(i+10, 0, "%2d %6ld %s: %s", + i+1, scores[i].gold, name, scores[i].death); + + if (i == rank) { + standend(); + } + } + refresh(); + messagef(0, "%s", ""); /* gcc objects to just "" */ + if (dopause) { + messagef(0, "%s", ""); + } + clean_up(""); +} + +static +void +make_score(struct score_entry *se, const object *monster, int other) +{ + const char *death = "bolts from the blue (?)"; + const char *hasamulet; + char deathbuf[80]; + + se->gold = rogue.gold; + strlcpy(se->username, login_name, sizeof(se->username)); + + if (other) { + switch(other) { + case HYPOTHERMIA: + death = "died of hypothermia"; + break; + case STARVATION: + death = "died of starvation"; + break; + case POISON_DART: + death = "killed by a dart"; + break; + case QUIT: + death = "quit"; + break; + case WIN: + death = "a total winner"; + break; + case KFIRE: + death = "killed by fire"; + break; + } + } else { + const char *mn, *article; + + mn = m_names[monster->m_char - 'A']; + if (is_vowel(mn[0])) { + article = "an"; + } else { + article = "a"; + } + + snprintf(deathbuf, sizeof(deathbuf), + "killed by %s %s", article, mn); + death = deathbuf; + } + + if (other != WIN && has_amulet()) { + hasamulet = " with amulet"; + } else { + hasamulet = ""; + } + + snprintf(se->death, sizeof(se->death), "%s on level %d%s", + death, max_level, hasamulet); + + strlcpy(se->nickname, nick_name, sizeof(se->nickname)); +} + +boolean +is_vowel(short ch) +{ + return( (ch == 'a') || + (ch == 'e') || + (ch == 'i') || + (ch == 'o') || + (ch == 'u') ); +} + +static void +sell_pack(void) +{ + object *obj; + short row = 2, val; + char buf[DCOLS]; + + obj = rogue.pack.next_object; + + clear(); + mvaddstr(1, 0, "Value Item"); + + while (obj) { + if (obj->what_is != FOOD) { + obj->identified = 1; + val = get_value(obj); + rogue.gold += val; + + if (row < DROWS) { + get_desc(obj, buf, sizeof(buf)); + mvprintw(row++, 0, "%5d %s", val, buf); + } + } + obj = obj->next_object; + } + refresh(); + if (rogue.gold > MAX_GOLD) { + rogue.gold = MAX_GOLD; + } + messagef(0, "%s", ""); /* gcc objects to just "" */ +} + +static int +get_value(const object *obj) +{ + short wc; + int val; + + val = 0; + wc = obj->which_kind; + + switch(obj->what_is) { + case WEAPON: + val = id_weapons[wc].value; + if ((wc == ARROW) || (wc == DAGGER) || (wc == SHURIKEN) || + (wc == DART)) { + val *= obj->quantity; + } + val += (obj->d_enchant * 85); + val += (obj->hit_enchant * 85); + break; + case ARMOR: + val = id_armors[wc].value; + val += (obj->d_enchant * 75); + if (obj->is_protected) { + val += 200; + } + break; + case WAND: + val = id_wands[wc].value * (obj->class + 1); + break; + case SCROL: + val = id_scrolls[wc].value * obj->quantity; + break; + case POTION: + val = id_potions[wc].value * obj->quantity; + break; + case AMULET: + val = 5000; + break; + case RING: + val = id_rings[wc].value * (obj->class + 1); + break; + } + if (val <= 0) { + val = 10; + } + return(val); +} + +static void +id_all(void) +{ + short i; + + for (i = 0; i < SCROLS; i++) { + id_scrolls[i].id_status = IDENTIFIED; + } + for (i = 0; i < WEAPONS; i++) { + id_weapons[i].id_status = IDENTIFIED; + } + for (i = 0; i < ARMORS; i++) { + id_armors[i].id_status = IDENTIFIED; + } + for (i = 0; i < WANDS; i++) { + id_wands[i].id_status = IDENTIFIED; + } + for (i = 0; i < POTIONS; i++) { + id_potions[i].id_status = IDENTIFIED; + } +} + +void +xxxx(char *buf, short n) +{ + short i; + unsigned char c; + + for (i = 0; i < n; i++) { + + /* It does not matter if accuracy is lost during this assignment */ + c = (unsigned char)xxx(0); + + buf[i] ^= c; + } +} + +long +xxx(boolean st) +{ + static long f, s; + long r; + + if (st) { + f = 37; + s = 7; + return(0L); + } + r = ((f * s) + 9337) % 8887; + f = s; + s = r; + return(r); +} + +static void +center(short row, const char *buf) +{ + short margin; + + margin = ((DCOLS - strlen(buf)) / 2); + mvaddstr(row, margin, buf); +} + +static void +sf_error(void) +{ + md_lock(0); + messagef(1, "%s", ""); /* gcc objects to just "" */ + clean_up("sorry, score file is out of order"); +} diff --git a/rogue/spec_hit.c b/rogue/spec_hit.c new file mode 100644 index 0000000..69e314c --- /dev/null +++ b/rogue/spec_hit.c @@ -0,0 +1,536 @@ +/* $NetBSD: spec_hit.c,v 1.9 2011/05/23 23:01:17 joerg Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Timothy C. Stoehr. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)spec_hit.c 8.1 (Berkeley) 5/31/93"; +#else +__RCSID("$NetBSD: spec_hit.c,v 1.9 2011/05/23 23:01:17 joerg Exp $"); +#endif +#endif /* not lint */ + +/* + * special_hit.c + * + * This source herein may be modified and/or distributed by anybody who + * so desires, with the following restrictions: + * 1.) No portion of this notice shall be removed. + * 2.) Credit shall not be taken for the creation of this source. + * 3.) This code is not to be traded, sold, or used for personal + * gain or profit. + * + */ + +#include "rogue.h" + +static void disappear(object *); +static void drain_life(void); +static void drop_level(void); +static void freeze(object *); +static int get_dir(short, short, short, short); +static boolean gold_at(short, short); +static void steal_gold(object *); +static void steal_item(object *); +static void sting(object *); +static boolean try_to_cough(short, short, object *); + +short less_hp = 0; +boolean being_held; + +void +special_hit(object *monster) +{ + if ((monster->m_flags & CONFUSED) && rand_percent(66)) { + return; + } + if (monster->m_flags & RUSTS) { + rust(monster); + } + if ((monster->m_flags & HOLDS) && !levitate) { + being_held = 1; + } + if (monster->m_flags & FREEZES) { + freeze(monster); + } + if (monster->m_flags & STINGS) { + sting(monster); + } + if (monster->m_flags & DRAINS_LIFE) { + drain_life(); + } + if (monster->m_flags & DROPS_LEVEL) { + drop_level(); + } + if (monster->m_flags & STEALS_GOLD) { + steal_gold(monster); + } else if (monster->m_flags & STEALS_ITEM) { + steal_item(monster); + } +} + +void +rust(object *monster) +{ + if ((!rogue.armor) || (get_armor_class(rogue.armor) <= 1) || + (rogue.armor->which_kind == LEATHER)) { + return; + } + if ((rogue.armor->is_protected) || maintain_armor) { + if (monster && (!(monster->m_flags & RUST_VANISHED))) { + messagef(0, "the rust vanishes instantly"); + monster->m_flags |= RUST_VANISHED; + } + } else { + rogue.armor->d_enchant--; + messagef(0, "your armor weakens"); + print_stats(STAT_ARMOR); + } +} + +void +freeze(object *monster) +{ + short freeze_percent = 99; + short i, n; + + if (rand_percent(12)) { + return; + } + freeze_percent -= (rogue.str_current+(rogue.str_current / 2)); + freeze_percent -= ((rogue.exp + ring_exp) * 4); + freeze_percent -= (get_armor_class(rogue.armor) * 5); + freeze_percent -= (rogue.hp_max / 3); + + if (freeze_percent > 10) { + monster->m_flags |= FREEZING_ROGUE; + messagef(1, "you are frozen"); + + n = get_rand(4, 8); + for (i = 0; i < n; i++) { + mv_mons(); + } + if (rand_percent(freeze_percent)) { + for (i = 0; i < 50; i++) { + mv_mons(); + } + killed_by(NULL, HYPOTHERMIA); + } + messagef(1, "%s", you_can_move_again); + monster->m_flags &= (~FREEZING_ROGUE); + } +} + +void +steal_gold(object *monster) +{ + int amount; + + if ((rogue.gold <= 0) || rand_percent(10)) { + return; + } + + amount = get_rand((cur_level * 10), (cur_level * 30)); + + if (amount > rogue.gold) { + amount = rogue.gold; + } + rogue.gold -= amount; + messagef(0, "your purse feels lighter"); + print_stats(STAT_GOLD); + disappear(monster); +} + +void +steal_item(object *monster) +{ + object *obj; + short i, n, t = 0; + char desc[80]; + boolean has_something = 0; + + if (rand_percent(15)) { + return; + } + obj = rogue.pack.next_object; + + if (!obj) { + goto DSPR; + } + while (obj) { + if (!(obj->in_use_flags & BEING_USED)) { + has_something = 1; + break; + } + obj = obj->next_object; + } + if (!has_something) { + goto DSPR; + } + n = get_rand(0, MAX_PACK_COUNT); + obj = rogue.pack.next_object; + + for (i = 0; i <= n; i++) { + obj = obj->next_object; + while ((!obj) || (obj->in_use_flags & BEING_USED)) { + if (!obj) { + obj = rogue.pack.next_object; + } else { + obj = obj->next_object; + } + } + } + if (obj->what_is != WEAPON) { + t = obj->quantity; + obj->quantity = 1; + } + get_desc(obj, desc, sizeof(desc)); + messagef(0, "she stole %s", desc); + + obj->quantity = ((obj->what_is != WEAPON) ? t : 1); + + vanish(obj, 0, &rogue.pack); +DSPR: + disappear(monster); +} + +static void +disappear(object *monster) +{ + short row, col; + + row = monster->row; + col = monster->col; + + dungeon[row][col] &= ~MONSTER; + if (rogue_can_see(row, col)) { + mvaddch(row, col, get_dungeon_char(row, col)); + } + take_from_pack(monster, &level_monsters); + free_object(monster); + mon_disappeared = 1; +} + +void +cough_up(object *monster) +{ + object *obj; + short row, col, i, n; + + if (cur_level < max_level) { + return; + } + + if (monster->m_flags & STEALS_GOLD) { + obj = alloc_object(); + obj->what_is = GOLD; + obj->quantity = get_rand((cur_level * 15), (cur_level * 30)); + } else { + if (!rand_percent((int)monster->drop_percent)) { + return; + } + obj = gr_object(); + } + row = monster->row; + col = monster->col; + + for (n = 0; n <= 5; n++) { + for (i = -n; i <= n; i++) { + if (try_to_cough(row+n, col+i, obj)) { + return; + } + if (try_to_cough(row-n, col+i, obj)) { + return; + } + } + for (i = -n; i <= n; i++) { + if (try_to_cough(row+i, col-n, obj)) { + return; + } + if (try_to_cough(row+i, col+n, obj)) { + return; + } + } + } + free_object(obj); +} + +static boolean +try_to_cough(short row, short col, object *obj) +{ + if ((row < MIN_ROW) || + (row > (DROWS-2)) || (col < 0) || (col>(DCOLS-1))) { + return(0); + } + if ((!(dungeon[row][col] & (OBJECT | STAIRS | TRAP))) && + (dungeon[row][col] & (TUNNEL | FLOOR | DOOR))) { + place_at(obj, row, col); + if (((row != rogue.row) || (col != rogue.col)) && + (!(dungeon[row][col] & MONSTER))) { + mvaddch(row, col, get_dungeon_char(row, col)); + } + return(1); + } + return(0); +} + +boolean +seek_gold(object *monster) +{ + short i, j, rn, s; + + if ((rn = get_room_number(monster->row, monster->col)) < 0) { + return(0); + } + for (i = rooms[rn].top_row+1; i < rooms[rn].bottom_row; i++) { + for (j = rooms[rn].left_col+1; j < rooms[rn].right_col; j++) { + if ((gold_at(i, j)) && !(dungeon[i][j] & MONSTER)) { + monster->m_flags |= CAN_FLIT; + s = mon_can_go(monster, i, j); + monster->m_flags &= (~CAN_FLIT); + if (s) { + move_mon_to(monster, i, j); + monster->m_flags |= ASLEEP; + monster->m_flags &= (~(WAKENS | SEEKS_GOLD)); + return(1); + } + monster->m_flags &= (~SEEKS_GOLD); + monster->m_flags |= CAN_FLIT; + mv_1_monster(monster, i, j); + monster->m_flags &= (~CAN_FLIT); + monster->m_flags |= SEEKS_GOLD; + return(1); + } + } + } + return(0); +} + +static boolean +gold_at(short row, short col) +{ + if (dungeon[row][col] & OBJECT) { + object *obj; + + if ((obj = object_at(&level_objects, row, col)) && + (obj->what_is == GOLD)) { + return(1); + } + } + return(0); +} + +void +check_gold_seeker(object *monster) +{ + monster->m_flags &= (~SEEKS_GOLD); +} + +boolean +check_imitator(object *monster) +{ + if (monster->m_flags & IMITATES) { + wake_up(monster); + if (!blind) { + mvaddch(monster->row, monster->col, + get_dungeon_char(monster->row, monster->col)); + check_message(); + messagef(1, "wait, that's a %s!", mon_name(monster)); + } + return(1); + } + return(0); +} + +boolean +imitating(short row, short col) +{ + if (dungeon[row][col] & MONSTER) { + object *monster; + + if ((monster = object_at(&level_monsters, row, col)) != NULL) { + if (monster->m_flags & IMITATES) { + return(1); + } + } + } + return(0); +} + +static void +sting(object *monster) +{ + short sting_chance = 35; + + if ((rogue.str_current <= 3) || sustain_strength) { + return; + } + sting_chance += (6 * (6 - get_armor_class(rogue.armor))); + + if ((rogue.exp + ring_exp) > 8) { + sting_chance -= (6 * ((rogue.exp + ring_exp) - 8)); + } + if (rand_percent(sting_chance)) { + messagef(0, "the %s's bite has weakened you", + mon_name(monster)); + rogue.str_current--; + print_stats(STAT_STRENGTH); + } +} + +static void +drop_level(void) +{ + int hp; + + if (rand_percent(80) || (rogue.exp <= 5)) { + return; + } + rogue.exp_points = level_points[rogue.exp-2] - get_rand(9, 29); + rogue.exp -= 2; + hp = hp_raise(); + if ((rogue.hp_current -= hp) <= 0) { + rogue.hp_current = 1; + } + if ((rogue.hp_max -= hp) <= 0) { + rogue.hp_max = 1; + } + add_exp(1, 0); +} + +void +drain_life(void) +{ + short n; + + if (rand_percent(60) || (rogue.hp_max <= 30) || (rogue.hp_current < 10)) { + return; + } + n = get_rand(1, 3); /* 1 Hp, 2 Str, 3 both */ + + if ((n != 2) || (!sustain_strength)) { + messagef(0, "you feel weaker"); + } + if (n != 2) { + rogue.hp_max--; + rogue.hp_current--; + less_hp++; + } + if (n != 1) { + if ((rogue.str_current > 3) && (!sustain_strength)) { + rogue.str_current--; + if (coin_toss()) { + rogue.str_max--; + } + } + } + print_stats((STAT_STRENGTH | STAT_HP)); +} + +boolean +m_confuse(object *monster) +{ + if (!rogue_can_see(monster->row, monster->col)) { + return(0); + } + if (rand_percent(45)) { + monster->m_flags &= (~CONFUSES); /* will not confuse the rogue */ + return(0); + } + if (rand_percent(55)) { + monster->m_flags &= (~CONFUSES); + messagef(1, "the gaze of the %s has confused you", + mon_name(monster)); + cnfs(); + return(1); + } + return(0); +} + +boolean +flame_broil(object *monster) +{ + short row, col, dir; + + if ((!mon_sees(monster, rogue.row, rogue.col)) || coin_toss()) { + return(0); + } + row = rogue.row - monster->row; + col = rogue.col - monster->col; + if (row < 0) { + row = -row; + } + if (col < 0) { + col = -col; + } + if (((row != 0) && (col != 0) && (row != col)) || + ((row > 7) || (col > 7))) { + return(0); + } + dir = get_dir(monster->row, monster->col, row, col); + bounce(FIRE, dir, monster->row, monster->col, 0); + + return(1); +} + +static int +get_dir(short srow, short scol, short drow, short dcol) +{ + if (srow == drow) { + if (scol < dcol) { + return(RIGHT); + } else { + return(LEFT); + } + } + if (scol == dcol) { + if (srow < drow) { + return(DOWN); + } else { + return(UPWARD); + } + } + if ((srow > drow) && (scol > dcol)) { + return(UPLEFT); + } + if ((srow < drow) && (scol < dcol)) { + return(DOWNRIGHT); + } + if ((srow < drow) && (scol > dcol)) { + return(DOWNLEFT); + } + /*if ((srow > drow) && (scol < dcol)) {*/ + return(UPRIGHT); + /*}*/ +} diff --git a/rogue/throw.c b/rogue/throw.c new file mode 100644 index 0000000..93c88c5 --- /dev/null +++ b/rogue/throw.c @@ -0,0 +1,324 @@ +/* $NetBSD: throw.c,v 1.12 2011/05/23 23:01:17 joerg Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Timothy C. Stoehr. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)throw.c 8.1 (Berkeley) 5/31/93"; +#else +__RCSID("$NetBSD: throw.c,v 1.12 2011/05/23 23:01:17 joerg Exp $"); +#endif +#endif /* not lint */ + +/* + * throw.c + * + * This source herein may be modified and/or distributed by anybody who + * so desires, with the following restrictions: + * 1.) No portion of this notice shall be removed. + * 2.) Credit shall not be taken for the creation of this source. + * 3.) This code is not to be traded, sold, or used for personal + * gain or profit. + * + */ + +#include "rogue.h" + +static void flop_weapon(object *, short, short); +static object *get_thrown_at_monster(object *, short, short *, short *); +static boolean throw_at_monster(object *, object *); + +void +throw(void) +{ + short wch, d; + boolean first_miss = 1; + object *weapon; + short dir, row, col; + object *monster; + + while (!is_direction(dir = rgetchar(), &d)) { + sound_bell(); + if (first_miss) { + messagef(0, "direction? "); + first_miss = 0; + } + } + check_message(); + if (dir == CANCEL) { + return; + } + if ((wch = pack_letter("throw what?", WEAPON)) == CANCEL) { + return; + } + check_message(); + + if (!(weapon = get_letter_object(wch))) { + messagef(0, "no such item."); + return; + } + if ((weapon->in_use_flags & BEING_USED) && weapon->is_cursed) { + messagef(0, "%s", curse_message); + return; + } + row = rogue.row; col = rogue.col; + + if ((weapon->in_use_flags & BEING_WIELDED) && (weapon->quantity <= 1)) { + unwield(rogue.weapon); + } else if (weapon->in_use_flags & BEING_WORN) { + mv_aquatars(); + unwear(rogue.armor); + print_stats(STAT_ARMOR); + } else if (weapon->in_use_flags & ON_EITHER_HAND) { + un_put_on(weapon); + } + monster = get_thrown_at_monster(weapon, d, &row, &col); + mvaddch(rogue.row, rogue.col, rogue.fchar); + refresh(); + + if (rogue_can_see(row, col) && ((row != rogue.row) || (col != rogue.col))){ + mvaddch(row, col, get_dungeon_char(row, col)); + } + if (monster) { + wake_up(monster); + check_gold_seeker(monster); + + if (!throw_at_monster(monster, weapon)) { + flop_weapon(weapon, row, col); + } + } else { + flop_weapon(weapon, row, col); + } + vanish(weapon, 1, &rogue.pack); +} + +boolean +throw_at_monster(object *monster, object *weapon) +{ + short damage, hit_chance; + short t; + + hit_chance = get_hit_chance(weapon); + damage = get_weapon_damage(weapon); + if ((weapon->which_kind == ARROW) && + (rogue.weapon && (rogue.weapon->which_kind == BOW))) { + damage += get_weapon_damage(rogue.weapon); + damage = ((damage * 2) / 3); + hit_chance += (hit_chance / 3); + } else if ((weapon->in_use_flags & BEING_WIELDED) && + ((weapon->which_kind == DAGGER) || + (weapon->which_kind == SHURIKEN) || + (weapon->which_kind == DART))) { + damage = ((damage * 3) / 2); + hit_chance += (hit_chance / 3); + } + t = weapon->quantity; + weapon->quantity = 1; + snprintf(hit_message, HIT_MESSAGE_SIZE, "the %s", name_of(weapon)); + weapon->quantity = t; + + if (!rand_percent(hit_chance)) { + (void)strlcat(hit_message, "misses ", HIT_MESSAGE_SIZE); + return(0); + } + s_con_mon(monster); + (void)strlcat(hit_message, "hit ", HIT_MESSAGE_SIZE); + (void)mon_damage(monster, damage); + return(1); +} + +object * +get_thrown_at_monster(object *obj, short dir, short *row, short *col) +{ + short orow, ocol; + short i, ch; + + orow = *row; ocol = *col; + + ch = get_mask_char(obj->what_is); + + for (i = 0; i < 24; i++) { + get_dir_rc(dir, row, col, 0); + if ( (((*col <= 0) || (*col >= DCOLS-1)) || + (dungeon[*row][*col] == NOTHING)) || + ((dungeon[*row][*col] & (HORWALL | VERTWALL | HIDDEN)) && + (!(dungeon[*row][*col] & TRAP)))) { + *row = orow; + *col = ocol; + return(0); + } + if ((i != 0) && rogue_can_see(orow, ocol)) { + mvaddch(orow, ocol, get_dungeon_char(orow, ocol)); + } + if (rogue_can_see(*row, *col)) { + if (!(dungeon[*row][*col] & MONSTER)) { + mvaddch(*row, *col, ch); + } + refresh(); + } + orow = *row; ocol = *col; + if (dungeon[*row][*col] & MONSTER) { + if (!imitating(*row, *col)) { + return(object_at(&level_monsters, *row, *col)); + } + } + if (dungeon[*row][*col] & TUNNEL) { + i += 2; + } + } + return(0); +} + +void +flop_weapon(object *weapon, short row, short col) +{ + object *new_weapon, *monster; + short i = 0; + boolean found = 0; + short mch, dch; + unsigned short mon; + + if ((row < 0) || (row >= DROWS) || (col < 0) || (col >= DCOLS)) + clean_up("flop_weapon: weapon landed outside of dungeon"); + + while ((i < 9) && dungeon[row][col] & ~(FLOOR | TUNNEL | DOOR | MONSTER)) { + rand_around(i++, &row, &col); + if ((row > (DROWS-2)) || (row < MIN_ROW) || + (col > (DCOLS-1)) || (col < 0) || (!dungeon[row][col]) || + (dungeon[row][col] & ~(FLOOR | TUNNEL | DOOR | MONSTER))) { + continue; + } + found = 1; + break; + } + + if (found || (i == 0)) { + new_weapon = alloc_object(); + *new_weapon = *weapon; + new_weapon->in_use_flags = NOT_USED; + new_weapon->quantity = 1; + new_weapon->ichar = 'L'; + place_at(new_weapon, row, col); + if (rogue_can_see(row, col) && + ((row != rogue.row) || (col != rogue.col))) { + mon = dungeon[row][col] & MONSTER; + dungeon[row][col] &= (~MONSTER); + dch = get_dungeon_char(row, col); + if (mon) { + mch = mvinch(row, col); + if ((monster = object_at(&level_monsters, + row, col)) != NULL) { + monster->trail_char = dch; + } + if ((mch < 'A') || (mch > 'Z')) { + mvaddch(row, col, dch); + } + } else { + mvaddch(row, col, dch); + } + dungeon[row][col] |= mon; + } + } else { + short t; + + t = weapon->quantity; + weapon->quantity = 1; + messagef(0, "the %svanishes as it hits the ground", + name_of(weapon)); + weapon->quantity = t; + } +} + +void +rand_around(short i, short *r, short *c) +{ + static char pos[] = "\010\007\001\003\004\005\002\006\0"; + static short row, col; + short j; + + if (i == 0) { + short x, y, o, t; + + row = *r; + col = *c; + + o = get_rand(1, 8); + + for (j = 0; j < 5; j++) { + x = get_rand(0, 8); + y = (x + o) % 9; + t = pos[x]; + pos[x] = pos[y]; + pos[y] = t; + } + } + switch((short)pos[i]) { + case 0: + *r = row + 1; + *c = col + 1; + break; + case 1: + *r = row + 1; + *c = col - 1; + break; + case 2: + *r = row - 1; + *c = col + 1; + break; + case 3: + *r = row - 1; + *c = col - 1; + break; + case 4: + *r = row; + *c = col + 1; + break; + case 5: + *r = row + 1; + *c = col; + break; + case 6: + *r = row; + *c = col; + break; + case 7: + *r = row - 1; + *c = col; + break; + case 8: + *r = row; + *c = col - 1; + break; + } +} diff --git a/rogue/trap.c b/rogue/trap.c new file mode 100644 index 0000000..ae3c918 --- /dev/null +++ b/rogue/trap.c @@ -0,0 +1,282 @@ +/* $NetBSD: trap.c,v 1.10 2009/08/12 08:44:45 dholland Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Timothy C. Stoehr. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)trap.c 8.1 (Berkeley) 5/31/93"; +#else +__RCSID("$NetBSD: trap.c,v 1.10 2009/08/12 08:44:45 dholland Exp $"); +#endif +#endif /* not lint */ + +/* + * trap.c + * + * This source herein may be modified and/or distributed by anybody who + * so desires, with the following restrictions: + * 1.) No portion of this notice shall be removed. + * 2.) Credit shall not be taken for the creation of this source. + * 3.) This code is not to be traded, sold, or used for personal + * gain or profit. + * + */ + +#include "rogue.h" + +trap traps[MAX_TRAPS]; +boolean trap_door = 0; +short bear_trap = 0; + +static const char *const trap_strings[TRAPS * 2] = { + "trap door", + "you fell down a trap", + "bear trap", + "you are caught in a bear trap", + "teleport trap", + "teleport", + "poison dart trap", + "a small dart just hit you in the shoulder", + "sleeping gas trap", + "a strange white mist envelops you and you fall asleep", + "rust trap", + "a gush of water hits you on the head" +}; + +static short +trap_at(int row, int col) +{ + short i; + + for (i = 0; ((i < MAX_TRAPS) && (traps[i].trap_type != NO_TRAP)); i++) { + if ((traps[i].trap_row == row) && (traps[i].trap_col == col)) { + return(traps[i].trap_type); + } + } + return(NO_TRAP); +} + +void +trap_player(short row, short col) +{ + short t; + + if ((t = trap_at(row, col)) == NO_TRAP) { + return; + } + dungeon[row][col] &= (~HIDDEN); + if (rand_percent(rogue.exp + ring_exp)) { + messagef(1, "the trap failed"); + return; + } + switch(t) { + case TRAP_DOOR: + trap_door = 1; + new_level_message = trap_strings[(t*2)+1]; + break; + case BEAR_TRAP: + messagef(1, "%s", trap_strings[(t*2)+1]); + bear_trap = get_rand(4, 7); + break; + case TELE_TRAP: + mvaddch(rogue.row, rogue.col, '^'); + tele(); + break; + case DART_TRAP: + messagef(1, "%s", trap_strings[(t*2)+1]); + rogue.hp_current -= get_damage("1d6", 1); + if (rogue.hp_current <= 0) { + rogue.hp_current = 0; + } + if ((!sustain_strength) && rand_percent(40) && + (rogue.str_current >= 3)) { + rogue.str_current--; + } + print_stats(STAT_HP | STAT_STRENGTH); + if (rogue.hp_current <= 0) { + killed_by((object *)0, POISON_DART); + } + break; + case SLEEPING_GAS_TRAP: + messagef(1, "%s", trap_strings[(t*2)+1]); + take_a_nap(); + break; + case RUST_TRAP: + messagef(1, "%s", trap_strings[(t*2)+1]); + rust(NULL); + break; + } +} + +void +add_traps(void) +{ + short i, n, tries = 0; + short row, col; + + if (cur_level <= 2) { + n = 0; + } else if (cur_level <= 7) { + n = get_rand(0, 2); + } else if (cur_level <= 11) { + n = get_rand(1, 2); + } else if (cur_level <= 16) { + n = get_rand(2, 3); + } else if (cur_level <= 21) { + n = get_rand(2, 4); + } else if (cur_level <= (AMULET_LEVEL + 2)) { + n = get_rand(3, 5); + } else { + n = get_rand(5, MAX_TRAPS); + } + for (i = 0; i < n; i++) { + traps[i].trap_type = get_rand(0, (TRAPS - 1)); + + if ((i == 0) && (party_room != NO_ROOM)) { + do { + row = get_rand((rooms[party_room].top_row+1), + (rooms[party_room].bottom_row-1)); + col = get_rand((rooms[party_room].left_col+1), + (rooms[party_room].right_col-1)); + tries++; + } while (((dungeon[row][col] & (OBJECT|STAIRS|TRAP|TUNNEL)) || + (dungeon[row][col] == NOTHING)) && (tries < 15)); + if (tries >= 15) { + gr_row_col(&row, &col, (FLOOR | MONSTER)); + } + } else { + gr_row_col(&row, &col, (FLOOR | MONSTER)); + } + traps[i].trap_row = row; + traps[i].trap_col = col; + dungeon[row][col] |= (TRAP | HIDDEN); + } +} + +void +id_trap(void) +{ + short dir, row, col, d, t; + + messagef(0, "direction? "); + + while (!is_direction(dir = rgetchar(), &d)) { + sound_bell(); + } + check_message(); + + if (dir == CANCEL) { + return; + } + row = rogue.row; + col = rogue.col; + + get_dir_rc(d, &row, &col, 0); + + if ((dungeon[row][col] & TRAP) && (!(dungeon[row][col] & HIDDEN))) { + t = trap_at(row, col); + messagef(0, "%s", trap_strings[t*2]); + } else { + messagef(0, "no trap there"); + } +} + +void +show_traps(void) +{ + short i, j; + + for (i = 0; i < DROWS; i++) { + for (j = 0; j < DCOLS; j++) { + if (dungeon[i][j] & TRAP) { + mvaddch(i, j, '^'); + } + } + } +} + +void +search(short n, boolean is_auto) +{ + short s, i, j, row, col, t; + short shown = 0, found = 0; + static boolean reg_search; + + for (i = -1; i <= 1; i++) { + for (j = -1; j <= 1; j++) { + row = rogue.row + i; + col = rogue.col + j; + if ((row < MIN_ROW) || (row >= (DROWS-1)) || + (col < 0) || (col >= DCOLS)) { + continue; + } + if (dungeon[row][col] & HIDDEN) { + found++; + } + } + } + for (s = 0; s < n; s++) { + for (i = -1; i <= 1; i++) { + for (j = -1; j <= 1; j++) { + row = rogue.row + i; + col = rogue.col + j ; + if ((row < MIN_ROW) || (row >= (DROWS-1)) || + (col < 0) || (col >= DCOLS)) { + continue; + } + if (dungeon[row][col] & HIDDEN) { + if (rand_percent(17 + (rogue.exp + ring_exp))) { + dungeon[row][col] &= (~HIDDEN); + if ((!blind) && ((row != rogue.row) || + (col != rogue.col))) { + mvaddch(row, col, get_dungeon_char(row, col)); + } + shown++; + if (dungeon[row][col] & TRAP) { + t = trap_at(row, col); + messagef(1, "%s", + trap_strings[t*2]); + } + } + } + if (((shown == found) && (found > 0)) || interrupted) { + return; + } + } + } + if ((!is_auto) && (reg_search = !reg_search)) { + (void)reg_move(); + } + } +} diff --git a/rogue/use.c b/rogue/use.c new file mode 100644 index 0000000..8371a6f --- /dev/null +++ b/rogue/use.c @@ -0,0 +1,625 @@ +/* $NetBSD: use.c,v 1.10 2009/08/12 08:44:45 dholland Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Timothy C. Stoehr. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)use.c 8.1 (Berkeley) 5/31/93"; +#else +__RCSID("$NetBSD: use.c,v 1.10 2009/08/12 08:44:45 dholland Exp $"); +#endif +#endif /* not lint */ + +/* + * use.c + * + * This source herein may be modified and/or distributed by anybody who + * so desires, with the following restrictions: + * 1.) No portion of this notice shall be removed. + * 2.) Credit shall not be taken for the creation of this source. + * 3.) This code is not to be traded, sold, or used for personal + * gain or profit. + * + */ + +#include "rogue.h" + +short halluc = 0; +short blind = 0; +short confused = 0; +short levitate = 0; +short haste_self = 0; +boolean see_invisible = 0; +short extra_hp = 0; +boolean detect_monster = 0; +boolean con_mon = 0; + +static const char strange_feeling[] = + "you have a strange feeling for a moment, then it passes"; + +static const char *get_ench_color(void); +static void go_blind(void); +static void hold_monster(void); +static void idntfy(void); +static void potion_heal(int); +static void uncurse_all(void); + +void +quaff(void) +{ + short ch; + object *obj; + + ch = pack_letter("quaff what?", POTION); + + if (ch == CANCEL) { + return; + } + if (!(obj = get_letter_object(ch))) { + messagef(0, "no such item."); + return; + } + if (obj->what_is != POTION) { + messagef(0, "you can't drink that"); + return; + } + switch(obj->which_kind) { + case INCREASE_STRENGTH: + messagef(0, "you feel stronger now, what bulging muscles!"); + rogue.str_current++; + if (rogue.str_current > rogue.str_max) { + rogue.str_max = rogue.str_current; + } + break; + case RESTORE_STRENGTH: + rogue.str_current = rogue.str_max; + messagef(0, "this tastes great, you feel warm all over"); + break; + case HEALING: + messagef(0, "you begin to feel better"); + potion_heal(0); + break; + case EXTRA_HEALING: + messagef(0, "you begin to feel much better"); + potion_heal(1); + break; + case POISON: + if (!sustain_strength) { + rogue.str_current -= get_rand(1, 3); + if (rogue.str_current < 1) { + rogue.str_current = 1; + } + } + messagef(0, "you feel very sick now"); + if (halluc) { + unhallucinate(); + } + break; + case RAISE_LEVEL: + rogue.exp_points = level_points[rogue.exp - 1]; + messagef(0, "you suddenly feel much more skillful"); + add_exp(1, 1); + break; + case BLINDNESS: + go_blind(); + break; + case HALLUCINATION: + messagef(0, "oh wow, everything seems so cosmic"); + halluc += get_rand(500, 800); + break; + case DETECT_MONSTER: + show_monsters(); + if (!(level_monsters.next_monster)) { + messagef(0, "%s", strange_feeling); + } + break; + case DETECT_OBJECTS: + if (level_objects.next_object) { + if (!blind) { + show_objects(); + } + } else { + messagef(0, "%s", strange_feeling); + } + break; + case CONFUSION: + messagef(0, (halluc ? "what a trippy feeling" : + "you feel confused")); + cnfs(); + break; + case LEVITATION: + messagef(0, "you start to float in the air"); + levitate += get_rand(15, 30); + being_held = bear_trap = 0; + break; + case HASTE_SELF: + messagef(0, "you feel yourself moving much faster"); + haste_self += get_rand(11, 21); + if (!(haste_self % 2)) { + haste_self++; + } + break; + case SEE_INVISIBLE: + messagef(0, "hmm, this potion tastes like %sjuice", + fruit); + if (blind) { + unblind(); + } + see_invisible = 1; + relight(); + break; + } + print_stats((STAT_STRENGTH | STAT_HP)); + if (id_potions[obj->which_kind].id_status != CALLED) { + id_potions[obj->which_kind].id_status = IDENTIFIED; + } + vanish(obj, 1, &rogue.pack); +} + +void +read_scroll(void) +{ + short ch; + object *obj; + + ch = pack_letter("read what?", SCROL); + + if (ch == CANCEL) { + return; + } + if (!(obj = get_letter_object(ch))) { + messagef(0, "no such item."); + return; + } + if (obj->what_is != SCROL) { + messagef(0, "you can't read that"); + return; + } + switch(obj->which_kind) { + case SCARE_MONSTER: + messagef(0, "you hear a maniacal laughter in the distance"); + break; + case HOLD_MONSTER: + hold_monster(); + break; + case ENCH_WEAPON: + if (rogue.weapon) { + if (rogue.weapon->what_is == WEAPON) { + messagef(0, "your %sglow%s %sfor a moment", + name_of(rogue.weapon), + ((rogue.weapon->quantity <= 1) ? "s" : ""), + get_ench_color()); + if (coin_toss()) { + rogue.weapon->hit_enchant++; + } else { + rogue.weapon->d_enchant++; + } + } + rogue.weapon->is_cursed = 0; + } else { + messagef(0, "your hands tingle"); + } + break; + case ENCH_ARMOR: + if (rogue.armor) { + messagef(0, "your armor glows %sfor a moment", + get_ench_color()); + rogue.armor->d_enchant++; + rogue.armor->is_cursed = 0; + print_stats(STAT_ARMOR); + } else { + messagef(0, "your skin crawls"); + } + break; + case IDENTIFY: + messagef(0, "this is a scroll of identify"); + obj->identified = 1; + id_scrolls[obj->which_kind].id_status = IDENTIFIED; + idntfy(); + break; + case TELEPORT: + tele(); + break; + case SLEEP: + messagef(0, "you fall asleep"); + take_a_nap(); + break; + case PROTECT_ARMOR: + if (rogue.armor) { + messagef(0, "your armor is covered by a shimmering gold shield"); + rogue.armor->is_protected = 1; + rogue.armor->is_cursed = 0; + } else { + messagef(0, "your acne seems to have disappeared"); + } + break; + case REMOVE_CURSE: + messagef(0, (!halluc) ? + "you feel as though someone is watching over you" : + "you feel in touch with the universal oneness"); + uncurse_all(); + break; + case CREATE_MONSTER: + create_monster(); + break; + case AGGRAVATE_MONSTER: + aggravate(); + break; + case MAGIC_MAPPING: + messagef(0, "this scroll seems to have a map on it"); + draw_magic_map(); + break; + case CON_MON: + con_mon = 1; + messagef(0, "your hands glow %sfor a moment", + get_ench_color()); + break; + } + if (id_scrolls[obj->which_kind].id_status != CALLED) { + id_scrolls[obj->which_kind].id_status = IDENTIFIED; + } + vanish(obj, (obj->which_kind != SLEEP), &rogue.pack); +} + +/* vanish() does NOT handle a quiver of weapons with more than one + * arrow (or whatever) in the quiver. It will only decrement the count. + */ + +void +vanish(object *obj, short rm, object *pack) +{ + if (obj->quantity > 1) { + obj->quantity--; + } else { + if (obj->in_use_flags & BEING_WIELDED) { + unwield(obj); + } else if (obj->in_use_flags & BEING_WORN) { + unwear(obj); + } else if (obj->in_use_flags & ON_EITHER_HAND) { + un_put_on(obj); + } + take_from_pack(obj, pack); + free_object(obj); + } + if (rm) { + (void)reg_move(); + } +} + +static void +potion_heal(int extra) +{ + float ratio; + short add; + + rogue.hp_current += rogue.exp; + + ratio = ((float)rogue.hp_current) / rogue.hp_max; + + if (ratio >= 1.00) { + rogue.hp_max += (extra ? 2 : 1); + extra_hp += (extra ? 2 : 1); + rogue.hp_current = rogue.hp_max; + } else if (ratio >= 0.90) { + rogue.hp_max += (extra ? 1 : 0); + extra_hp += (extra ? 1 : 0); + rogue.hp_current = rogue.hp_max; + } else { + if (ratio < 0.33) { + ratio = 0.33; + } + if (extra) { + ratio += ratio; + } + add = (short)(ratio * (rogue.hp_max - rogue.hp_current)); + rogue.hp_current += add; + if (rogue.hp_current > rogue.hp_max) { + rogue.hp_current = rogue.hp_max; + } + } + if (blind) { + unblind(); + } + if (confused && extra) { + unconfuse(); + } else if (confused) { + confused = (confused / 2) + 1; + } + if (halluc && extra) { + unhallucinate(); + } else if (halluc) { + halluc = (halluc / 2) + 1; + } +} + +static void +idntfy(void) +{ + short ch; + object *obj; + struct id *id_table; + char desc[DCOLS]; +AGAIN: + ch = pack_letter("what would you like to identify?", ALL_OBJECTS); + + if (ch == CANCEL) { + return; + } + if (!(obj = get_letter_object(ch))) { + messagef(0, "no such item, try again"); + messagef(0, "%s", ""); /* gcc objects to just "" */ + check_message(); + goto AGAIN; + } + obj->identified = 1; + if (obj->what_is & (SCROL | POTION | WEAPON | ARMOR | WAND | RING)) { + id_table = get_id_table(obj); + id_table[obj->which_kind].id_status = IDENTIFIED; + } + get_desc(obj, desc, sizeof(desc)); + messagef(0, "%s", desc); +} + +void +eat(void) +{ + short ch; + short moves; + object *obj; + + ch = pack_letter("eat what?", FOOD); + + if (ch == CANCEL) { + return; + } + if (!(obj = get_letter_object(ch))) { + messagef(0, "no such item."); + return; + } + if (obj->what_is != FOOD) { + messagef(0, "you can't eat that"); + return; + } + if ((obj->which_kind == FRUIT) || rand_percent(60)) { + moves = get_rand(950, 1150); + if (obj->which_kind == RATION) { + messagef(0, "yum, that tasted good"); + } else { + messagef(0, "my, that was a yummy %s", fruit); + } + } else { + moves = get_rand(750, 950); + messagef(0, "yuk, that food tasted awful"); + add_exp(2, 1); + } + rogue.moves_left /= 3; + rogue.moves_left += moves; + hunger_str[0] = 0; + print_stats(STAT_HUNGER); + + vanish(obj, 1, &rogue.pack); +} + +static void +hold_monster(void) +{ + short i, j; + short mcount = 0; + object *monster; + short row, col; + + for (i = -2; i <= 2; i++) { + for (j = -2; j <= 2; j++) { + row = rogue.row + i; + col = rogue.col + j; + if ((row < MIN_ROW) || (row > (DROWS-2)) || (col < 0) || + (col > (DCOLS-1))) { + continue; + } + if (dungeon[row][col] & MONSTER) { + monster = object_at(&level_monsters, row, col); + monster->m_flags |= ASLEEP; + monster->m_flags &= (~WAKENS); + mcount++; + } + } + } + if (mcount == 0) { + messagef(0, "you feel a strange sense of loss"); + } else if (mcount == 1) { + messagef(0, "the monster freezes"); + } else { + messagef(0, "the monsters around you freeze"); + } +} + +void +tele(void) +{ + mvaddch(rogue.row, rogue.col, get_dungeon_char(rogue.row, rogue.col)); + + if (cur_room >= 0) { + darken_room(cur_room); + } + put_player(get_room_number(rogue.row, rogue.col)); + being_held = 0; + bear_trap = 0; +} + +void +hallucinate(void) +{ + object *obj, *monster; + short ch; + + if (blind) return; + + obj = level_objects.next_object; + + while (obj) { + ch = mvinch(obj->row, obj->col); + if (((ch < 'A') || (ch > 'Z')) && + ((obj->row != rogue.row) || (obj->col != rogue.col))) + if ((ch != ' ') && (ch != '.') && (ch != '#') && (ch != '+')) { + addch(gr_obj_char()); + } + obj = obj->next_object; + } + monster = level_monsters.next_monster; + + while (monster) { + ch = mvinch(monster->row, monster->col); + if ((ch >= 'A') && (ch <= 'Z')) { + addch(get_rand('A', 'Z')); + } + monster = monster->next_monster; + } +} + +void +unhallucinate(void) +{ + halluc = 0; + relight(); + messagef(1, "everything looks SO boring now"); +} + +void +unblind(void) +{ + blind = 0; + messagef(1, "the veil of darkness lifts"); + relight(); + if (halluc) { + hallucinate(); + } + if (detect_monster) { + show_monsters(); + } +} + +void +relight(void) +{ + if (cur_room == PASSAGE) { + light_passage(rogue.row, rogue.col); + } else { + light_up_room(cur_room); + } + mvaddch(rogue.row, rogue.col, rogue.fchar); +} + +void +take_a_nap(void) +{ + short i; + + i = get_rand(2, 5); + md_sleep(1); + + while (i--) { + mv_mons(); + } + md_sleep(1); + messagef(0, "%s", you_can_move_again); +} + +static void +go_blind(void) +{ + short i, j; + + if (!blind) { + messagef(0, "a cloak of darkness falls around you"); + } + blind += get_rand(500, 800); + + if (detect_monster) { + object *monster; + + monster = level_monsters.next_monster; + + while (monster) { + mvaddch(monster->row, monster->col, monster->trail_char); + monster = monster->next_monster; + } + } + if (cur_room >= 0) { + for (i = rooms[cur_room].top_row + 1; + i < rooms[cur_room].bottom_row; i++) { + for (j = rooms[cur_room].left_col + 1; + j < rooms[cur_room].right_col; j++) { + mvaddch(i, j, ' '); + } + } + } + mvaddch(rogue.row, rogue.col, rogue.fchar); +} + +static const char * +get_ench_color(void) +{ + if (halluc) { + return(id_potions[get_rand(0, POTIONS-1)].title); + } else if (con_mon) { + return("red "); + } + return("blue "); +} + +void +cnfs(void) +{ + confused += get_rand(12, 22); +} + +void +unconfuse(void) +{ + confused = 0; + messagef(1, "you feel less %s now", (halluc ? "trippy" : "confused")); +} + +static void +uncurse_all(void) +{ + object *obj; + + obj = rogue.pack.next_object; + + while (obj) { + obj->is_cursed = 0; + obj = obj->next_object; + } +} diff --git a/rogue/zap.c b/rogue/zap.c new file mode 100644 index 0000000..3e9134f --- /dev/null +++ b/rogue/zap.c @@ -0,0 +1,407 @@ +/* $NetBSD: zap.c,v 1.9 2008/01/14 03:50:03 dholland Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Timothy C. Stoehr. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)zap.c 8.1 (Berkeley) 5/31/93"; +#else +__RCSID("$NetBSD: zap.c,v 1.9 2008/01/14 03:50:03 dholland Exp $"); +#endif +#endif /* not lint */ + +/* + * zap.c + * + * This source herein may be modified and/or distributed by anybody who + * so desires, with the following restrictions: + * 1.) No portion of this notice shall be removed. + * 2.) Credit shall not be taken for the creation of this source. + * 3.) This code is not to be traded, sold, or used for personal + * gain or profit. + * + */ + +#include "rogue.h" + +static object *get_zapped_monster(short, short *, short *); +static void tele_away(object *); +static void wdrain_life(object *); +static void zap_monster(object *, unsigned short); + +boolean wizard = 0; + +void +zapp(void) +{ + short wch; + boolean first_miss = 1; + object *wand; + short dir, d, row, col; + object *monster; + + while (!is_direction(dir = rgetchar(), &d)) { + sound_bell(); + if (first_miss) { + messagef(0, "direction? "); + first_miss = 0; + } + } + check_message(); + if (dir == CANCEL) { + return; + } + if ((wch = pack_letter("zap with what?", WAND)) == CANCEL) { + return; + } + check_message(); + + if (!(wand = get_letter_object(wch))) { + messagef(0, "no such item."); + return; + } + if (wand->what_is != WAND) { + messagef(0, "you can't zap with that"); + return; + } + if (wand->class <= 0) { + messagef(0, "nothing happens"); + } else { + wand->class--; + row = rogue.row; col = rogue.col; + if ((wand->which_kind == COLD) || (wand->which_kind == FIRE)) { + bounce((short)wand->which_kind, d, row, col, 0); + } else { + monster = get_zapped_monster(d, &row, &col); + if (wand->which_kind == DRAIN_LIFE) { + wdrain_life(monster); + } else if (monster) { + wake_up(monster); + s_con_mon(monster); + zap_monster(monster, wand->which_kind); + relight(); + } + } + } + (void)reg_move(); +} + +static object * +get_zapped_monster(short dir, short *row, short *col) +{ + short orow, ocol; + + for (;;) { + orow = *row; ocol = *col; + get_dir_rc(dir, row, col, 0); + if (((*row == orow) && (*col == ocol)) || + (dungeon[*row][*col] & (HORWALL | VERTWALL)) || + (dungeon[*row][*col] == NOTHING)) { + return(0); + } + if (dungeon[*row][*col] & MONSTER) { + if (!imitating(*row, *col)) { + return(object_at(&level_monsters, *row, *col)); + } + } + } +} + +static void +zap_monster(object *monster, unsigned short kind) +{ + short row, col; + object *nm; + short tc; + + row = monster->row; + col = monster->col; + + switch(kind) { + case SLOW_MONSTER: + if (monster->m_flags & HASTED) { + monster->m_flags &= (~HASTED); + } else { + monster->slowed_toggle = 0; + monster->m_flags |= SLOWED; + } + break; + case HASTE_MONSTER: + if (monster->m_flags & SLOWED) { + monster->m_flags &= (~SLOWED); + } else { + monster->m_flags |= HASTED; + } + break; + case TELE_AWAY: + tele_away(monster); + break; + case INVISIBILITY: + monster->m_flags |= INVISIBLE; + break; + case POLYMORPH: + if (monster->m_flags & HOLDS) { + being_held = 0; + } + nm = monster->next_monster; + tc = monster->trail_char; + (void)gr_monster(monster, get_rand(0, MONSTERS-1)); + monster->row = row; + monster->col = col; + monster->next_monster = nm; + monster->trail_char = tc; + if (!(monster->m_flags & IMITATES)) { + wake_up(monster); + } + break; + case MAGIC_MISSILE: + rogue_hit(monster, 1); + break; + case CANCELLATION: + if (monster->m_flags & HOLDS) { + being_held = 0; + } + if (monster->m_flags & STEALS_ITEM) { + monster->drop_percent = 0; + } + monster->m_flags &= (~(FLIES | FLITS | SPECIAL_HIT | INVISIBLE | + FLAMES | IMITATES | CONFUSES | SEEKS_GOLD | HOLDS)); + break; + case DO_NOTHING: + messagef(0, "nothing happens"); + break; + } +} + +static void +tele_away(object *monster) +{ + short row, col; + + if (monster->m_flags & HOLDS) { + being_held = 0; + } + gr_row_col(&row, &col, (FLOOR | TUNNEL | STAIRS | OBJECT)); + mvaddch(monster->row, monster->col, monster->trail_char); + dungeon[monster->row][monster->col] &= ~MONSTER; + monster->row = row; monster->col = col; + dungeon[row][col] |= MONSTER; + monster->trail_char = mvinch(row, col); + if (detect_monster || rogue_can_see(row, col)) { + mvaddch(row, col, gmc(monster)); + } +} + +void +wizardize(void) +{ + char buf[100]; + + if (wizard) { + wizard = 0; + messagef(0, "not wizard anymore"); + } else { + if (get_input_line("wizard's password:", "", buf, sizeof(buf), + "", 0, 0)) { + (void)xxx(1); + xxxx(buf, strlen(buf)); + if (!strncmp(buf, "\247\104\126\272\115\243\027", 7)) { + wizard = 1; + score_only = 1; + messagef(0, "Welcome, mighty wizard!"); + } else { + messagef(0, "sorry"); + } + } + } +} + +static void +wdrain_life(object *monster) +{ + short hp; + object *lmon, *nm; + + hp = rogue.hp_current / 3; + rogue.hp_current = (rogue.hp_current + 1) / 2; + + if (cur_room >= 0) { + lmon = level_monsters.next_monster; + while (lmon) { + nm = lmon->next_monster; + if (get_room_number(lmon->row, lmon->col) == cur_room) { + wake_up(lmon); + (void)mon_damage(lmon, hp); + } + lmon = nm; + } + } else { + if (monster) { + wake_up(monster); + (void)mon_damage(monster, hp); + } + } + print_stats(STAT_HP); + relight(); +} + +void +bounce(short ball, short dir, short row, short col, short r) +{ + short orow, ocol; + const char *s; + short i, ch, new_dir = -1, damage; + static short btime; + + if (++r == 1) { + btime = get_rand(3, 6); + } else if (r > btime) { + return; + } + + if (ball == FIRE) { + s = "fire"; + } else { + s = "ice"; + } + if (r > 1) { + messagef(0, "the %s bounces", s); + } + orow = row; + ocol = col; + do { + ch = mvinch(orow, ocol); + standout(); + mvaddch(orow, ocol, ch); + get_dir_rc(dir, &orow, &ocol, 1); + } while (!( (ocol <= 0) || + (ocol >= DCOLS-1) || + (dungeon[orow][ocol] == NOTHING) || + (dungeon[orow][ocol] & MONSTER) || + (dungeon[orow][ocol] & (HORWALL | VERTWALL)) || + ((orow == rogue.row) && (ocol == rogue.col)))); + standend(); + refresh(); + do { + orow = row; + ocol = col; + ch = mvinch(row, col); + mvaddch(row, col, ch); + get_dir_rc(dir, &row, &col, 1); + } while (!( (col <= 0) || + (col >= DCOLS-1) || + (dungeon[row][col] == NOTHING) || + (dungeon[row][col] & MONSTER) || + (dungeon[row][col] & (HORWALL | VERTWALL)) || + ((row == rogue.row) && (col == rogue.col)))); + + if (dungeon[row][col] & MONSTER) { + object *monster; + + monster = object_at(&level_monsters, row, col); + + wake_up(monster); + if (rand_percent(33)) { + messagef(0, "the %s misses the %s", s, + mon_name(monster)); + goto ND; + } + if (ball == FIRE) { + if (!(monster->m_flags & RUSTS)) { + if (monster->m_flags & FREEZES) { + damage = monster->hp_to_kill; + } else if (monster->m_flags & FLAMES) { + damage = (monster->hp_to_kill / 10) + 1; + } else { + damage = get_rand((rogue.hp_current / 3), rogue.hp_max); + } + } else { + damage = (monster->hp_to_kill / 2) + 1; + } + messagef(0, "the %s hits the %s", s, + mon_name(monster)); + (void)mon_damage(monster, damage); + } else { + damage = -1; + if (!(monster->m_flags & FREEZES)) { + if (rand_percent(33)) { + messagef(0, "the monster is frozen"); + monster->m_flags |= (ASLEEP | NAPPING); + monster->nap_length = get_rand(3, 6); + } else { + damage = rogue.hp_current / 4; + } + } else { + damage = -2; + } + if (damage != -1) { + messagef(0, "the %s hits the %s", s, + mon_name(monster)); + (void)mon_damage(monster, damage); + } + } + } else if ((row == rogue.row) && (col == rogue.col)) { + if (rand_percent(10 + (3 * get_armor_class(rogue.armor)))) { + messagef(0, "the %s misses", s); + goto ND; + } else { + damage = get_rand(3, (3 * rogue.exp)); + if (ball == FIRE) { + damage = (damage * 3) / 2; + damage -= get_armor_class(rogue.armor); + } + rogue_damage(damage, NULL, + ((ball == FIRE) ? KFIRE : HYPOTHERMIA)); + messagef(0, "the %s hits", s); + } + } else { + short nrow, ncol; + +ND: for (i = 0; i < 10; i++) { + dir = get_rand(0, DIRS-1); + nrow = orow; + ncol = ocol; + get_dir_rc(dir, &nrow, &ncol, 1); + if (((ncol >= 0) && (ncol <= DCOLS-1)) && + (dungeon[nrow][ncol] != NOTHING) && + (!(dungeon[nrow][ncol] & (VERTWALL | HORWALL)))) { + new_dir = dir; + break; + } + } + if (new_dir != -1) { + bounce(ball, new_dir, orow, ocol, r); + } + } +} diff --git a/tetris/Makefile b/tetris/Makefile new file mode 100644 index 0000000..0d9c0d6 --- /dev/null +++ b/tetris/Makefile @@ -0,0 +1,13 @@ +# $NetBSD: Makefile,v 1.7 2010/02/03 15:34:39 roy Exp $ +# @(#)Makefile 8.1 (Berkeley) 5/31/93 + +PROG= tetris +SRCS= input.c screen.c shapes.c scores.c tetris.c +MAN= tetris.6 +DPADD= ${LIBTERMINFO} +# 20150209 bkw: s/terminfo/curses/, add -lbsd +LDADD=-lcurses -lbsd +HIDEGAME=hidegame +SETGIDGAME=yes + +.include <bsd.prog.mk> diff --git a/tetris/input.c b/tetris/input.c new file mode 100644 index 0000000..e5f8c12 --- /dev/null +++ b/tetris/input.c @@ -0,0 +1,162 @@ +/* $NetBSD: input.c,v 1.11 2009/05/25 04:33:53 dholland Exp $ */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek and Darren F. Provine. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)input.c 8.1 (Berkeley) 5/31/93 + */ + +/* + * Tetris input. + */ + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/poll.h> + +#include <errno.h> +#include <unistd.h> + +#include "input.h" +#include "tetris.h" + +/* return true iff the given timeval is positive */ +#define TV_POS(tv) \ + ((tv)->tv_sec > 0 || ((tv)->tv_sec == 0 && (tv)->tv_usec > 0)) + +/* subtract timeval `sub' from `res' */ +#define TV_SUB(res, sub) \ + (res)->tv_sec -= (sub)->tv_sec; \ + (res)->tv_usec -= (sub)->tv_usec; \ + if ((res)->tv_usec < 0) { \ + (res)->tv_usec += 1000000; \ + (res)->tv_sec--; \ + } + +/* + * Do a `read wait': poll for reading from stdin, with timeout *tvp. + * On return, modify *tvp to reflect the amount of time spent waiting. + * It will be positive only if input appeared before the time ran out; + * otherwise it will be zero or perhaps negative. + * + * If tvp is nil, wait forever, but return if poll is interrupted. + * + * Return 0 => no input, 1 => can read() from stdin + */ +int +rwait(struct timeval *tvp) +{ + struct pollfd set[1]; + struct timeval starttv, endtv; + int timeout; +#define NILTZ ((struct timezone *)0) + + if (tvp) { + (void) gettimeofday(&starttv, NILTZ); + endtv = *tvp; + timeout = tvp->tv_sec * 1000 + tvp->tv_usec / 1000; + } else + timeout = INFTIM; +again: + set[0].fd = STDIN_FILENO; + set[0].events = POLLIN; + switch (poll(set, 1, timeout)) { + + case -1: + if (tvp == 0) + return (-1); + if (errno == EINTR) + goto again; + stop("poll failed, help"); + /* NOTREACHED */ + + case 0: /* timed out */ + if (tvp) { + tvp->tv_sec = 0; + tvp->tv_usec = 0; + } + return (0); + } + if (tvp) { + /* since there is input, we may not have timed out */ + (void) gettimeofday(&endtv, NILTZ); + TV_SUB(&endtv, &starttv); + TV_SUB(tvp, &endtv); /* adjust *tvp by elapsed time */ + } + return (1); +} + +/* + * `sleep' for the current turn time. + * Eat any input that might be available. + */ +void +tsleep(void) +{ + struct timeval tv; + char c; + + tv.tv_sec = 0; + tv.tv_usec = fallrate; + while (TV_POS(&tv)) + if (rwait(&tv) && read(0, &c, 1) != 1) + break; +} + +/* + * getchar with timeout. + */ +int +tgetchar(void) +{ + static struct timeval timeleft; + char c; + + /* + * Reset timeleft to fallrate whenever it is not positive. + * In any case, wait to see if there is any input. If so, + * take it, and update timeleft so that the next call to + * tgetchar() will not wait as long. If there is no input, + * make timeleft zero or negative, and return -1. + * + * Most of the hard work is done by rwait(). + */ + if (!TV_POS(&timeleft)) { + faster(); /* go faster */ + timeleft.tv_sec = 0; + timeleft.tv_usec = fallrate; + } + if (!rwait(&timeleft)) + return (-1); + if (read(0, &c, 1) != 1) + stop("end of file, help"); + return ((int)(unsigned char)c); +} diff --git a/tetris/input.h b/tetris/input.h new file mode 100644 index 0000000..856c66a --- /dev/null +++ b/tetris/input.h @@ -0,0 +1,39 @@ +/* $NetBSD: input.h,v 1.5 2004/01/27 20:30:30 jsm Exp $ */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek and Darren F. Provine. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)input.h 8.1 (Berkeley) 5/31/93 + */ + +int rwait(struct timeval *); +int tgetchar(void); +void tsleep(void); diff --git a/tetris/pathnames.h b/tetris/pathnames.h new file mode 100644 index 0000000..f96e3cc --- /dev/null +++ b/tetris/pathnames.h @@ -0,0 +1,37 @@ +/* $NetBSD: pathnames.h,v 1.3 2003/08/07 09:37:48 agc Exp $ */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek and Darren F. Provine. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pathnames.h 8.1 (Berkeley) 5/31/93 + */ + +#define _PATH_SCOREFILE "/var/games/tetris.scores" diff --git a/tetris/scores.c b/tetris/scores.c new file mode 100644 index 0000000..eca0ce3 --- /dev/null +++ b/tetris/scores.c @@ -0,0 +1,960 @@ +/* $NetBSD: scores.c,v 1.22 2014/03/22 19:05:30 dholland Exp $ */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek and Darren F. Provine. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)scores.c 8.1 (Berkeley) 5/31/93 + */ + +/* + * Score code for Tetris, by Darren Provine (kilroy@gboro.glassboro.edu) + * modified 22 January 1992, to limit the number of entries any one + * person has. + * + * Major whacks since then. + */ +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <pwd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/file.h> /* 20150211 bkw: for flock() */ +#include <time.h> +#include <term.h> +#include <unistd.h> + +#include "pathnames.h" +#include "screen.h" +#include "scores.h" +#include "tetris.h" + +/* + * Allow updating the high scores unless we're built as part of /rescue. + */ +#ifndef RESCUEDIR +#define ALLOW_SCORE_UPDATES +#endif + +/* + * Within this code, we can hang onto one extra "high score", leaving + * room for our current score (whether or not it is high). + * + * We also sometimes keep tabs on the "highest" score on each level. + * As long as the scores are kept sorted, this is simply the first one at + * that level. + */ +#define NUMSPOTS (MAXHISCORES + 1) +#define NLEVELS (MAXLEVEL + 1) + +static time_t now; +static int nscores; +static int gotscores; +static struct highscore scores[NUMSPOTS]; + +static int checkscores(struct highscore *, int); +static int cmpscores(const void *, const void *); +static void getscores(int *); +static void printem(int, int, struct highscore *, int, const char *); +static char *thisuser(void); + +/* contents chosen to be a highly illegal username */ +static const char hsh_magic_val[HSH_MAGIC_SIZE] = "//:\0\0://"; + +#define HSH_ENDIAN_NATIVE 0x12345678 +#define HSH_ENDIAN_OPP 0x78563412 + +/* current file format version */ +#define HSH_VERSION 1 + +/* codes for scorefile_probe return */ +#define SCOREFILE_ERROR (-1) +#define SCOREFILE_CURRENT 0 /* 40-byte */ +#define SCOREFILE_CURRENT_OPP 1 /* 40-byte, opposite-endian */ +#define SCOREFILE_599 2 /* 36-byte */ +#define SCOREFILE_599_OPP 3 /* 36-byte, opposite-endian */ +#define SCOREFILE_50 4 /* 32-byte */ +#define SCOREFILE_50_OPP 5 /* 32-byte, opposite-endian */ + +/* + * Check (or guess) what kind of score file contents we have. + */ +static int +scorefile_probe(int sd) +{ + struct stat st; + int t1, t2, t3, tx; + ssize_t result; + uint32_t numbers[3], offset56, offset60, offset64; + + if (fstat(sd, &st) < 0) { + warn("Score file %s: fstat", _PATH_SCOREFILE); + return -1; + } + + t1 = st.st_size % sizeof(struct highscore_ondisk) == 0; + t2 = st.st_size % sizeof(struct highscore_ondisk_599) == 0; + t3 = st.st_size % sizeof(struct highscore_ondisk_50) == 0; + tx = t1 + t2 + t3; + if (tx == 1) { + /* Size matches exact number of one kind of records */ + if (t1) { + return SCOREFILE_CURRENT; + } else if (t2) { + return SCOREFILE_599; + } else { + return SCOREFILE_50; + } + } else if (tx == 0) { + /* Size matches nothing, pick most likely as default */ + goto wildguess; + } + + /* + * File size is multiple of more than one structure size. + * (For example, 288 bytes could be 9*hso50 or 8*hso599.) + * Read the file and see if we can figure out what's going + * on. This is the layout of the first two records: + * + * offset hso / current hso_599 hso_50 + * (40-byte) (36-byte) (32-byte) + * + * 0 name #0 name #0 name #0 + * 4 : : : + * 8 : : : + * 12 : : : + * 16 : : : + * 20 score #0 score #0 score #0 + * 24 level #0 level #0 level #0 + * 28 (pad) time #0 time #0 + * 32 time #0 name #1 + * 36 name #1 : + * 40 name #1 : : + * 44 : : : + * 48 : : : + * 52 : : score #1 + * 56 : score #1 level #1 + * 60 score #1 level #1 time #1 + * 64 level #1 time #1 name #2 + * 68 (pad) : : + * 72 time #1 name #2 : + * 76 : : : + * 80 --- end --- + * + * There are a number of things we could check here, but the + * most effective test is based on the following restrictions: + * + * - The level must be between 1 and 9 (inclusive) + * - All times must be after 1985 and are before 2038, + * so the high word must be 0 and the low word may not be + * a small value. + * - Integer values of 0 or 1-9 cannot be the beginning of + * a login name string. + * - Values of 1-9 are probably not a score. + * + * So we read the three words at offsets 56, 60, and 64, and + * poke at the values to try to figure things... + */ + + if (lseek(sd, 56, SEEK_SET) < 0) { + warn("Score file %s: lseek", _PATH_SCOREFILE); + return -1; + } + result = read(sd, &numbers, sizeof(numbers)); + if (result < 0) { + warn("Score file %s: read", _PATH_SCOREFILE); + return -1; + } + if ((size_t)result != sizeof(numbers)) { + /* + * The smallest file whose size divides by more than + * one of the sizes is substantially larger than 64, + * so this should *never* happen. + */ + warnx("Score file %s: Unexpected EOF", _PATH_SCOREFILE); + return -1; + } + + offset56 = numbers[0]; + offset60 = numbers[1]; + offset64 = numbers[2]; + + if (offset64 >= MINLEVEL && offset64 <= MAXLEVEL) { + /* 40-byte structure */ + return SCOREFILE_CURRENT; + } else if (offset60 >= MINLEVEL && offset60 <= MAXLEVEL) { + /* 36-byte structure */ + return SCOREFILE_599; + } else if (offset56 >= MINLEVEL && offset56 <= MAXLEVEL) { + /* 32-byte structure */ + return SCOREFILE_50; + } + + /* None was a valid level; try opposite endian */ + offset64 = bswap32(offset64); + offset60 = bswap32(offset60); + offset56 = bswap32(offset56); + + if (offset64 >= MINLEVEL && offset64 <= MAXLEVEL) { + /* 40-byte structure */ + return SCOREFILE_CURRENT_OPP; + } else if (offset60 >= MINLEVEL && offset60 <= MAXLEVEL) { + /* 36-byte structure */ + return SCOREFILE_599_OPP; + } else if (offset56 >= MINLEVEL && offset56 <= MAXLEVEL) { + /* 32-byte structure */ + return SCOREFILE_50_OPP; + } + + /* That didn't work either, dunno what's going on */ + wildguess: + warnx("Score file %s is likely corrupt", _PATH_SCOREFILE); + if (sizeof(void *) == 8 && sizeof(time_t) == 8) { + return SCOREFILE_CURRENT; + } else if (sizeof(time_t) == 8) { + return SCOREFILE_599; + } else { + return SCOREFILE_50; + } +} + +/* + * Copy a string safely, making sure it's null-terminated. + */ +static void +readname(char *to, size_t maxto, const char *from, size_t maxfrom) +{ + size_t amt; + + amt = maxto < maxfrom ? maxto : maxfrom; + memcpy(to, from, amt); + to[maxto-1] = '\0'; +} + +/* + * Copy integers, byte-swapping if desired. + */ +static int32_t +read32(int32_t val, int doflip) +{ + if (doflip) { + val = bswap32(val); + } + return val; +} + +static int64_t +read64(int64_t val, int doflip) +{ + if (doflip) { + val = bswap64(val); + } + return val; +} + +/* + * Read up to MAXHISCORES scorefile_ondisk entries. + */ +static int +readscores(int sd, int doflip) +{ + struct highscore_ondisk buf[MAXHISCORES]; + ssize_t result; + int i; + + result = read(sd, buf, sizeof(buf)); + if (result < 0) { + warn("Score file %s: read", _PATH_SCOREFILE); + return -1; + } + nscores = result / sizeof(buf[0]); + + for (i=0; i<nscores; i++) { + readname(scores[i].hs_name, sizeof(scores[i].hs_name), + buf[i].hso_name, sizeof(buf[i].hso_name)); + scores[i].hs_score = read32(buf[i].hso_score, doflip); + scores[i].hs_level = read32(buf[i].hso_level, doflip); + scores[i].hs_time = read64(buf[i].hso_time, doflip); + } + return 0; +} + +/* + * Read up to MAXHISCORES scorefile_ondisk_599 entries. + */ +static int +readscores599(int sd, int doflip) +{ + struct highscore_ondisk_599 buf[MAXHISCORES]; + ssize_t result; + int i; + + result = read(sd, buf, sizeof(buf)); + if (result < 0) { + warn("Score file %s: read", _PATH_SCOREFILE); + return -1; + } + nscores = result / sizeof(buf[0]); + + for (i=0; i<nscores; i++) { + readname(scores[i].hs_name, sizeof(scores[i].hs_name), + buf[i].hso599_name, sizeof(buf[i].hso599_name)); + scores[i].hs_score = read32(buf[i].hso599_score, doflip); + scores[i].hs_level = read32(buf[i].hso599_level, doflip); + /* + * Don't bother pasting the time together into a + * 64-bit value; just take whichever half is nonzero. + */ + scores[i].hs_time = + read32(buf[i].hso599_time[buf[i].hso599_time[0] == 0], + doflip); + } + return 0; +} + +/* + * Read up to MAXHISCORES scorefile_ondisk_50 entries. + */ +static int +readscores50(int sd, int doflip) +{ + struct highscore_ondisk_50 buf[MAXHISCORES]; + ssize_t result; + int i; + + result = read(sd, buf, sizeof(buf)); + if (result < 0) { + warn("Score file %s: read", _PATH_SCOREFILE); + return -1; + } + nscores = result / sizeof(buf[0]); + + for (i=0; i<nscores; i++) { + readname(scores[i].hs_name, sizeof(scores[i].hs_name), + buf[i].hso50_name, sizeof(buf[i].hso50_name)); + scores[i].hs_score = read32(buf[i].hso50_score, doflip); + scores[i].hs_level = read32(buf[i].hso50_level, doflip); + scores[i].hs_time = read32(buf[i].hso50_time, doflip); + } + return 0; +} + +/* + * Read the score file. Can be called from savescore (before showscores) + * or showscores (if savescore will not be called). If the given pointer + * is not NULL, sets *fdp to an open file handle that corresponds to a + * read/write score file that is locked with LOCK_EX. Otherwise, the + * file is locked with LOCK_SH for the read and closed before return. + */ +static void +getscores(int *fdp) +{ + struct highscore_header header; + int sd, mint, lck; + mode_t mask; + const char *human; + int doflip; + int serrno; + ssize_t result; + +#ifdef ALLOW_SCORE_UPDATES + if (fdp != NULL) { + mint = O_RDWR | O_CREAT; + human = "read/write"; + lck = LOCK_EX; + } else +#endif + { + mint = O_RDONLY; + human = "reading"; + lck = LOCK_SH; + } + setegid(egid); + mask = umask(S_IWOTH); + sd = open(_PATH_SCOREFILE, mint, 0666); + serrno = errno; + (void)umask(mask); + setegid(gid); + if (sd < 0) { + /* + * If the file simply isn't there because nobody's + * played yet, and we aren't going to be trying to + * update it, don't warn. Even if we are going to be + * trying to write it, don't fail -- we can still show + * the player the score they got. + */ + errno = serrno; + if (fdp != NULL || errno != ENOENT) { + warn("Cannot open %s for %s", _PATH_SCOREFILE, human); + } + goto fail; + } + + /* + * Grab a lock. + * XXX: failure here should probably be more fatal than this. + */ + if (flock(sd, lck)) + warn("warning: score file %s cannot be locked", + _PATH_SCOREFILE); + + /* + * The current format (since -current of 20090525) is + * + * struct highscore_header + * up to MAXHIGHSCORES x struct highscore_ondisk + * + * Before this, there is no header, and the contents + * might be any of three formats: + * + * highscore_ondisk (64-bit machines with 64-bit time_t) + * highscore_ondisk_599 (32-bit machines with 64-bit time_t) + * highscore_ondisk_50 (32-bit machines with 32-bit time_t) + * + * The first two appear in 5.99 between the time_t change and + * 20090525, depending on whether the compiler inserts + * structure padding before an unaligned 64-bit time_t. The + * last appears in 5.0 and earlier. + * + * Any or all of these might also appear on other OSes where + * this code has been ported. + * + * Since the old file has no header, we will have to guess + * which of these formats it has. + */ + + /* + * First, look for a header. + */ + result = read(sd, &header, sizeof(header)); + if (result < 0) { + warn("Score file %s: read", _PATH_SCOREFILE); + goto sdfail; + } + if (result != 0 && (size_t)result != sizeof(header)) { + warnx("Score file %s: read: unexpected EOF", _PATH_SCOREFILE); + /* + * File is hopelessly corrupt, might as well truncate it + * and start over with empty scores. + */ + if (lseek(sd, 0, SEEK_SET) < 0) { + /* ? */ + warn("Score file %s: lseek", _PATH_SCOREFILE); + goto sdfail; + } + if (ftruncate(sd, 0) == 0) { + result = 0; + } else { + goto sdfail; + } + } + + if (result == 0) { + /* Empty file; that just means there are no scores. */ + nscores = 0; + } else { + /* + * Is what we read a header, or the first 16 bytes of + * a score entry? hsh_magic_val is chosen to be + * something that is extremely unlikely to appear in + * hs_name[]. + */ + if (!memcmp(header.hsh_magic, hsh_magic_val, HSH_MAGIC_SIZE)) { + /* Yes, we have a header. */ + + if (header.hsh_endiantag == HSH_ENDIAN_NATIVE) { + /* native endian */ + doflip = 0; + } else if (header.hsh_endiantag == HSH_ENDIAN_OPP) { + doflip = 1; + } else { + warnx("Score file %s: Unknown endian tag %u", + _PATH_SCOREFILE, header.hsh_endiantag); + goto sdfail; + } + + if (header.hsh_version != HSH_VERSION) { + warnx("Score file %s: Unknown version code %u", + _PATH_SCOREFILE, header.hsh_version); + goto sdfail; + } + + if (readscores(sd, doflip) < 0) { + goto sdfail; + } + } else { + /* + * Ok, it wasn't a header. Try to figure out what + * size records we have. + */ + result = scorefile_probe(sd); + if (lseek(sd, 0, SEEK_SET) < 0) { + warn("Score file %s: lseek", _PATH_SCOREFILE); + goto sdfail; + } + switch (result) { + case SCOREFILE_CURRENT: + result = readscores(sd, 0 /* don't flip */); + break; + case SCOREFILE_CURRENT_OPP: + result = readscores(sd, 1 /* do flip */); + break; + case SCOREFILE_599: + result = readscores599(sd, 0 /* don't flip */); + break; + case SCOREFILE_599_OPP: + result = readscores599(sd, 1 /* do flip */); + break; + case SCOREFILE_50: + result = readscores50(sd, 0 /* don't flip */); + break; + case SCOREFILE_50_OPP: + result = readscores50(sd, 1 /* do flip */); + break; + default: + goto sdfail; + } + if (result < 0) { + goto sdfail; + } + } + } + + + if (fdp) + *fdp = sd; + else + close(sd); + + return; + +sdfail: + close(sd); + fail: + if (fdp != NULL) { + *fdp = -1; + } + nscores = 0; +} + +#ifdef ALLOW_SCORE_UPDATES +/* + * Paranoid write wrapper; unlike fwrite() it preserves errno. + */ +static int +dowrite(int sd, const void *vbuf, size_t len) +{ + const char *buf = vbuf; + ssize_t result; + size_t done = 0; + + while (done < len) { + result = write(sd, buf+done, len-done); + if (result < 0) { + if (errno == EINTR) { + continue; + } + return -1; + } + done += result; + } + return 0; +} +#endif /* ALLOW_SCORE_UPDATES */ + +/* + * Write the score file out. + */ +static void +putscores(int sd) +{ +#ifdef ALLOW_SCORE_UPDATES + struct highscore_header header; + struct highscore_ondisk buf[MAXHISCORES]; + int i; + + if (sd == -1) { + return; + } + + memcpy(header.hsh_magic, hsh_magic_val, HSH_MAGIC_SIZE); + header.hsh_endiantag = HSH_ENDIAN_NATIVE; + header.hsh_version = HSH_VERSION; + + for (i=0; i<nscores; i++) { + strncpy(buf[i].hso_name, scores[i].hs_name, + sizeof(buf[i].hso_name)); + buf[i].hso_score = scores[i].hs_score; + buf[i].hso_level = scores[i].hs_level; + buf[i].hso_pad = 0xbaadf00d; + buf[i].hso_time = scores[i].hs_time; + } + + if (lseek(sd, 0, SEEK_SET) < 0) { + warn("Score file %s: lseek", _PATH_SCOREFILE); + goto fail; + } + if (dowrite(sd, &header, sizeof(header)) < 0 || + dowrite(sd, buf, sizeof(buf[0]) * nscores) < 0) { + warn("Score file %s: write", _PATH_SCOREFILE); + goto fail; + } + return; + fail: + warnx("high scores may be damaged"); +#else + (void)sd; +#endif /* ALLOW_SCORE_UPDATES */ +} + +/* + * Close the score file. + */ +static void +closescores(int sd) +{ + flock(sd, LOCK_UN); + close(sd); +} + +/* + * Read and update the scores file with the current reults. + */ +void +savescore(int level) +{ + struct highscore *sp; + int i; + int change; + int sd; + const char *me; + + getscores(&sd); + gotscores = 1; + (void)time(&now); + + /* + * Allow at most one score per person per level -- see if we + * can replace an existing score, or (easiest) do nothing. + * Otherwise add new score at end (there is always room). + */ + change = 0; + me = thisuser(); + for (i = 0, sp = &scores[0]; i < nscores; i++, sp++) { + if (sp->hs_level != level || strcmp(sp->hs_name, me) != 0) + continue; + if (score > sp->hs_score) { + (void)printf("%s bettered %s %d score of %d!\n", + "\nYou", "your old level", level, + sp->hs_score * sp->hs_level); + sp->hs_score = score; /* new score */ + sp->hs_time = now; /* and time */ + change = 1; + } else if (score == sp->hs_score) { + (void)printf("%s tied %s %d high score.\n", + "\nYou", "your old level", level); + sp->hs_time = now; /* renew it */ + change = 1; /* gotta rewrite, sigh */ + } /* else new score < old score: do nothing */ + break; + } + if (i >= nscores) { + strcpy(sp->hs_name, me); + sp->hs_level = level; + sp->hs_score = score; + sp->hs_time = now; + nscores++; + change = 1; + } + + if (change) { + /* + * Sort & clean the scores, then rewrite. + */ + nscores = checkscores(scores, nscores); + putscores(sd); + } + closescores(sd); +} + +/* + * Get login name, or if that fails, get something suitable. + * The result is always trimmed to fit in a score. + */ +static char * +thisuser(void) +{ + const char *p; + struct passwd *pw; + size_t l; + static char u[sizeof(scores[0].hs_name)]; + + if (u[0]) + return (u); + p = getlogin(); + if (p == NULL || *p == '\0') { + pw = getpwuid(getuid()); + if (pw != NULL) + p = pw->pw_name; + else + p = " ???"; + } + l = strlen(p); + if (l >= sizeof(u)) + l = sizeof(u) - 1; + memcpy(u, p, l); + u[l] = '\0'; + return (u); +} + +/* + * Score comparison function for qsort. + * + * If two scores are equal, the person who had the score first is + * listed first in the highscore file. + */ +static int +cmpscores(const void *x, const void *y) +{ + const struct highscore *a, *b; + long l; + + a = x; + b = y; + l = (long)b->hs_level * b->hs_score - (long)a->hs_level * a->hs_score; + if (l < 0) + return (-1); + if (l > 0) + return (1); + if (a->hs_time < b->hs_time) + return (-1); + if (a->hs_time > b->hs_time) + return (1); + return (0); +} + +/* + * If we've added a score to the file, we need to check the file and ensure + * that this player has only a few entries. The number of entries is + * controlled by MAXSCORES, and is to ensure that the highscore file is not + * monopolised by just a few people. People who no longer have accounts are + * only allowed the highest score. Scores older than EXPIRATION seconds are + * removed, unless they are someone's personal best. + * Caveat: the highest score on each level is always kept. + */ +static int +checkscores(struct highscore *hs, int num) +{ + struct highscore *sp; + int i, j, k, numnames; + int levelfound[NLEVELS]; + struct peruser { + char *name; + int times; + } count[NUMSPOTS]; + struct peruser *pu; + + /* + * Sort so that highest totals come first. + * + * levelfound[i] becomes set when the first high score for that + * level is encountered. By definition this is the highest score. + */ + qsort((void *)hs, nscores, sizeof(*hs), cmpscores); + for (i = MINLEVEL; i < NLEVELS; i++) + levelfound[i] = 0; + numnames = 0; + for (i = 0, sp = hs; i < num;) { + /* + * This is O(n^2), but do you think we care? + */ + for (j = 0, pu = count; j < numnames; j++, pu++) + if (strcmp(sp->hs_name, pu->name) == 0) + break; + if (j == numnames) { + /* + * Add new user, set per-user count to 1. + */ + pu->name = sp->hs_name; + pu->times = 1; + numnames++; + } else { + /* + * Two ways to keep this score: + * - Not too many (per user), still has acct, & + * score not dated; or + * - High score on this level. + */ + if ((pu->times < MAXSCORES && + getpwnam(sp->hs_name) != NULL && + sp->hs_time + EXPIRATION >= now) || + levelfound[sp->hs_level] == 0) + pu->times++; + else { + /* + * Delete this score, do not count it, + * do not pass go, do not collect $200. + */ + num--; + for (k = i; k < num; k++) + hs[k] = hs[k + 1]; + continue; + } + } + if (sp->hs_level < NLEVELS && sp->hs_level >= 0) + levelfound[sp->hs_level] = 1; + i++, sp++; + } + return (num > MAXHISCORES ? MAXHISCORES : num); +} + +/* + * Show current scores. This must be called after savescore, if + * savescore is called at all, for two reasons: + * - Showscores munches the time field. + * - Even if that were not the case, a new score must be recorded + * before it can be shown anyway. + */ +void +showscores(int level) +{ + struct highscore *sp; + int i, n, c; + const char *me; + int levelfound[NLEVELS]; + + if (!gotscores) + getscores(NULL); + (void)printf("\n\t\t\t Tetris High Scores\n"); + + /* + * If level == 0, the person has not played a game but just asked for + * the high scores; we do not need to check for printing in highlight + * mode. If SOstr is null, we can't do highlighting anyway. + */ + me = level && enter_standout_mode ? thisuser() : NULL; + + /* + * Set times to 0 except for high score on each level. + */ + for (i = MINLEVEL; i < NLEVELS; i++) + levelfound[i] = 0; + for (i = 0, sp = scores; i < nscores; i++, sp++) { + if (sp->hs_level < NLEVELS && sp->hs_level >= 0) { + if (levelfound[sp->hs_level]) + sp->hs_time = 0; + else { + sp->hs_time = 1; + levelfound[sp->hs_level] = 1; + } + } + } + + /* + * Page each screenful of scores. + */ + for (i = 0, sp = scores; i < nscores; sp += n) { + n = 40; + if (i + n > nscores) + n = nscores - i; + printem(level, i + 1, sp, n, me); + if ((i += n) < nscores) { + (void)printf("\nHit RETURN to continue."); + (void)fflush(stdout); + while ((c = getchar()) != '\n') + if (c == EOF) + break; + (void)printf("\n"); + } + } +} + +static void +printem(int level, int offset, struct highscore *hs, int n, const char *me) +{ + struct highscore *sp; + int nrows, row, col, item, i, highlight; + char buf[100]; +#define TITLE "Rank Score Name (points/level)" + + /* + * This makes a nice two-column sort with headers, but it's a bit + * convoluted... + */ + printf("%s %s\n", TITLE, n > 1 ? TITLE : ""); + + highlight = 0; + nrows = (n + 1) / 2; + + for (row = 0; row < nrows; row++) { + for (col = 0; col < 2; col++) { + item = col * nrows + row; + if (item >= n) { + /* + * Can only occur on trailing columns. + */ + (void)putchar('\n'); + continue; + } + sp = &hs[item]; + (void)snprintf(buf, sizeof(buf), + "%3d%c %6d %-11s (%6d on %d)", + item + offset, sp->hs_time ? '*' : ' ', + sp->hs_score * sp->hs_level, + sp->hs_name, sp->hs_score, sp->hs_level); + /* + * Highlight if appropriate. This works because + * we only get one score per level. + */ + if (me != NULL && + sp->hs_level == level && + sp->hs_score == score && + strcmp(sp->hs_name, me) == 0) { + putpad(enter_standout_mode); + highlight = 1; + } + (void)printf("%s", buf); + if (highlight) { + putpad(exit_standout_mode); + highlight = 0; + } + + /* fill in spaces so column 1 lines up */ + if (col == 0) + for (i = 40 - strlen(buf); --i >= 0;) + (void)putchar(' '); + else /* col == 1 */ + (void)putchar('\n'); + } + } +} diff --git a/tetris/scores.h b/tetris/scores.h new file mode 100644 index 0000000..5c8db42 --- /dev/null +++ b/tetris/scores.h @@ -0,0 +1,89 @@ +/* $NetBSD: scores.h,v 1.5 2009/05/25 08:33:57 dholland Exp $ */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek and Darren F. Provine. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)scores.h 8.1 (Berkeley) 5/31/93 + */ + +/* + * Tetris scores. + */ + +#include <stdint.h> /* 20150209 bkw */ + +/* Header for high score file. */ +#define HSH_MAGIC_SIZE 8 +struct highscore_header { + char hsh_magic[HSH_MAGIC_SIZE]; + uint32_t hsh_endiantag; + uint32_t hsh_version; +}; + +/* Current on-disk high score record. */ +struct highscore_ondisk { + char hso_name[20]; + int32_t hso_score; + int32_t hso_level; + int32_t hso_pad; + int64_t hso_time; +}; + +/* 5.99.x after time_t change, on 32-bit machines */ +struct highscore_ondisk_599 { + char hso599_name[20]; + int32_t hso599_score; + int32_t hso599_level; + int32_t hso599_time[2]; +}; + +/* 5.0 and earlier on-disk high score record. */ +struct highscore_ondisk_50 { + char hso50_name[20]; + int32_t hso50_score; + int32_t hso50_level; + int32_t hso50_time; +}; + +/* In-memory high score record. */ +struct highscore { + char hs_name[20]; /* login name */ + int hs_score; /* raw score */ + int hs_level; /* play level */ + time_t hs_time; /* time at game end */ +}; + +#define MAXHISCORES 80 +#define MAXSCORES 9 /* maximum high score entries per person */ +#define EXPIRATION (5L * 365 * 24 * 60 * 60) + +void savescore(int); +void showscores(int); diff --git a/tetris/screen.c b/tetris/screen.c new file mode 100644 index 0000000..af1b1d6 --- /dev/null +++ b/tetris/screen.c @@ -0,0 +1,430 @@ +/* $NetBSD: screen.c,v 1.29 2014/07/13 16:23:55 pgoyette Exp $ */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek and Darren F. Provine. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)screen.c 8.1 (Berkeley) 5/31/93 + */ + +/* + * Tetris screen control. + */ + +#include <sys/cdefs.h> +#include <sys/ioctl.h> + +#include <setjmp.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <term.h> +#include <termios.h> +#include <unistd.h> + +#ifndef sigmask +#define sigmask(s) (1 << ((s) - 1)) +#endif + +#include "screen.h" +#include "tetris.h" + +static cell curscreen[B_SIZE]; /* 1 => standout (or otherwise marked) */ +static int curscore; +static int isset; /* true => terminal is in game mode */ +static struct termios oldtt; +static void (*tstp)(int); + +static void scr_stop(int); +static void stopset(int) __dead; + + +/* + * Routine used by tputs(). + */ +int +put(int c) +{ + + return (putchar(c)); +} + +/* + * putstr() is for unpadded strings (either as in termcap(5) or + * simply literal strings); putpad() is for padded strings with + * count=1. (See screen.h for putpad().) + */ +#define putstr(s) (void)fputs(s, stdout) + +static void +moveto(int r, int c) +{ + char *buf; + + buf = tiparm(cursor_address, r, c); + if (buf != NULL) + putpad(buf); +} + +static void +setcolor(int c) +{ + char *buf; + if (nocolor == 1) + return; + if (set_a_foreground == NULL) + return; + + /* 20150209 bkw: original code: + buf = tiparm(set_a_foreground, c == 7 ? 0 : c); + ...assumes user has a white terminal background. On + Slackware, we have more terminals that default to a + black background (the console itself, Konsole, xfce4-terminal), + so let's assume that instead: */ + buf = tiparm(set_a_foreground, c); + if (buf != NULL) + putpad(buf); +} + +/* + * Set up from termcap. + */ +void +scr_init(void) +{ + + setupterm(NULL, 0, NULL); + if (clear_screen == NULL) + stop("cannot clear screen"); + if (cursor_address == NULL || cursor_up == NULL) + stop("cannot do random cursor positioning"); +} + +/* this foolery is needed to modify tty state `atomically' */ +static jmp_buf scr_onstop; + +static void +stopset(int sig) +{ + sigset_t set; + + (void) signal(sig, SIG_DFL); + (void) kill(getpid(), sig); + sigemptyset(&set); + sigaddset(&set, sig); + (void) sigprocmask(SIG_UNBLOCK, &set, (sigset_t *)0); + longjmp(scr_onstop, 1); +} + +static void +scr_stop(int sig) +{ + sigset_t set; + + scr_end(); + (void) kill(getpid(), sig); + sigemptyset(&set); + sigaddset(&set, sig); + (void) sigprocmask(SIG_UNBLOCK, &set, (sigset_t *)0); + scr_set(); + scr_msg(key_msg, 1); +} + +/* + * Set up screen mode. + */ +void +scr_set(void) +{ + struct winsize ws; + struct termios newtt; + sigset_t nsigset, osigset; + void (*ttou)(int); + + sigemptyset(&nsigset); + sigaddset(&nsigset, SIGTSTP); + sigaddset(&nsigset, SIGTTOU); + (void) sigprocmask(SIG_BLOCK, &nsigset, &osigset); + if ((tstp = signal(SIGTSTP, stopset)) == SIG_IGN) + (void) signal(SIGTSTP, SIG_IGN); + if ((ttou = signal(SIGTTOU, stopset)) == SIG_IGN) + (void) signal(SIGTTOU, SIG_IGN); + /* + * At last, we are ready to modify the tty state. If + * we stop while at it, stopset() above will longjmp back + * to the setjmp here and we will start over. + */ + (void) setjmp(scr_onstop); + (void) sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0); + Rows = 0, Cols = 0; + if (ioctl(0, TIOCGWINSZ, &ws) == 0) { + Rows = ws.ws_row; + Cols = ws.ws_col; + } + if (Rows == 0) + Rows = lines; + if (Cols == 0) + Cols = columns; + if (Rows < MINROWS || Cols < MINCOLS) { + (void) fprintf(stderr, + "the screen is too small: must be at least %dx%d, ", + MINCOLS, MINROWS); + stop(""); /* stop() supplies \n */ + } + if (tcgetattr(0, &oldtt) < 0) + stop("tcgetattr() fails"); + newtt = oldtt; + newtt.c_lflag &= ~(ICANON|ECHO); + newtt.c_oflag &= ~OXTABS; + if (tcsetattr(0, TCSADRAIN, &newtt) < 0) + stop("tcsetattr() fails"); + /* ospeed = cfgetospeed(&newtt); 20150209 bkw */ + (void) sigprocmask(SIG_BLOCK, &nsigset, &osigset); + + /* + * We made it. We are now in screen mode, modulo TIstr + * (which we will fix immediately). + */ + if (enter_ca_mode) + putstr(enter_ca_mode); + if (cursor_invisible) + putstr(cursor_invisible); + if (tstp != SIG_IGN) + (void) signal(SIGTSTP, scr_stop); + if (ttou != SIG_IGN) + (void) signal(SIGTTOU, ttou); + + isset = 1; + (void) sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0); + scr_clear(); +} + +/* + * End screen mode. + */ +void +scr_end(void) +{ + sigset_t nsigset, osigset; + + sigemptyset(&nsigset); + sigaddset(&nsigset, SIGTSTP); + sigaddset(&nsigset, SIGTTOU); + (void) sigprocmask(SIG_BLOCK, &nsigset, &osigset); + /* move cursor to last line */ + if (cursor_to_ll) + putstr(cursor_to_ll); + else + moveto(Rows - 1, 0); + /* exit screen mode */ + if (exit_ca_mode) + putstr(exit_ca_mode); + if (cursor_normal) + putstr(cursor_normal); + (void) fflush(stdout); + (void) tcsetattr(0, TCSADRAIN, &oldtt); + isset = 0; + /* restore signals */ + (void) signal(SIGTSTP, tstp); + (void) sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0); +} + +void +stop(const char *why) +{ + + if (isset) + scr_end(); + (void) fprintf(stderr, "aborting: %s\n", why); + exit(1); +} + +/* + * Clear the screen, forgetting the current contents in the process. + */ +void +scr_clear(void) +{ + + putpad(clear_screen); + curscore = -1; + memset((char *)curscreen, 0, sizeof(curscreen)); +} + +#if vax && !__GNUC__ +typedef int regcell; /* pcc is bad at `register char', etc */ +#else +typedef cell regcell; +#endif + +/* + * Update the screen. + */ +void +scr_update(void) +{ + cell *bp, *sp; + regcell so, cur_so = 0; + int i, ccol, j; + sigset_t nsigset, osigset; + static const struct shape *lastshape; + + sigemptyset(&nsigset); + sigaddset(&nsigset, SIGTSTP); + (void) sigprocmask(SIG_BLOCK, &nsigset, &osigset); + + /* always leave cursor after last displayed point */ + curscreen[D_LAST * B_COLS - 1] = -1; + + if (score != curscore) { + if (cursor_home) + putpad(cursor_home); + else + moveto(0, 0); + (void) printf("Score: %d", score); + curscore = score; + } + + /* draw preview of nextpattern */ + if (showpreview && (nextshape != lastshape)) { + static int r=5, c=2; + int tr, tc, t; + + lastshape = nextshape; + + /* clean */ + putpad(exit_standout_mode); + moveto(r-1, c-1); putstr(" "); + moveto(r, c-1); putstr(" "); + moveto(r+1, c-1); putstr(" "); + moveto(r+2, c-1); putstr(" "); + + moveto(r-3, c-2); + putstr("Next shape:"); + + /* draw */ + putpad(enter_standout_mode); + setcolor(nextshape->color); + moveto(r, 2*c); + putstr(" "); + for(i=0; i<3; i++) { + t = c + r*B_COLS; + t += nextshape->off[i]; + + tr = t / B_COLS; + tc = t % B_COLS; + + moveto(tr, 2*tc); + putstr(" "); + } + putpad(exit_standout_mode); + } + + bp = &board[D_FIRST * B_COLS]; + sp = &curscreen[D_FIRST * B_COLS]; + for (j = D_FIRST; j < D_LAST; j++) { + ccol = -1; + for (i = 0; i < B_COLS; bp++, sp++, i++) { + if (*sp == (so = *bp)) + continue; + *sp = so; + if (i != ccol) { + if (cur_so && move_standout_mode) { + putpad(exit_standout_mode); + cur_so = 0; + } + moveto(RTOD(j), CTOD(i)); + } + if (enter_standout_mode) { + if (so != cur_so) { + putpad(so ? + enter_standout_mode : + exit_standout_mode); + cur_so = so; + } + setcolor(so); +#ifdef DEBUG + char buf[3]; + snprintf(buf, sizeof(buf), "%d%d", so, so); + putstr(buf); +#else + putstr(" "); +#endif + } else + putstr(so ? "XX" : " "); + ccol = i + 1; + /* + * Look ahead a bit, to avoid extra motion if + * we will be redrawing the cell after the next. + * Motion probably takes four or more characters, + * so we save even if we rewrite two cells + * `unnecessarily'. Skip it all, though, if + * the next cell is a different color. + */ +#define STOP (B_COLS - 3) + if (i > STOP || sp[1] != bp[1] || so != bp[1]) + continue; + if (sp[2] != bp[2]) + sp[1] = -1; + else if (i < STOP && so == bp[2] && sp[3] != bp[3]) { + sp[2] = -1; + sp[1] = -1; + } + } + } + if (cur_so) + putpad(exit_standout_mode); + (void) fflush(stdout); + (void) sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0); +} + +/* + * Write a message (set!=0), or clear the same message (set==0). + * (We need its length in case we have to overwrite with blanks.) + */ +void +scr_msg(char *s, int set) +{ + + if (set || clr_eol == NULL) { + int l = strlen(s); + + moveto(Rows - 2, ((Cols - l) >> 1) - 1); + if (set) + putstr(s); + else + while (--l >= 0) + (void) putchar(' '); + } else { + moveto(Rows - 2, 0); + putpad(clr_eol); + } +} diff --git a/tetris/screen.c.orig b/tetris/screen.c.orig new file mode 100644 index 0000000..788a279 --- /dev/null +++ b/tetris/screen.c.orig @@ -0,0 +1,424 @@ +/* $NetBSD: screen.c,v 1.29 2014/07/13 16:23:55 pgoyette Exp $ */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek and Darren F. Provine. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)screen.c 8.1 (Berkeley) 5/31/93 + */ + +/* + * Tetris screen control. + */ + +#include <sys/cdefs.h> +#include <sys/ioctl.h> + +#include <setjmp.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <term.h> +#include <termios.h> +#include <unistd.h> + +#ifndef sigmask +#define sigmask(s) (1 << ((s) - 1)) +#endif + +#include "screen.h" +#include "tetris.h" + +static cell curscreen[B_SIZE]; /* 1 => standout (or otherwise marked) */ +static int curscore; +static int isset; /* true => terminal is in game mode */ +static struct termios oldtt; +static void (*tstp)(int); + +static void scr_stop(int); +static void stopset(int) __dead; + + +/* + * Routine used by tputs(). + */ +int +put(int c) +{ + + return (putchar(c)); +} + +/* + * putstr() is for unpadded strings (either as in termcap(5) or + * simply literal strings); putpad() is for padded strings with + * count=1. (See screen.h for putpad().) + */ +#define putstr(s) (void)fputs(s, stdout) + +static void +moveto(int r, int c) +{ + char *buf; + + buf = tiparm(cursor_address, r, c); + if (buf != NULL) + putpad(buf); +} + +static void +setcolor(int c) +{ + char *buf; + if (nocolor == 1) + return; + if (set_a_foreground == NULL) + return; + + buf = tiparm(set_a_foreground, c == 7 ? 0 : c); + if (buf != NULL) + putpad(buf); +} + +/* + * Set up from termcap. + */ +void +scr_init(void) +{ + + setupterm(NULL, 0, NULL); + if (clear_screen == NULL) + stop("cannot clear screen"); + if (cursor_address == NULL || cursor_up == NULL) + stop("cannot do random cursor positioning"); +} + +/* this foolery is needed to modify tty state `atomically' */ +static jmp_buf scr_onstop; + +static void +stopset(int sig) +{ + sigset_t set; + + (void) signal(sig, SIG_DFL); + (void) kill(getpid(), sig); + sigemptyset(&set); + sigaddset(&set, sig); + (void) sigprocmask(SIG_UNBLOCK, &set, (sigset_t *)0); + longjmp(scr_onstop, 1); +} + +static void +scr_stop(int sig) +{ + sigset_t set; + + scr_end(); + (void) kill(getpid(), sig); + sigemptyset(&set); + sigaddset(&set, sig); + (void) sigprocmask(SIG_UNBLOCK, &set, (sigset_t *)0); + scr_set(); + scr_msg(key_msg, 1); +} + +/* + * Set up screen mode. + */ +void +scr_set(void) +{ + struct winsize ws; + struct termios newtt; + sigset_t nsigset, osigset; + void (*ttou)(int); + + sigemptyset(&nsigset); + sigaddset(&nsigset, SIGTSTP); + sigaddset(&nsigset, SIGTTOU); + (void) sigprocmask(SIG_BLOCK, &nsigset, &osigset); + if ((tstp = signal(SIGTSTP, stopset)) == SIG_IGN) + (void) signal(SIGTSTP, SIG_IGN); + if ((ttou = signal(SIGTTOU, stopset)) == SIG_IGN) + (void) signal(SIGTTOU, SIG_IGN); + /* + * At last, we are ready to modify the tty state. If + * we stop while at it, stopset() above will longjmp back + * to the setjmp here and we will start over. + */ + (void) setjmp(scr_onstop); + (void) sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0); + Rows = 0, Cols = 0; + if (ioctl(0, TIOCGWINSZ, &ws) == 0) { + Rows = ws.ws_row; + Cols = ws.ws_col; + } + if (Rows == 0) + Rows = lines; + if (Cols == 0) + Cols = columns; + if (Rows < MINROWS || Cols < MINCOLS) { + (void) fprintf(stderr, + "the screen is too small: must be at least %dx%d, ", + MINCOLS, MINROWS); + stop(""); /* stop() supplies \n */ + } + if (tcgetattr(0, &oldtt) < 0) + stop("tcgetattr() fails"); + newtt = oldtt; + newtt.c_lflag &= ~(ICANON|ECHO); + newtt.c_oflag &= ~OXTABS; + if (tcsetattr(0, TCSADRAIN, &newtt) < 0) + stop("tcsetattr() fails"); + /* ospeed = cfgetospeed(&newtt); 20150209 bkw */ + (void) sigprocmask(SIG_BLOCK, &nsigset, &osigset); + + /* + * We made it. We are now in screen mode, modulo TIstr + * (which we will fix immediately). + */ + if (enter_ca_mode) + putstr(enter_ca_mode); + if (cursor_invisible) + putstr(cursor_invisible); + if (tstp != SIG_IGN) + (void) signal(SIGTSTP, scr_stop); + if (ttou != SIG_IGN) + (void) signal(SIGTTOU, ttou); + + isset = 1; + (void) sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0); + scr_clear(); +} + +/* + * End screen mode. + */ +void +scr_end(void) +{ + sigset_t nsigset, osigset; + + sigemptyset(&nsigset); + sigaddset(&nsigset, SIGTSTP); + sigaddset(&nsigset, SIGTTOU); + (void) sigprocmask(SIG_BLOCK, &nsigset, &osigset); + /* move cursor to last line */ + if (cursor_to_ll) + putstr(cursor_to_ll); + else + moveto(Rows - 1, 0); + /* exit screen mode */ + if (exit_ca_mode) + putstr(exit_ca_mode); + if (cursor_normal) + putstr(cursor_normal); + (void) fflush(stdout); + (void) tcsetattr(0, TCSADRAIN, &oldtt); + isset = 0; + /* restore signals */ + (void) signal(SIGTSTP, tstp); + (void) sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0); +} + +void +stop(const char *why) +{ + + if (isset) + scr_end(); + (void) fprintf(stderr, "aborting: %s\n", why); + exit(1); +} + +/* + * Clear the screen, forgetting the current contents in the process. + */ +void +scr_clear(void) +{ + + putpad(clear_screen); + curscore = -1; + memset((char *)curscreen, 0, sizeof(curscreen)); +} + +#if vax && !__GNUC__ +typedef int regcell; /* pcc is bad at `register char', etc */ +#else +typedef cell regcell; +#endif + +/* + * Update the screen. + */ +void +scr_update(void) +{ + cell *bp, *sp; + regcell so, cur_so = 0; + int i, ccol, j; + sigset_t nsigset, osigset; + static const struct shape *lastshape; + + sigemptyset(&nsigset); + sigaddset(&nsigset, SIGTSTP); + (void) sigprocmask(SIG_BLOCK, &nsigset, &osigset); + + /* always leave cursor after last displayed point */ + curscreen[D_LAST * B_COLS - 1] = -1; + + if (score != curscore) { + if (cursor_home) + putpad(cursor_home); + else + moveto(0, 0); + (void) printf("Score: %d", score); + curscore = score; + } + + /* draw preview of nextpattern */ + if (showpreview && (nextshape != lastshape)) { + static int r=5, c=2; + int tr, tc, t; + + lastshape = nextshape; + + /* clean */ + putpad(exit_standout_mode); + moveto(r-1, c-1); putstr(" "); + moveto(r, c-1); putstr(" "); + moveto(r+1, c-1); putstr(" "); + moveto(r+2, c-1); putstr(" "); + + moveto(r-3, c-2); + putstr("Next shape:"); + + /* draw */ + putpad(enter_standout_mode); + setcolor(nextshape->color); + moveto(r, 2*c); + putstr(" "); + for(i=0; i<3; i++) { + t = c + r*B_COLS; + t += nextshape->off[i]; + + tr = t / B_COLS; + tc = t % B_COLS; + + moveto(tr, 2*tc); + putstr(" "); + } + putpad(exit_standout_mode); + } + + bp = &board[D_FIRST * B_COLS]; + sp = &curscreen[D_FIRST * B_COLS]; + for (j = D_FIRST; j < D_LAST; j++) { + ccol = -1; + for (i = 0; i < B_COLS; bp++, sp++, i++) { + if (*sp == (so = *bp)) + continue; + *sp = so; + if (i != ccol) { + if (cur_so && move_standout_mode) { + putpad(exit_standout_mode); + cur_so = 0; + } + moveto(RTOD(j), CTOD(i)); + } + if (enter_standout_mode) { + if (so != cur_so) { + putpad(so ? + enter_standout_mode : + exit_standout_mode); + cur_so = so; + } + setcolor(so); +#ifdef DEBUG + char buf[3]; + snprintf(buf, sizeof(buf), "%d%d", so, so); + putstr(buf); +#else + putstr(" "); +#endif + } else + putstr(so ? "XX" : " "); + ccol = i + 1; + /* + * Look ahead a bit, to avoid extra motion if + * we will be redrawing the cell after the next. + * Motion probably takes four or more characters, + * so we save even if we rewrite two cells + * `unnecessarily'. Skip it all, though, if + * the next cell is a different color. + */ +#define STOP (B_COLS - 3) + if (i > STOP || sp[1] != bp[1] || so != bp[1]) + continue; + if (sp[2] != bp[2]) + sp[1] = -1; + else if (i < STOP && so == bp[2] && sp[3] != bp[3]) { + sp[2] = -1; + sp[1] = -1; + } + } + } + if (cur_so) + putpad(exit_standout_mode); + (void) fflush(stdout); + (void) sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0); +} + +/* + * Write a message (set!=0), or clear the same message (set==0). + * (We need its length in case we have to overwrite with blanks.) + */ +void +scr_msg(char *s, int set) +{ + + if (set || clr_eol == NULL) { + int l = strlen(s); + + moveto(Rows - 2, ((Cols - l) >> 1) - 1); + if (set) + putstr(s); + else + while (--l >= 0) + (void) putchar(' '); + } else { + moveto(Rows - 2, 0); + putpad(clr_eol); + } +} diff --git a/tetris/screen.h b/tetris/screen.h new file mode 100644 index 0000000..c0b3905 --- /dev/null +++ b/tetris/screen.h @@ -0,0 +1,54 @@ +/* $NetBSD: screen.h,v 1.9 2009/08/12 08:51:21 dholland Exp $ */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek and Darren F. Provine. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)screen.h 8.1 (Berkeley) 5/31/93 + */ + +/* + * Capabilities from TERMCAP (used in the score code). + */ +extern char *SEstr; /* end standout mode */ +extern char *SOstr; /* begin standout mode */ + +/* + * putpad() is for padded strings with count=1. + */ +#define putpad(s) tputs(s, 1, put) + +int put(int); /* just calls putchar; for tputs */ +void scr_clear(void); +void scr_end(void); +void scr_init(void); +void scr_msg(char *, int); +void scr_set(void); +void scr_update(void); diff --git a/tetris/shapes.c b/tetris/shapes.c new file mode 100644 index 0000000..ef82771 --- /dev/null +++ b/tetris/shapes.c @@ -0,0 +1,106 @@ +/* $NetBSD: shapes.c,v 1.9 2014/06/11 16:47:39 christos Exp $ */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek and Darren F. Provine. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)shapes.c 8.1 (Berkeley) 5/31/93 + */ + +/* + * Tetris shapes and related routines. + * + * Note that the first 7 are `well known'. + */ + +#include <sys/cdefs.h> +#include "tetris.h" + +#define TL -B_COLS-1 /* top left */ +#define TC -B_COLS /* top center */ +#define TR -B_COLS+1 /* top right */ +#define ML -1 /* middle left */ +#define MR 1 /* middle right */ +#define BL B_COLS-1 /* bottom left */ +#define BC B_COLS /* bottom center */ +#define BR B_COLS+1 /* bottom right */ + +const struct shape shapes[] = { + /* 0*/ { 7, 7, { TL, TC, MR, } }, + /* 1*/ { 1, 8, { TC, TR, ML, } }, + /* 2*/ { 2, 9, { ML, MR, BC, } }, + /* 3*/ { 3, 3, { TL, TC, ML, } }, + /* 4*/ { 4, 12, { ML, BL, MR, } }, + /* 5*/ { 5, 15, { ML, BR, MR, } }, + /* 6*/ { 6, 18, { ML, MR, 2 } }, /* sticks out */ + /* 7*/ { 7, 0, { TC, ML, BL, } }, + /* 8*/ { 1, 1, { TC, MR, BR, } }, + /* 9*/ { 2, 10, { TC, MR, BC, } }, + /*10*/ { 2, 11, { TC, ML, MR, } }, + /*11*/ { 2, 2, { TC, ML, BC, } }, + /*12*/ { 4, 13, { TC, BC, BR, } }, + /*13*/ { 4, 14, { TR, ML, MR, } }, + /*14*/ { 4, 4, { TL, TC, BC, } }, + /*15*/ { 5, 16, { TR, TC, BC, } }, + /*16*/ { 5, 17, { TL, MR, ML, } }, + /*17*/ { 5, 5, { TC, BC, BL, } }, + /*18*/ { 6, 6, { TC, BC, 2*B_COLS } } /* sticks out */ +}; + +/* + * Return true iff the given shape fits in the given position, + * taking the current board into account. + */ +int +fits_in(const struct shape *shape, int pos) +{ + const int *o = shape->off; + + if (board[pos] || board[pos + *o++] || board[pos + *o++] || + board[pos + *o]) + return 0; + return 1; +} + +/* + * Write the given shape into the current board, turning it on + * if `onoff' is 1, and off if `onoff' is 0. + */ +void +place(const struct shape *shape, int pos, int onoff) +{ + const int *o = shape->off; + onoff = onoff ? shape->color : 0; + + board[pos] = onoff; + board[pos + *o++] = onoff; + board[pos + *o++] = onoff; + board[pos + *o] = onoff; +} diff --git a/tetris/tetris.6 b/tetris/tetris.6 new file mode 100644 index 0000000..b0c6778 --- /dev/null +++ b/tetris/tetris.6 @@ -0,0 +1,161 @@ +.\" $NetBSD: tetris.6,v 1.14 2014/07/15 16:17:15 wiz Exp $ +.\" +.\" Copyright (c) 1992, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Nancy L. Tinkham and Darren F. Provine. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)tetris.6 8.1 (Berkeley) 5/31/93 +.\" +.Dd July 13, 2014 +.Dt TETRIS 6 +.Os +.Sh NAME +.Nm tetris +.Nd the game of tetris +.Sh SYNOPSIS +.Nm +.Op Fl bps +.Op Fl k Ar keys +.Op Fl l Ar level +.Sh DESCRIPTION +The +.Nm +command runs display-based game which must be played on a CRT terminal. +The object is to fit the shapes together forming complete rows, +which then vanish. +When the shapes fill up to the top, the game ends. +You can optionally select a level of play, or custom-select control keys. +.Pp +The default level of play is 2. +.Pp +The default control keys are as follows: +.Pp +.Bl -tag -width "xxspacexx" -compact -offset indent +.It j +move left +.It k +rotate 1/4 turn counterclockwise +.It l +move right +.It Aq space +drop +.It p +pause +.It q +quit +.El +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl b +By default, shapes are displayed colorfully if the user's CRT supports color. +The +.Fl b +option can be used to restore the traditional black-and-white behavior. +.It Fl k +The default control keys can be changed using the +.Fl k +option. +The +.Ar keys +argument must have the six keys in order, and, remember to quote any +space or tab characters from the shell. +For example: +.sp +.Dl "tetris -l 2 -k 'jkl pq'" +.sp +will play the default games, i.e. level 2 and with the default +control keys. +The current key settings are displayed at the bottom of the screen +during play. +.It Fl l +Select a level of play. +.It Fl s +Display the top scores. +.It Fl p +Switch on previewing of the shape that will appear next. +.El +.Sh PLAY +At the start of the game, a shape will appear at the top of the screen, +falling one square at a time. +The speed at which it falls is determined directly by the level: +if you select level 2, the blocks will fall twice per second; +at level 9, they fall 9 times per second. +(As the game goes on, things speed up, +no matter what your initial selection.) +When this shape +.Dq touches down +on the bottom of the field, another will appear at the top. +.Pp +You can move shapes to the left or right, rotate them counterclockwise, +or drop them to the bottom by pressing the appropriate keys. +As you fit them together, completed horizontal rows vanish, +and any blocks above fall down to fill in. +When the blocks stack up to the top of the screen, the game is over. +.Sh SCORING +You get one point for every block you fit into the stack, +and one point for every space a block falls when you hit the drop key. +(Dropping the blocks is therefore a good way to increase your score.) +Your total score is the product of the level of play +and your accumulated +.ie t points\(em200 +.el points -- 200 +points on level 3 gives you a score of 600. +Each player gets at most one entry on any level, +for a total of nine scores in the high scores file. +Players who no longer have accounts are limited to one score. +Also, scores over 5 years old are expired. +The exception to these conditions is that the highest score on a given +level is +.Em always +kept, +so that following generations can pay homage to those who have +wasted serious amounts of time. +.Pp +The score list is produced at the end of the game. +The printout includes each player's overall ranking, +name, score, and how many points were scored on what level. +Scores which are the highest on a given level +are marked with asterisks +.Dq * . +.Sh FILES +.Bl -tag -width /var/games/tetris.scoresxx +.It /var/games/tetris.scores +high score file +.El +.Sh AUTHORS +Adapted from a 1989 International Obfuscated C Code Contest winner by +Chris Torek and Darren F. Provine. +.Pp +Manual adapted from the original entry written by Nancy L. Tinkham and +Darren F. Provine. +.Pp +Code for previewing next shape added by Hubert Feyrer in 1999. +.Sh BUGS +The higher levels are unplayable without a fast terminal connection. diff --git a/tetris/tetris.c b/tetris/tetris.c new file mode 100644 index 0000000..53a31f2 --- /dev/null +++ b/tetris/tetris.c @@ -0,0 +1,338 @@ +/* $NetBSD: tetris.c,v 1.27 2014/07/13 17:38:38 pgoyette Exp $ */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek and Darren F. Provine. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)tetris.c 8.1 (Berkeley) 5/31/93 + */ + +#include <sys/cdefs.h> +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1992, 1993\ + The Regents of the University of California. All rights reserved."); +#endif /* not lint */ + +/* + * Tetris (or however it is spelled). + */ + +#include <sys/time.h> + +#include <err.h> +#include <fcntl.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "input.h" +#include "scores.h" +#include "screen.h" +#include "tetris.h" + +cell board[B_SIZE]; /* 1 => occupied, 0 => empty */ + +int Rows, Cols; /* current screen size */ + +static const struct shape *curshape; +const struct shape *nextshape; + +long fallrate; /* less than 1 million; smaller => faster */ + +int score; /* the obvious thing */ +gid_t gid, egid; + +char key_msg[100]; +int showpreview; +int nocolor; + +static void elide(void); +static void setup_board(void); +static void onintr(int) __dead; +static void usage(void) __dead; + +/* + * Set up the initial board. The bottom display row is completely set, + * along with another (hidden) row underneath that. Also, the left and + * right edges are set. + */ +static void +setup_board(void) +{ + int i; + cell *p; + + p = board; + for (i = B_SIZE; i; i--) + *p++ = (i <= (2 * B_COLS) || (i % B_COLS) < 2) ? 7 : 0; +} + +/* + * Elide any full active rows. + */ +static void +elide(void) +{ + int i, j, base; + cell *p; + + for (i = A_FIRST; i < A_LAST; i++) { + base = i * B_COLS + 1; + p = &board[base]; + for (j = B_COLS - 2; *p++ != 0;) { + if (--j <= 0) { + /* this row is to be elided */ + memset(&board[base], 0, B_COLS - 2); + scr_update(); + tsleep(); + while (--base != 0) + board[base + B_COLS] = board[base]; + scr_update(); + tsleep(); + break; + } + } + } +} + +int +main(int argc, char *argv[]) +{ + int pos, c; + const char *keys; + int level = 2; + char key_write[6][10]; + int ch, i, j; + int fd; + + gid = getgid(); + egid = getegid(); + setegid(gid); + + fd = open("/dev/null", O_RDONLY); + if (fd < 3) + exit(1); + close(fd); + + keys = "jkl pq"; + + while ((ch = getopt(argc, argv, "bk:l:ps")) != -1) + switch(ch) { + case 'b': + nocolor = 1; + break; + case 'k': + if (strlen(keys = optarg) != 6) + usage(); + break; + case 'l': + level = atoi(optarg); + if (level < MINLEVEL || level > MAXLEVEL) { + errx(1, "level must be from %d to %d", + MINLEVEL, MAXLEVEL); + } + break; + case 'p': + showpreview = 1; + break; + case 's': + showscores(0); + exit(0); + case '?': + default: + usage(); + } + + argc -= optind; + argv += optind; + + if (argc) + usage(); + + fallrate = 1000000 / level; + + for (i = 0; i <= 5; i++) { + for (j = i+1; j <= 5; j++) { + if (keys[i] == keys[j]) { + errx(1, "duplicate command keys specified."); + } + } + if (keys[i] == ' ') + strcpy(key_write[i], "<space>"); + else { + key_write[i][0] = keys[i]; + key_write[i][1] = '\0'; + } + } + + snprintf(key_msg, sizeof(key_msg), +"%s - left %s - rotate %s - right %s - drop %s - pause %s - quit", + key_write[0], key_write[1], key_write[2], key_write[3], + key_write[4], key_write[5]); + + (void)signal(SIGINT, onintr); + scr_init(); + setup_board(); + + srandom(getpid()); + scr_set(); + + pos = A_FIRST*B_COLS + (B_COLS/2)-1; + nextshape = randshape(); + curshape = randshape(); + + scr_msg(key_msg, 1); + + for (;;) { + place(curshape, pos, 1); + scr_update(); + place(curshape, pos, 0); + c = tgetchar(); + if (c < 0) { + /* + * Timeout. Move down if possible. + */ + if (fits_in(curshape, pos + B_COLS)) { + pos += B_COLS; + continue; + } + + /* + * Put up the current shape `permanently', + * bump score, and elide any full rows. + */ + place(curshape, pos, 1); + score++; + elide(); + + /* + * Choose a new shape. If it does not fit, + * the game is over. + */ + curshape = nextshape; + nextshape = randshape(); + pos = A_FIRST*B_COLS + (B_COLS/2)-1; + if (!fits_in(curshape, pos)) + break; + continue; + } + + /* + * Handle command keys. + */ + if (c == keys[5]) { + /* quit */ + break; + } + if (c == keys[4]) { + static char msg[] = + "paused - press RETURN to continue"; + + place(curshape, pos, 1); + do { + scr_update(); + scr_msg(key_msg, 0); + scr_msg(msg, 1); + (void) fflush(stdout); + } while (rwait(NULL) == -1); + scr_msg(msg, 0); + scr_msg(key_msg, 1); + place(curshape, pos, 0); + continue; + } + if (c == keys[0]) { + /* move left */ + if (fits_in(curshape, pos - 1)) + pos--; + continue; + } + if (c == keys[1]) { + /* turn */ + const struct shape *new = &shapes[curshape->rot]; + + if (fits_in(new, pos)) + curshape = new; + continue; + } + if (c == keys[2]) { + /* move right */ + if (fits_in(curshape, pos + 1)) + pos++; + continue; + } + if (c == keys[3]) { + /* move to bottom */ + while (fits_in(curshape, pos + B_COLS)) { + pos += B_COLS; + score++; + } + continue; + } + if (c == '\f') { + scr_clear(); + scr_msg(key_msg, 1); + } + } + + scr_clear(); + scr_end(); + + (void)printf("Your score: %d point%s x level %d = %d\n", + score, score == 1 ? "" : "s", level, score * level); + savescore(level); + + printf("\nHit RETURN to see high scores, ^C to skip.\n"); + + while ((i = getchar()) != '\n') + if (i == EOF) + break; + + showscores(level); + + exit(0); +} + +static void +onintr(int signo __attribute__((unused))) +{ + scr_clear(); + scr_end(); + exit(0); +} + +static void +usage(void) +{ + (void)fprintf(stderr, "usage: %s [-ps] [-k keys] [-l level]\n", + getprogname()); + exit(1); +} diff --git a/tetris/tetris.h b/tetris/tetris.h new file mode 100644 index 0000000..559ef76 --- /dev/null +++ b/tetris/tetris.h @@ -0,0 +1,175 @@ +/* $NetBSD: tetris.h,v 1.14 2014/07/13 16:23:55 pgoyette Exp $ */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek and Darren F. Provine. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)tetris.h 8.1 (Berkeley) 5/31/93 + */ + +#include <sys/types.h> + +/* + * Definitions for Tetris. + */ + +/* + * The display (`board') is composed of 23 rows of 12 columns of characters + * (numbered 0..22 and 0..11), stored in a single array for convenience. + * Columns 1 to 10 of rows 1 to 20 are the actual playing area, where + * shapes appear. Columns 0 and 11 are always occupied, as are all + * columns of rows 21 and 22. Rows 0 and 22 exist as boundary areas + * so that regions `outside' the visible area can be examined without + * worrying about addressing problems. + */ + + /* the board */ +#define B_COLS 12 +#define B_ROWS 23 +#define B_SIZE (B_ROWS * B_COLS) + +typedef unsigned char cell; +extern cell board[B_SIZE]; /* 1 => occupied, 0 => empty */ + + /* the displayed area (rows) */ +#define D_FIRST 1 +#define D_LAST 22 + + /* the active area (rows) */ +#define A_FIRST 1 +#define A_LAST 21 + +/* + * Minimum display size. + */ +#define MINROWS 23 +#define MINCOLS 40 + +extern int Rows, Cols; /* current screen size */ + +/* + * Translations from board coordinates to display coordinates. + * As with board coordinates, display coordiates are zero origin. + */ +#define RTOD(x) ((x) - 1) +#define CTOD(x) ((x) * 2 + (((Cols - 2 * B_COLS) >> 1) - 1)) + +/* + * A `shape' is the fundamental thing that makes up the game. There + * are 7 basic shapes, each consisting of four `blots': + * + * X.X X.X X.X + * X.X X.X X.X.X X.X X.X.X X.X.X X.X.X.X + * X X X + * + * 0 1 2 3 4 5 6 + * + * Except for 3 and 6, the center of each shape is one of the blots. + * This blot is designated (0,0). The other three blots can then be + * described as offsets from the center. Shape 3 is the same under + * rotation, so its center is effectively irrelevant; it has been chosen + * so that it `sticks out' upward and leftward. Except for shape 6, + * all the blots are contained in a box going from (-1,-1) to (+1,+1); + * shape 6's center `wobbles' as it rotates, so that while it `sticks out' + * rightward, its rotation---a vertical line---`sticks out' downward. + * The containment box has to include the offset (2,0), making the overall + * containment box range from offset (-1,-1) to (+2,+1). (This is why + * there is only one row above, but two rows below, the display area.) + * + * The game works by choosing one of these shapes at random and putting + * its center at the middle of the first display row (row 1, column 5). + * The shape is moved steadily downward until it collides with something: + * either another shape, or the bottom of the board. When the shape can + * no longer be moved downwards, it is merged into the current board. + * At this time, any completely filled rows are elided, and blots above + * these rows move down to make more room. A new random shape is again + * introduced at the top of the board, and the whole process repeats. + * The game ends when the new shape will not fit at (1,5). + * + * While the shapes are falling, the user can rotate them counterclockwise + * 90 degrees (in addition to moving them left or right), provided that the + * rotation puts the blots in empty spaces. The table of shapes is set up + * so that each shape contains the index of the new shape obtained by + * rotating the current shape. Due to symmetry, each shape has exactly + * 1, 2, or 4 rotations total; the first 7 entries in the table represent + * the primary shapes, and the remaining 12 represent their various + * rotated forms. + */ +struct shape { + int color; + int rot; /* index of rotated version of this shape */ + int off[3]; /* offsets to other blots if center is at (0,0) */ +}; + +extern const struct shape shapes[]; +#define randshape() (&shapes[random() % 7]) + +extern const struct shape *nextshape; + +/* + * Shapes fall at a rate faster than once per second. + * + * The initial rate is determined by dividing 1 million microseconds + * by the game `level'. (This is at most 1 million, or one second.) + * Each time the fall-rate is used, it is decreased a little bit, + * depending on its current value, via the `faster' macro below. + * The value eventually reaches a limit, and things stop going faster, + * but by then the game is utterly impossible. + */ +extern long fallrate; /* less than 1 million; smaller => faster */ +#define faster() (fallrate -= fallrate / 3000) + +/* + * Game level must be between 1 and 9. This controls the initial fall rate + * and affects scoring. + */ +#define MINLEVEL 1 +#define MAXLEVEL 9 + +/* + * Scoring is as follows: + * + * When the shape comes to rest, and is integrated into the board, + * we score one point. If the shape is high up (at a low-numbered row), + * and the user hits the space bar, the shape plummets all the way down, + * and we score a point for each row it falls (plus one more as soon as + * we find that it is at rest and integrate it---until then, it can + * still be moved or rotated). + */ +extern int score; /* the obvious thing */ +extern gid_t gid, egid; + +extern char key_msg[100]; +extern int showpreview; +extern int nocolor; + +int fits_in(const struct shape *, int); +void place(const struct shape *, int, int); +void stop(const char *) __dead; |