pc-hack

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

commit b3b1674e2431de730f5c3085873a48db4d9c0dac
Author: Brian Swetland <swetland@frotz.net>
Date:   Mon, 21 Nov 2022 12:46:15 -0800

initial checkin

Diffstat:
Andmake/make.doc | 1122+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Andmake/make.exe | 0
Andmake/make.ini | 65+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Areadme.txt | 3+++
Asrc/alloc.c | 48++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/apply.c | 445+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/bones.c | 115+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/cmd.c | 390+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/config.h | 170+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/decl.c | 58++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/do.c | 524+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/do_name.c | 320+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/do_wear.c | 344+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/dog.c | 426+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/eat.c | 496+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/edog.h | 12++++++++++++
Asrc/end.c | 596+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/engrave.c | 310+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/eshk.h | 24++++++++++++++++++++++++
Asrc/fight.c | 385+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/flag.h | 53+++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/gen.h | 15+++++++++++++++
Asrc/gold.h | 12++++++++++++
Asrc/hack.c | 830+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/hack.h | 159+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/invent.c | 927+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lev.c | 559+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/main.c | 494+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/makefile | 98+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/makemon.c | 244+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/mfndpos.h | 12++++++++++++
Asrc/mhitu.c | 379+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/mklev.c | 790+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/mkmaze.c | 187+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/mkobj.c | 148+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/mkroom.h | 26++++++++++++++++++++++++++
Asrc/mkshop.c | 273+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/mon.c | 890+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/monst.c | 76++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/monst.h | 60++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/msdos.c | 758+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/msdos.h | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/o_init.c | 201+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/obj.h | 56++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/objclass.h | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/objects.h | 289++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/objnam.c | 547+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/onames.h | 227+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/options.c | 336+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/pager.c | 305++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/permonst.h | 25+++++++++++++++++++++++++
Asrc/potion.c | 386+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/pri.c | 746+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/read.c | 527+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/readme.txt | 89+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/rip.c | 78++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/rm.h | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/rnd.c | 30++++++++++++++++++++++++++++++
Asrc/rumors.c | 111+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/save.c | 355+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/search.c | 141+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/shk.c | 998+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/shknam.c | 140+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/steal.c | 203+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/termcap.c | 310+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/timeout.c | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/topl.c | 192+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/track.c | 38++++++++++++++++++++++++++++++++++++++
Asrc/trap.c | 464+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/trap.h | 27+++++++++++++++++++++++++++
Asrc/tty.c | 204+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/u_init.c | 344+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/unix.c | 89+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/vault.c | 248+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/version.c | 17+++++++++++++++++
Asrc/wield.c | 99+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/wizard.c | 339+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/worm.c | 183+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/worn.c | 65+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/wseg.h | 13+++++++++++++
Asrc/zap.c | 657+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atermcap/fgetlr.c | 109+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atermcap/isdigit.c | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atermcap/makefile | 60++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atermcap/termcap | 26++++++++++++++++++++++++++
Atermcap/testtcp.c | 348+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atermcap/tgetent.c | 320+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atermcap/tgetflag.c | 89+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atermcap/tgetnum.c | 108+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atermcap/tgetstr.c | 281+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atermcap/tgoto.c | 247+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atermcap/tputs.c | 218+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
92 files changed, 24035 insertions(+), 0 deletions(-)

diff --git a/ndmake/make.doc b/ndmake/make.doc @@ -0,0 +1,1122 @@ + + + + + + + + NDMAKE version 3.8 + ------------------ + Copyright (C) 1985, 1986 D. G. Kneller + All rights reserved. + + + Program Description + ------------------- + + NDMAKE is an implementation of the UNIX(tm) program maintenance + utility called `make'. It has the same syntax and most of the + capability. If you are familiar with UNIX `make' you should have no + difficulties with NDMAKE. In the rest of this documentation, NDMAKE + will be referred to simply as MAKE. + + MAKE is a utility that helps you maintain programs, particularly + programs that are composed of several modules (files). Once you + describe the relationships between these modules, MAKE will keep track + of the modules and only `make' those that are out of date with respect + to their sources. MAKE can also be used as a general compile and link + tool for handling single files, much like a batch file. + + One feature of MAKE that makes it very useful for software development + is its special handling of LINK. Under MSDOS(tm), commands must be + shorter than the command line limit of 128 characters. Since LINK is + used so often when doing modular programming, MAKE knows about it + specially and will automatically generate a response file if the LINK + command is longer than the limit. + + MAKE requires at least DOS 2.0 and uses a minimum of about 40000 bytes + of memory. Since MAKE executes other programs from within itself, + this memory will be unavailable to them while MAKE is running. Also, + since MAKE uses the file time to determine which files are out of + date, it is imperative that you either have a real-time clock or are + diligent about setting the time and date when you boot up. + + + Synopsis + -------- + + make [ -f makefile ] [ options ] [ macros ] [ targets ] + + The [ ] delimit optional parameters. [ options ] and [ macros ] will + be discussed later. + + + Description + ----------- + + MAKE executes commands in a MAKE description file to update one or + more targets. The targets are typically the names of programs. If no + -f option is present, the MAKE description file called MAKEFILE is + + + + + + + + NDMAKE v3.8 page 2 + + + + + + tried. If makefile is `-', the keyboard (standard input) is used as + the makefile. More than one -f option may appear. + + Make updates a target if it is older than the files it depends on, or + if the target does not exist. If no targets are given on the command + line, the first target in the makefile is `made'. + + + The MAKE description files + -------------------------- + + MAKE uses 2 description files: MAKEFILE and MAKE.INI. The description + files consists of several kinds of entries: + + 1) dependency and command lines + 2) macro definitions + 3) default rules + 4) "dot commands" + + When MAKE starts up, it looks for an initialization file called + MAKE.INI. This file usually contains only default rules and macro + definitions that you don't want to put in every makefile. The current + directory is searched first, followed by directories along the PATH. + You customize your copy of MAKE by changing MAKE.INI. + + + 1) Dependency and command lines + ------------------------------- + + These are lines that specify the relationship between targets and + prerequisites, and how to update the targets. The general form is: + + targets : [prerequisites] + [<tab>command] + .... + + where <tab> is the tab character. + + The first line of an entry is a blank-separated list of targets, then + a colon, then a list of prerequisite files. All following lines that + begin with a tab are commands to be executed to update the target. + For example, assume you have a program TEST.EXE that is composed of + modules MAIN.OBJ and SUB.OBJ. Each of these depend on a common + include file, INCL.H, and on their respective `.c' files. The + makefile might look like: + + test.exe : main.obj sub.obj + link main.obj sub.obj, test; + + main.obj : main.c incl.h + msc -AL main.c; + + sub.obj : sub.c incl.h + msc -AL sub.c; + + + + + + + NDMAKE v3.8 page 3 + + + + + + If a target appears on the left of more than one `colon' line, then it + depends on all of the names on the right of the colon on those lines, + but only one command sequence may be specified for it. The previous + example could have been written as: + + test.exe : main.obj sub.obj + link main.obj sub.obj, test; + + main.obj sub.obj : incl.h + + main.obj : main.c + msc -AL main.c; + + sub.obj : sub.c + msc -AL sub.c; + + When you do the command `make' without any arguments, the first target + in MAKEFILE gets made. A dummy target is often used as the first + target when you wish to make several targets. For example: + + all : target1.exe target2.exe + + target1.exe : .... + + target2.exe : .... + + Executing `make' with no arguments results in both target1.exe and + target2.exe being made. This happens because MAKE always ensures all + prerequisites are up to date before it makes a target. Here, the + target ALL has two prerequisite files - TARGET1.EXE and TARGET2.EXE. + First TARGET1.EXE then TARGET2.EXE get made, then MAKE checks if + either of these files are more current than ALL. Since ALL doesn't + exist MAKE assumes its very old. Both TARGET1.EXE and TARGET2.EXE are + newer than ALL, so the commands to update ALL will be executed. MAKE + sees there are no commands and stops. + + + 2) Macro definitions + -------------------- + + Makefile entries of the form: + + name = [value] + + are macro definitions. Macros allow the association of a name and a + value. Subsequent appearances of $(name) or ${name} are replaced by + value. If name is a single character, the parentheses or braces are + optional. Spaces between name and =, and between = and value are + ignored. If value is not given, the macro value is a null string. + + The previous example could have had: + + OBJS = main.obj sub.obj + + + + + + + + NDMAKE v3.8 page 4 + + + + + + test.exe : $(OBJS) + link $(OBJS), test; + + main.obj : main.c + msc -AL main.c; + + sub.obj : sub.c + msc -AL sub.c; + + $(OBJS) : incl.h + + Macros can be entered as command line parameters. For example: + + A> make CFLAGS=-AL LIBS= test.exe + + MAKE evaluates macros only when needed, and the order in which macros + appear in a description file is insignificant. Conventionally, all + definitions appear at the top of the description file. If the same + name is defined more than once, the most recent definition is used. + The precedence of definitions is: + + 1. command line definition (highest) + 2. MAKEFILE definition + 3. MAKE.INI definition + 4. environment definition (lowest) + - this refers to the environment variables put into the DOS + environment with the `SET variable = value' DOS command. + + + Predefined macros: + + There are 4 "run-time" macros. These are: + + $* - stands for the target name with suffix deleted + $@ - stands for the full target name + $< - stands for the complete list of prerequisites + $? - stands for the list of prerequisites that are out of date + with respect to the target. + + These are usually used when defining default rules (to be discussed + next). Unlike UNIX `make', you can use these run-time macros anywhere + they make sense. Thus, a dependency line for OBJS could be: + + $(OBJS) : $*.c + + The macro `MFLAGS' gets filled in with the initial command line + options supplied to MAKE. This can be used to invoke MAKE on + makefiles in subdirectories and pass along the options. + + The macro `CWD' gets filled in with the current directory. If you + `cd' to another directory, you can `cd $(CWD)' to get back. + + The macro `$$' evaluates to the dollar sign `$'. + + + + + + + + NDMAKE v3.8 page 5 + + + + + + 3) Default rules + ---------------- + + MAKE can use default rules to specify commands for files for which the + makefile gives no explicit commands. A default rule tells MAKE how to + create a file with a particular extension from a file with the same + base name but another extension. Default rules take the following + form: + + .from_extension.to_extension : + command + [command] + ... + + For example, to produce a `.obj' file from a `.c' file, the default + rule could be: + + .c.obj : + msc -AL $*.c; + + When MAKE finds a target with no commands, it looks for the first + possible name for which both a rule and a file exist. There may be + several ways to produce a `.obj' file (eg from a C, FORTRAN, or PASCAL + compiler, or from MASM), and the order in which rules are attempted is + specified by the `.SUFFIXES' list. This is a special target with a + list of extensions. For example: + + .SUFFIXES: .exe .obj .c .asm .for + + If MAKE was trying to make a TEST.OBJ file using a default rule, it + would first look for a `.c.obj' rule (since `.c' follows `.obj' in the + .SUFFIXES list). If it found a `.c.obj' rule, it would check for the + file TEST.C. If the file didn't exist, MAKE would look for a + `.asm.obj' rule (and TEST.ASM file), and finally a `.for.obj' rule + (and TEST.FOR file). + + Assuming MAKE.INI contained the .c.obj rule and .SUFFIXES as defined + above, our previous example could be written more succinctly as: + + OBJS = main.obj sub.obj + + test.exe : $(OBJS) + link $(OBJS), test; + + $(OBJS) : incl.h + + Because of the default rules, MAIN.OBJ and SUB.OBJ implicitly depend + on files MAIN.C and SUB.C, respectively, as well as explicitly + depending on INCL.H. + + Suffixes accumulate, so if the makefile had the line: + + .SUFFIXES : .obj .pas + + + + + + + + NDMAKE v3.8 page 6 + + + + + + the suffix list would look like: .obj .pas .exe .obj .c .asm .for + (the ".obj .pas" from .SUFFIXES of the makefile and the ".exe .obj .c + .asm .for" of make.ini). + + A .SUFFIXES line with no suffixes clears the list of suffixes. + + + 4) "dot commands" + ----------------- + + Besides the special target `.SUFFIXES' mentioned above, there are a + few other special targets that can be put in MAKE description files. + + .IGNORE - Commands returning nonzero status (ie. the exit code or + errorlevel) cause MAKE to terminate unless the special + target `.IGNORE' is in makefile or the command begins with + `-' (hyphen). Equivalent to the `-i' option. + + .SILENT - Commands to be executed are printed when executed unless the + special entry `.SILENT' is in makefile, or the first + character of the command is `@'. Equivalent to the `-s' + option. For example: + + all.exe : 1.obj 2.obj 3.obj + link 1 2 3, tmp.exe; # this line will be echoed + - exepack tmp.exe all.exe # ignore any errors + @erase tmp.exe # don't echo this line + + .PRECIOUS - Break (control-C) and command errors cause the target + being worked on to be deleted unless the target has no + prerequisites (explicit or implicit), or depends on the + special name `.PRECIOUS'. For example: + + nerase.exe : nerase.obj .PRECIOUS + link nerase; + + .BEFORE - Before MAKE starts determining which files need to be made, + it executes all commands associated with this target. Use + this to turn off resident programs which affect the workings + of MAKE (notably, the program DPATH). MAKE uses DOS + interrupt 3Dh to open files, then interrupt 57h to get the + file time. + + .AFTER - After MAKE has finished running, all commands associated + with this target are executed. .BEFORE could turn off + DPATH, .AFTER could turn it back on. For example: + + .BEFORE: + @ echo Hello world! + .AFTER: + @ echo Goodbye cruel world ... + + None of these special targets can be default targets. + + + + + + + + NDMAKE v3.8 page 7 + + + + + + OPTIONS + ------- + + Options are entered on the command line with a `-' preceding them. + You cannot use `/' instead of `-'. Options can be grouped together + after a single `-', so -nd is equivalent to -n -d. + + -d Debug mode. Prints information on macros, dependencies, + SUFFIXES, default rules. Also traces MAKE as it executes. + + -h Print a help screen. + + -i Equivalent to the special entry .IGNORE. Causes commands + that return errors to be ignored. Doing `make -i > errs' + collects all error messages into 1 `errs' file. To stop + running `make -i' you may have to push ^C several times. + + -k Keep going. When a command returns nonzero status, abandon + work on the current target, but continue on branches that do + not depend on the current target. + + -n Display but do not execute the commands needed to update the + targets. Doing `make -n > todo.bat' produces the batch file + TODO.BAT containing the commands to be done. Executing the + batch file will update the targets. This technique can be + used if you don't have enough memory for MAKE to execute the + commands. + + -r Clears .SUFFIXES after MAKE.INI is read. The effect of this + is to prevent MAKE from looking for default rules. + + -s Equivalent to the special entry .SILENT. Commands are not + echoed to the screen before they are executed. + + -t Touch, i.e. set the file time of the out of date targets to + the current time without executing any commands. This will + create target files of length zero if they do not exist. + MAKE `touches' files just like the included TOUCH.EXE + program. The small tutorial included later in this + documentation shows how to use this flag. + + + + + + + + + + + + + + + + + + + + + NDMAKE v3.8 page 8 + + + + + + UNIX features + ------------- + + As with UNIX `make', dependency lines and default rules can have a + command on the same line as the `colon' line, provided the command + follows a semicolon: + + .c.obj:; msc $*.c; + test.exe: $(OBJS); link $(OBJS), test; + @echo done + + # are equivalent to + + .c.obj: + msc $*.c; + test.exe: $(OBJS) + link $(OBJS), test; + @echo done + + If a name appears on a line with a double colon, `::', then the + command sequence following that line is performed only if the name is + out of date with respect to the names to the right of the double + colon, and is not affected by other double colon lines on which that + name may appear. Consider the following makefile: + + 1:: 2 + @echo 2 + 1:: 3 + @echo 3 + + If 2 and 3 are more recent than 1 and you type: + + A> make 1 + + The response will be: + 2 + 3 + + If 1 is more recent than 3, but 2 is newer than 1 the response is: + 2 + + If 1 is more recent than both 2 and 3, the response will be: + Make: `1' is up to date. + + + + + + + + + + + + + + + + + + NDMAKE v3.8 page 9 + + + + + + Additional notes and technical information + ------------------------------------------ + + Lines in a MAKE description file that are too long to fit on one line + can be extended to the next line by putting backslash, `\', followed + be <RETURN> as the last two characters of the line. If you need a `\' + as the last character, put a space or comment character somewhere + after it on that line. The longest single line MAKE can handle is 512 + bytes, but by using `\' to break up the line into shorter pieces, + there is no limit to line length (except available memory). + + Case is unimportant, so `test.obj' and `TEST.OBJ' are the same. + + Everything on a line after the comment character, `#', is ignored. + + The character separating targets and dependents, `:', is also the + character used for the drive separator in MSDOS. To distinguish this + colon from the drive separator, it must be followed by space or tab + (eg: ), semicolon (eg:;), colon (eg::), or nothing (eg:<return>). If + you consistently use a space you will have no problems. + + If you type in a `makefile' from the keyboard (by using the command + `make -f -'), put a ^Z (control-Z) followed by a <RETURN> as the last + two characters. This tells MAKE to stop reading from the keyboard. + + Targets defined in MAKEFILE take precedence over targets of the same + name defined in MAKE.INI. Targets defined in MAKE.INI can never be + default targets. + + MAKE is stupid in that after the commands to update a target have been + executed without error, MAKE assumes the target is up to date. If you + give commands that don't really update a target, MAKE doesn't know. + + When MAKE executes commands, such as `link', it first tries to find a + file called `link.exe' (or `link.com'). If the file is not found, + MAKE loads a second copy of COMMAND.COM and passes it the command + line, in the hope that the command is an internal DOS command. This + is backwards to how COMMAND.COM normally works (first checking + internally, then checking externally). It is done this way for two + reasons: 1) for speed and lower memory requirements, and 2) because + the second copy of COMMAND.COM *does not* return the exit code of the + passed command. I'm using Microsoft C v3.0 and PCDOS 2.0, so if + anyone knows how to get the exit code back, please let me know. + + You can force MAKE to load a second copy of COMMAND.COM by putting a + `+' as the first letter in the command. This will be faster for + executing internal DOS commands. You can put more than one of `-', + `@', and `+' for each command. Ex: + + @+ echo Using + is faster for internal DOS commands + + MAKE always uses a second copy of COMMAND.COM if the command involves + redirection of IO (with `>', `>>', `<', or `|'). If you're using IO + + + + + + + + NDMAKE v3.8 page 10 + + + + + + redirection just to capture MAKE output, use redirection at the level + you invoke MAKE (ie. from DOS, something like `make > make.out'). + + Macros can refer to environment variables. For example, $(PATH) will + be filled in with the DOS environment variable PATH if there is no + PATH=... macro definition in MAKEFILE or MAKE.INI. This is handy for + environment variables like LIB (where your libraries are) and TMP (a + temporary area, usually a RAMdisk). + + Macro definitions can refer to other macros. You could have: + + CFLAGS = -A$(MODEL) -Od # remember, order is not important + MODEL = L + + When it comes time to use CFLAGS, MAKE will expand CFLAGS as + `-AL -Od'. Command line macros have the highest precedence, so: + + A> make MODEL=S test.exe + + results in CFLAGS having the value `-AS -Od'. For command line macros + that contain spaces, enclose entirely the macro in double quotes, " ": + + A> make "CFLAGS=-A$(MODEL) -Zd" MODEL=M test.exe + + MAKE will let you define a recursive macro: + + macro1 = $(macro2) # macro1 = the value of macro2 + macro2 = $(macro1) # macro2 = the value of macro1 + + but signals a `recursive macro' error if it tries to use it. + + + Changes since version 3.0 + ------------------------- + + - The -k flag did not work due to a coding mistake. + + - MAKE did not use the switchar when executing COMMAND.COM, thus not + working properly for people who had switchar = `-'. + + - MAKE did not find MAKE.INI in directories ending with `\'. + + - Targets in MAKE.INI no longer take precedence over targets in + MAKEFILE. Also, only the first target in MAKEFILE is used if MAKE is + invoked with no targets. Previously, if there was no MAKEFILE, the + first target in MAKE.INI got made. + + - Too much memory was being used. MAKE used about 94000 bytes + regardless of the size of the makefile! Now MAKE uses a minimum of + about 40000 bytes. Larger makefiles require more memory. + + - Touch (the -t flag) was made to act exactly like the UNIX version. + Previously it did not create a file if it did not exist. + + + + + + + + NDMAKE v3.8 page 11 + + + + + + - .BEFORE and .AFTER were added to compensate for DPATH. + + - Commas in the LINK command had to be separated by spaces if a + response file was being used. This was a bug. + + - Space can be used instead of tab before command lines. This is + compensation for some editors which do not really do tabs. + + - Fixed an uninitialized pointer problem that manifest itself with + large makefiles. + + - Now "dot commands" and targets defined in MAKE.INI can no longer be + a default targets. + + - .BEFORE and .AFTER have to been done even with `make -n'. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NDMAKE v3.8 page 12 + + + + + + Sample session - what MAKE does when it's running + ------------------------------------------------- + + Assume you have the following MAKE.INI file and MAKEFILE. + + + make.ini + -------- + + .SUFFIXES : .exe .obj .c .for .asm + M = S + CFLAGS = -A$M + + .c.obj:; cl $(CFLAGS) -c $*.c + + .obj.exe:; link $<, $@; + + .c.exe: + cl $(CFLAGS) -c $*.c + link $*.obj, $@; + erase $*.obj + + + makefile + -------- + + OBJS = main.obj sub.obj + + test.exe: $(OBJS) + link $<, $@,,\lib\local; + + $(OBJS): incl.h + + sub.obj: sub.c + cl $(CFLAGS) -Od -c sub.c + + install: test.exe + copy test.exe $(BIN) # BIN comes from the environment + + ---------- + + Assume the following files are in your directory: MAIN.C, SUB.C, + INCL.H. When you type: + + A> make + + MAKE first reads MAKE.INI then MAKEFILE. It sees the first target + TEST.EXE and tries to make it. But first, MAKE must know if the files + that TEST.EXE depends on are up to date. As TEST.EXE depends on + several `.obj' files, and these `.obj' files also have dependents, the + detailed procedure MAKE undergoes looks like this: + + + + + + + + + + NDMAKE v3.8 page 13 + + + + + + -Make TEST.EXE + - there are explicit commands for making TEST.EXE so don't + bother looking for implicit prerequisites. + - TEST.EXE depends on MAIN.OBJ and SUB.OBJ. Make these. + - Make MAIN.OBJ + - Since there are no explicit commands for MAIN.OBJ, check + for implicit prerequisites based on default rules. + - Find rule `.c.obj' and file `main.c'. + - Add MAIN.C to the prerequisites of MAIN.OBJ. + - MAIN.OBJ depends on INCL.H and MAIN.C. Make these. + - Make INCL.H + - Since there are no explicit commands for making + INCL.H, check for implicit prerequisites. + - Since there is no `.h' suffix in .SUFFIXES, there are + no implicit prerequisites. + - There are no explicit prerequisites. + - There are no prerequisites, so assume INCL.H is up to + date. + - Make MAIN.C + - Since there are no explicit commands for making + MAIN.C, check for implicit prerequisites. + - Since there are no `.from_extension.c' rules, there + are no implicit prerequisites. + - There are no explicit prerequisites. + - There are no prerequisites, so assume MAIN.C is up to + date. + - Compare MAIN.OBJ with INCL.H and MAIN.C. Since MAIN.OBJ + doesn't exist, it is out of date with respect to its + prerequisites, so execute the (implicit) command: + + cl -AS -c main.c + + - Assume MAIN.OBJ is up to date. + - Make SUB.OBJ + - There are explicit commands for making SUB.OBJ so don't + bother looking for implicit prerequisites. + - SUB.OBJ depends on INCL.H and SUB.C. Make these. + - Make INCL.H + - MAKE already knows that INCL.H is up to date. + - Make SUB.C + - Since there are no explicit commands to make SUB.C, + check for implicit prerequisites. + - Since there are no `.from_extension.c' rules, there + are no implicit prerequisites. + - There are no explicit prerequisites. + - There are no prerequisites, so assume SUB.C is up to + date. + - Compare SUB.OBJ with INCL.H and SUB.C. Since SUB.OBJ + doesn't exist, it is out of date with respect to its + prerequisites, so execute the (explicit) command: + + cl -AS -Od -c sub.c + + - Assume SUB.OBJ is up to date. + - Compare TEST.EXE with MAIN.OBJ and SUB.OBJ. Since TEST.EXE + + + + + + NDMAKE v3.8 page 14 + + + + + + doesn't exist, execute the command: + + link main.obj sub.obj, test.exe,,\lib\local; + + - Assume TEST.EXE is up to date. + + Note the way $< gets replaced with the files TEST.EXE depends on, and + $@ gets replaced with TEST.EXE. Although in this case we could have + used $(OBJS) for $< and TEST.EXE for $@, when writing default rules we + don't know in advance that we want to link $(OBJS) to make TEST.EXE. + + Assuming no errors occurred, when you now type `make' you will get the + message that TEST.EXE is up to date. If you edit SUB.C and change it, + when you next type `make', MAKE will see that SUB.C is more recent + than SUB.OBJ and recompile SUB.C. MAKE will then see that SUB.OBJ is + more recent than TEST.EXE and relink the files. + + If you type `make install', MAKE will ensure TEST.EXE is up to date, + then copy it to your BIN directory. BIN was assumed to be defined in + your environment. + + + Use of flags -n, -t and -i + -------------------------- + + Now assume you edit INCL.H and make changes that only affect SUB.C + (for example, you change the value of a #define but you don't have to + edit SUB.C). If you were now to type `make', MAKE would compile both + SUB.C and MAIN.C. To have MAKE only recompile SUB.C you do three + things. First, `make -t' to touch (update) all files. You will see + that MAKE touches MAIN.OBJ and SUB.OBJ, then TEST.EXE. Now, `touch + sub.c'. This results in SUB.C being newer than SUB.OBJ. Finally, + `make' again. Now MAKE will compile only SUB.OBJ, then link the + files. + + The process of editing a common include file to change something that + only affects one file occurs often enough that the `make -t' + `touch' + + `make' procedure can save a lot of time. + + If you are changing an include file and also changing some of the `.c' + files, then usually you edit the include file, do `make -t', edit the + `.c' files, then do `make'. + + The `-i' flag is useful for collecting all errors into a single file + without stopping MAKE. This is helpful when you're porting software + and expect a lot of errors or when you make global changes that may + produce a lot of errors (for example, changing a structure definition + in an include file or changing from small to large code models). + + The `-n' flag is used when you just want to see what MAKE will be + doing. This is useful if you've changed several modules, but forget + which ones. `make -n' shows which ones will be compiled. + + + + + + + + + NDMAKE v3.8 page 15 + + + + + + Using MAKE without a makefile + ----------------------------- + + MAKE can be used in a limited fashion without having a makefile. + Assume you have a file called XYZZY.C and using the same MAKE.INI file + described above, you type: + + A> make xyzzy.exe + + MAKE uses its default rules to compile XYZZY.C, link XYZZY.OBJ to form + XYZZY.EXE, then erases XYZZY.OBJ. If several `.exe' files exist in a + directory and you have just finished editing some of their `.c' files, + you could type: + + A> make *.exe + + and update only the `.exe' files that are out of date. By adding more + default rules to MAKE.INI, MAKE could invoke the FORTRAN compiler for + `.for' files or MASM for `.asm' files. In this way, MAKE can do the + right thing for each type of file. You can do `make *.exe' and have + MAKE figure out what to do. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NDMAKE v3.8 page 16 + + + + + + USER-SUPPORTED SOFTWARE NOTIFICATION + ------------------------------------ + + If you like and use this program, I ask you to consider + registering it. The benefits of registration include the first + bugfix/enhanced version free. Registered owners will get a response + to any questions they may have. The lastest version includes VPATH + (a way to look for dependent files in other than the current directory), + LIB support (like the current LINK support) and return codes from + commands executed through COMMAND.COM. Also, there is support for + writing to files so you build your own custom response files, a loop + construct for looping over commands, and enhanced macros. + + The suggested registration fee is $35. Regardless of whether you + register or not, you may freely copy and distribute this program for + noncommercial purposes. The programs, MAKE.EXE and TOUCH.EXE, the + documentation, MAKE.DOC and TOUCH.DOC, and the initialization file, + MAKE.INI, must all be distributed together. If you post this program + to a public bulletin board, please put all the files in an ARChive + called NDMAKE38.ARC + + Commercial use of this program requires you to be registered. + + I hope you enjoy NDMAKE and find it to be a useful addition to + your programming tools. If you have any questions I can be reached by + mail at: + + UUCP: ...ucbvax!ucsfcgl!kneller + ARPANET: kneller@ucsf-cgl.ARPA + BITNET: kneller@ucsfcgl.BITNET + FIDONET: node 125/84 (SCI-Fido 415-655-0667) + + US MAIL: D. G. Kneller + 2 Panoramic Way #204 + Berkeley, CA 94704 + + + ---------- + + UNIX is a registered trademark of Bell Laboratories. + MSDOS is a registered trademark of Microsoft Corp. + + + + + + + + + + + + + + + + + + + + NDMAKE v3.8 page 17 + + + + + + --------------------------------------------------------------------- + + Registration form for NDMAKE v3.8 + + + + Name: _________________________________________________ + + Address: _________________________________________________ + + City, State, Zip: _________________________________________________ + + Country: _________________________________________________ + + + + OPTIONAL: System description (computer, memory, DOS version) + + _____________________________________________________________________ + + _____________________________________________________________________ + + + + COMMENTS: Please feel free to add your thoughts or suggestions! + + _____________________________________________________________________ + + _____________________________________________________________________ + + _____________________________________________________________________ + + _____________________________________________________________________ + + + + Mail to: + + D. G. Kneller + 2 Panoramic Way #204 + Berkeley CA, 94704 + U.S.A + + + + + + + + + + + + + + + + + + diff --git a/ndmake/make.exe b/ndmake/make.exe Binary files differ. diff --git a/ndmake/make.ini b/ndmake/make.ini @@ -0,0 +1,65 @@ +# This is a sample `make.ini' file for NDMAKE v3.8. You will probably want +# to customize it for your system. + + +# The order to search for rules and files is specified by .SUFFIXES +.SUFFIXES : .exe .obj .c .for .asm + +# A few macros. +CFLAGS = -A$(MODEL) +MODEL = S +SETARGV = $(LIB)\$(MODEL)SETARGV +LIBS = +BIN = +LFLAGS = + +# A universally useful target. +clean: + +-erase *.bak + +-erase *.map + +# .BEFORE and .AFTER can be used to turn DPATH off and on. +.BEFORE:; @+echo For help with ndmake, use the command `make -h' +.AFTER:; @+echo All done. + + +# DEFAULT RULES +# To produce a `.obj' file from a `.asm' file using MASM. +.asm.obj:; masm $*.asm; + +# To produce a `.obj' file from a `.c' file using Microsoft C. +.c.obj:; cl $(CFLAGS) -c $*.c + +# To produce a `.obj' file from a `.for' file using Microsoft Fortran. +.for.obj: + for1 $*.for; + pas2 + +# To produce a `.exe' file from an `.obj' file. Note that there is a +# problem because LIBS may be different for linking `.obj' files +# produced by different compilers (C, FORTRAN, PASCAL, etc). To avoid +# this problem you may want to have the C compiler produce `.cbj' files, +# the FORTRAN compiler produce `.fbj' files, etc. Then you could write +# specific rules for `.cbj.exe' and `.fbj.exe' which would use the correct +# libraries. +.obj.exe: + link $*.obj $(SETARGV), $@,, $(LIBS) $(LFLAGS); + +# To produce a `.exe' file from a `.asm' file. +.asm.exe: + masm $*.asm; + link $*.obj, $@,, $(LIBS) + erase $*.obj + +# To produce a `.exe' file from a `.c' file. +.c.exe: + cl $(CFLAGS) -c $*.c + link $*.obj $(SETARGV), $@,, $(LIBS) $(LFLAGS) + erase $*.obj + +# To produce a `.exe' file from a `.for' file. +.for.exe: + for1 $*.for; + pas2 + link $*.obj, $@,, $(LIB)\FORTRAN.LIB + erase $*.obj diff --git a/readme.txt b/readme.txt @@ -0,0 +1,3 @@ +PC HACK 3.61 Source + +The bundled 'ndmake' tool and 'termcap' library split into their own directories. diff --git a/src/alloc.c b/src/alloc.c @@ -0,0 +1,48 @@ +/* alloc.c - version 1.0.2 */ +#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 +*/ +#include <stdio.h> +long * +alloc(n) unsigned n; { +long dummy = ftell(stderr); + if(n) dummy = 0; /* make sure arg is used */ + return(&dummy); +} + +#else + +long * +alloc(lth) +register unsigned lth; +{ + register char *ptr; + extern char *malloc(); + + if(!(ptr = malloc(lth))) + panic("Cannot get %d bytes", lth); + return((long *) ptr); +} + +#ifndef DGK +long * +enlarge(ptr,lth) +register char *ptr; +register unsigned lth; +{ + register char *nptr; + extern char *realloc(); + + if(!(nptr = realloc(ptr,lth))) + panic("Cannot reallocate %d bytes", lth); + return((long *) nptr); +} +#endif + +#endif /* LINT /**/ diff --git a/src/apply.c b/src/apply.c @@ -0,0 +1,445 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* apply.c - version 1.0.3 */ + +#include "hack.h" +#include "edog.h" +#include "mkroom.h" +static struct monst *bchit(); +extern struct obj *addinv(); +extern struct trap *maketrap(); +extern int (*occupation)(); +extern char *occtxt; +extern char quitchars[]; +extern char pl_character[]; + +static use_camera(), use_ice_box(), use_whistle(); +static use_magic_whistle(), use_pick_axe(); + +doapply() { + register struct obj *obj; + register 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); +} + +/* ARGSUSED */ +static +use_camera(obj) /* register */ struct obj *obj; { +register 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, '!')) { + 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){ + register int tmp = dist(mtmp->mx,mtmp->my); + register 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 +in_ice_box(obj) register 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((struct obj *) 0); + } + 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 +ck_ice_box(obj) register struct obj *obj; { + return(obj->o_cnt_id == current_ice_box->o_id); +} + +static +out_ice_box(obj) register struct obj *obj; { +register 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 */ + (void) addinv(obj); +} + +static +use_ice_box(obj) register struct obj *obj; { +register int cnt = 0; +register 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, (char *) 0, 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(ddx,ddy,range,sym) register int ddx,ddy,range; char sym; { + register struct monst *mtmp = (struct monst *) 0; + register 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); +} + +/* ARGSUSED */ +static +use_whistle(obj) struct obj *obj; { +register 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; + } +} + +/* ARGSUSED */ +static +use_magic_whistle(obj) struct obj *obj; { +register 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 +dig() { + register struct rm *lev; + register 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) { + register 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) { + register char *digtxt; + register struct obj *obj; + + lev = &levl[dpx][dpy]; + if(obj = sobj_at(ENORMOUS_ROCK, dpx, dpy)) { + 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(digtxt); /* after mnewsym & prl */ + return(0); + } else { + if(IS_WALL(levl[dpx][dpy].typ)) { + register 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. */ +holetime() { + return( (occupation == dig) ? (250 - dig_effort)/20 : -1); +} + +dighole() +{ + register 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 +use_pick_axe(obj) +struct obj *obj; +{ + char dirsyms[12]; + extern char sdir[]; + register char *dsp = dirsyms, *sdp = sdir; + register struct monst *mtmp; + register struct rm *lev; + register 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) { + (void) 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, (char *) 0)); + } 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."); +#ifdef DGK + set_occupation(dig, "digging", 0); +#else + occupation = dig; + occtxt = "digging"; +#endif + } + } 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."); +#ifdef DGK + set_occupation(dig, "digging", 0); +#else + occupation = dig; + occtxt = "digging"; +#endif + } + return(1); +} diff --git a/src/bones.c b/src/bones.c @@ -0,0 +1,115 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* bones.c - version 1.0.3 */ + +#include "hack.h" +extern char plname[PL_NSIZ]; +extern long somegold(); +extern struct monst *makemon(); +extern struct permonst pm_ghost; + +#ifdef DGK +char bones[FILENAME]; +#else +char bones[] = "bones_xx"; +#endif + +/* save bones and possessions of a deceased adventurer */ +savebones(){ +register fd; +register struct obj *otmp; +register struct trap *ttmp; +register struct monst *mtmp; + if(dlevel <= 0 || dlevel > MAXLEVEL) return; + if(!rn2(1 + dlevel/2)) return; /* not so many ghosts on low levels */ +#ifdef DGK + name_file(bones, dlevel); +#else + bones[6] = '0' + (dlevel/10); + bones[7] = '0' + (dlevel%10); +#endif + if((fd = open(bones,0)) >= 0){ + (void) 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; + (void) 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 */ + } + } +#ifdef DGK + fd = open(bones, O_WRONLY | O_BINARY | O_CREAT, FMASK); +#else + fd = creat(bones, FMASK); +#endif + if(fd < 0) + return; +#ifdef DGK + savelev(fd,dlevel, COUNT | WRITE); +#else + savelev(fd,dlevel); +#endif + (void) close(fd); +} + +getbones(){ +register fd,x,y; + if(rn2(3)) return(0); /* only once in three times do we find bones */ +#ifdef DGK + name_file(bones, dlevel); +#else + bones[6] = '0' + (dlevel/10); + bones[7] = '0' + (dlevel%10); +#endif + if((fd = open(bones, 0)) < 0) return(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; + (void) close(fd); +#ifdef WIZARD + if(!wizard) +#endif + if(unlink(bones) < 0){ + pline("Cannot unlink %s .", bones); + return(0); + } + return(1); +} diff --git a/src/cmd.c b/src/cmd.c @@ -0,0 +1,390 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* cmd.c - version 1.0.3 */ + +#include "hack.h" + +int doredraw(),doredotopl(),dodrop(),dodrink(),doread(),dosearch(),dopickup(), +doversion(),doweararm(),dowearring(),doremarm(),doremring(),dopay(),doapply(), +dosave(),dowield(),ddoinv(),dozap(),ddocall(),dowhatis(),doengrave(),dotele(), +dohelp(),doeat(),doddrop(),do_mname(),doidtrap(),doprwep(),doprarm(), +doprring(),doprgold(),dodiscovered(),dotypeinv(),dolook(),doset(), +doup(), dodown(), done1(), donull(), dothrow(), doextcmd(), dodip(), dopray(); +#ifdef SHELL +int dosh(); +#endif +#ifdef SUSPEND +int dosuspend(); +#endif + +# ifdef DGK +extern int (*occupation)(); +extern int occtime; +extern char *occtxt; /* defined when occupation != NULL */ +int dotogglepickup(), doMSCversion(); +# ifdef DEBUG +int dodebug(); +# endif +static int (*timed_occ_fn)(); + +/* Count down by decrementing multi */ +timed_occupation() { + (*timed_occ_fn)(); + if (multi > 0) + multi--; + return (multi > 0); +} + +/* If a time is given, use it to timeout this function, otherwise the + * function times out by its own means. + */ +void +set_occupation(fn, txt, time) +int (*fn)(); +char *txt; +{ + if (time) { + occupation = timed_occupation; + timed_occ_fn = fn; + } else + occupation = fn; + occtxt = txt; + occtime = 0; +} + +# ifdef REDO +/* Provide a means to redo the last command. The flag `in_doagain' is set + * to true while redoing the command. This flag is tested in commands that + * require additional input (like `throw' which requires a thing and a + * direction), and the input prompt is not shown. Also, while in_doagain is + * TRUE, no keystrokes can be saved into the saveq. + */ +#define BSIZE 20 +static char pushq[BSIZE], saveq[BSIZE]; +static int phead, ptail, shead, stail; +int in_doagain; + +char +popch() { + /* If occupied, return 0, letting tgetch know a character should + * be read from the keyboard. If the character read is not the + * ABORT character (as checked in main.c), that character will be + * pushed back on the pushq. + */ + if (occupation) + return 0; + if (in_doagain) + return ((shead != stail) ? saveq[stail++] : 0); + else + return ((phead != ptail) ? pushq[ptail++] : 0); +} + +/* A ch == 0 resets the pushq */ +void +pushch(ch) +char ch; +{ + if (!ch) + phead = ptail = 0; + if (phead < BSIZE) + pushq[phead++] = ch; +} + +/* A ch == 0 resets the saveq. Only save keystrokes when not + * replaying a previous command. + */ +void +savech(ch) +char ch; +{ + if (!in_doagain) { + if (!ch) + phead = ptail = shead = stail = 0; + else if (shead < BSIZE) + saveq[shead++] = ch; + } +} +# endif /* REDO */ +#endif /* DGK */ + +struct func_tab { + char f_char; + int (*f_funct)(); + char *f_text; +}; + +struct ext_func_tab { + char *ef_txt; + int (*ef_funct)(); +}; + +struct func_tab cmdlist[]={ + {'\020', doredotopl}, + {'\022', doredraw}, + {'\024', dotele}, +#ifdef SUSPEND + {'\032', dosuspend}, +#endif + {'A', doapply}, +/* 'b', 'B' : go sw */ + {'c', ddocall}, + {'C', do_mname}, + {'d', dodrop}, + {'D', doddrop}, + {'e', doeat}, + {'E', doengrave}, +/* Soon to be + {'f', dofight, "fighting"}, + {'F', doFight, "fighting"}, + */ +/* 'g', 'G' : multiple go */ +/* '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', done1}, + {'r', doread}, + {'R', doremring}, + {'s', dosearch, "searching"}, + {'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 + {'.', donull, "waiting"}, + {' ', donull, "waiting"}, + {',', dopickup}, + {':', dolook}, + {'^', doidtrap}, + {'\\', dodiscovered}, /* Robert Viduya */ +#ifdef DGK + {'@', dotogglepickup}, + {'V', doMSCversion}, +# ifdef DEBUG + {'\004', dodebug}, /* generic debug function */ +# endif +#endif + {WEAPON_SYM, doprwep}, + {ARMOR_SYM, doprarm}, + {RING_SYM, doprring}, + {'$', doprgold}, + {'#', doextcmd}, + {0,0} +}; + +struct ext_func_tab extcmdlist[] = { + "dip", dodip, + "pray", dopray, + (char *) 0, donull +}; + +extern char *parse(), lowc(), unctrl(), quitchars[]; + +rhack(cmd) +register char *cmd; +{ + register struct func_tab *tlist = cmdlist; + boolean firsttime = FALSE; + register res; + + if(!cmd) { + firsttime = TRUE; + flags.nopick = 0; + cmd = parse(); + } +#ifdef DGK +# ifdef REDO + if (*cmd == DOAGAIN && !in_doagain && saveq[0]) { + in_doagain = TRUE; + stail = 0; + rhack((char *) 0); /* read and execute command */ + in_doagain = FALSE; + return; + } +# endif + + /* Special case of *cmd == ' ' handled better below */ + if(!*cmd || *cmd == 0377) { +#else + if(!*cmd || *cmd == 0377 || (flags.no_rest_on_space && *cmd == ' ')){ +#endif + bell(); + flags.move = 0; + return 0; /* 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; + domove(); + return; + } + if((*cmd == 'g' && movecmd(cmd[1])) || movecmd(unctrl(*cmd))) { + flags.run = 2; + goto rush; + } + if(*cmd == 'G' && 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; + } + while(tlist->f_char) { + if(*cmd == tlist->f_char){ +#ifdef DGK + /* Special case of *cmd == ' ' handled here */ + if (*cmd == ' ' && flags.no_rest_on_space) + break; + + /* Now control-A can stop lengthy commands */ + if (tlist->f_text && !occupation && multi) + set_occupation(tlist->f_funct, tlist->f_text, + multi); +#endif + res = (*(tlist->f_funct))(); + if(!res) { + flags.move = 0; + multi = 0; + } + return; + } + tlist++; + } + { char expcmd[10]; + register char *cp = expcmd; + while(*cmd && cp-expcmd < 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; +} + +doextcmd() /* here after # - now read a full-word command */ +{ + char buf[BUFSZ]; + register 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); +} + +char +lowc(sym) +char sym; +{ + return( (sym >= 'A' && sym <= 'Z') ? sym+'a'-'A' : sym ); +} + +char +unctrl(sym) +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 }; + +movecmd(sym) /* also sets u.dz, but returns false for <> */ +char sym; +{ + register char *dp; + + u.dz = 0; + if(!(dp = index(sdir, sym))) return(0); + u.dx = xdir[dp-sdir]; + u.dy = ydir[dp-sdir]; + u.dz = zdir[dp-sdir]; + return(!u.dz); +} + +getdir(s) +boolean s; +{ + char dirsym; + +#ifdef REDO + if (!in_doagain) +#endif + if(s) pline("In what direction?"); + dirsym = readchar(); +#ifdef REDO + savech(dirsym); +#endif + if(!movecmd(dirsym) && !u.dz) { + if(!index(quitchars, dirsym)) + pline("What a strange direction!"); + return(0); + } + if(Confusion && !u.dz) + confdir(); + return(1); +} + +confdir() +{ + register x = rn2(8); + u.dx = xdir[x]; + u.dy = ydir[x]; +} + +isok(x,y) register x,y; { + /* x corresponds to curx, so x==1 is the first column. Ach. %% */ + return(x >= 1 && x <= COLNO-1 && y >= 0 && y <= ROWNO-1); +} diff --git a/src/config.h b/src/config.h @@ -0,0 +1,170 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* config.h - version 1.0.3 */ + +#ifndef CONFIG /* make sure the compiler doesnt see the typedefs twice */ + +#define CONFIG +#define CHDIR /* delete if no chdir() available */ + +/* + * Some include files are in a different place under SYSV + * BSD SYSV + * <strings.h> <string.h> + * <sys/wait.h> <wait.h> + * <sys/time.h> <time.h> + * <sgtty.h> <termio.h> + * Some routines are called differently + * index strchr + * rindex strrchr + * Also, the code for suspend and various ioctls is only given for BSD4.2 + * (I do not have access to a SYSV system.) + */ +/* #define BSD /* define for BSD */ +/* #define SYSV /* define for System V */ +/* #define MSDOS /* define for MS-DOS (actually defined by compiler) */ + +/* #define UNIX /* delete if no fork(), exec() 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 */ +#ifdef MSDOS +# define NOWAITINCLUDE +#endif + + +/* #define WIZARD "kneller" /* the person allowed to use the -D option */ +#define RECORD "record"/* the file containing the list of topscorers */ +#define HELP "help" /* the file containing a description of the commands */ +#define SHELP "hh" /* abbreviated form of the same */ +#define FMASK 0660 /* file creation mask */ + +#ifdef UNIX +#define HLOCK "perm" /* an empty file used for locking purposes */ +#define LLOCK "safelock" /* link to previous */ +/* + * 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" + */ + +/* #define SHELL /* do not delete the '!' command */ + +#ifdef BSD +#define SUSPEND /* let ^Z suspend the game */ +#endif /* BSD /**/ +#endif /* UNIX /**/ + +#ifdef CHDIR +/* + * If you define HACKDIR, then this will be the default playground; + * otherwise it will be the current directory. + */ +#define HACKDIR "/hack" + +/* + * 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 6 + */ +#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 char 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 xchar 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])) + +# ifdef MSDOS +# include <fcntl.h> +# define exit msexit /* do chdir first */ +# ifdef getchar +# undef getchar +# endif /* getchar /**/ +# define getchar tgetch +# define DGK /* enhancements by dgk */ +# ifdef MSC30 +# define REGBUG /* A bug in register usage under 3.0 */ +# endif + +# ifdef DGK +# include "msdos.h" /* contains necessary externs for msdos.c */ +# define REDO /* support for redoing last command */ +# define SHELL /* via exec of COMMAND.COM */ +# define PATHLEN 64 /* maximum pathlength */ +# define FILENAME 80 /* maximum filename length (conservative) */ +# define FROMPERM 1 /* for ramdisk use */ +# define TOPERM 2 /* for ramdisk use */ +# define glo(x) name_file(lock, x) /* name_file used for bones */ +# define IS_CORNER(x) ((x) == symbol.tlcorn || (x) == symbol.trcorn\ + || (x) == symbol.blcorn || (x)==symbol.brcorn) + /* screen symbols for using character graphics. + */ + struct symbols { + unsigned char vwall, hwall, tlcorn, trcorn, blcorn, brcorn; + unsigned char door, room, corr; + }; + extern struct symbols symbol; + extern int vminor, vmajor; + extern char *configfile; + + /* Fake indices into the mons[] array for writing save files. These + * monsters appear outside of this array but we have to save them + * without pointers. + */ +# define INDEX_LITTLEDOG -1 +# define INDEX_DOG -2 +# define INDEX_LARGEDOG -3 +# define INDEX_HELLHOUND -4 +# endif /* DGK /**/ +# endif /* MSDOS /**/ + +#endif /* CONFIG /**/ diff --git a/src/decl.c b/src/decl.c @@ -0,0 +1,58 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* decl.c - version 1.0.3 */ + +#include "hack.h" +char nul[40]; /* contains zeros */ +char plname[PL_NSIZ]; /* player name */ +#ifdef DGK +char hackdir[PATHLEN]; /* where rumors, help, record are */ +char levels[PATHLEN]; /* where levels are */ +char lock[FILENAME]; /* pathname of level files */ +char permbones[PATHLEN]; /* where permanent copy of bones go */ +int ramdisk = FALSE; /* whether to copy bones to levels or not */ +struct symbols symbol = {'|', '-', '-', '-', '-', '-', '+', '.', '#'}; +int saveprompt = TRUE; +char *alllevels = "levels.*"; +char *allbones = "bones.*"; +char *configfile = "HACK.CNF"; /* read by read_config_file() */ +int vmajor = VER / 100; /* Major and minor version numbers */ +int vminor = VER % 100; +#else +char lock[PL_NSIZ+4] = "1lock"; /* long enough for login name .99 */ +#endif + +boolean in_mklev, restoring; + +struct rm levl[COLNO][ROWNO]; /* level map */ +#ifndef QUEST +#include "mkroom.h" +struct mkroom rooms[MAXNROFROOMS+1]; +coord doors[DOORMAX]; +#endif /* QUEST /**/ +struct monst *fmon = 0; +struct trap *ftrap = 0; +struct gold *fgold = 0; +struct obj *fobj = 0, *fcobj = 0, *invent = 0, *uwep = 0, *uarm = 0, + *uarm2 = 0, *uarmh = 0, *uarms = 0, *uarmg = 0, *uright = 0, + *uleft = 0, *uchain = 0, *uball = 0; +struct flag flags; +struct you u; +struct monst youmonst; /* dummy; used as return value for boomhit */ + +xchar dlevel = 1; +xchar xupstair, yupstair, xdnstair, ydnstair; +char *save_cm = 0, *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/src/do.c b/src/do.c @@ -0,0 +1,524 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* do.c - version 1.0.3 */ + +/* Contains code for 'd', 'D' (drop), '>', '<' (up, down) and 't' (throw) */ + +#include "hack.h" + +extern struct obj *splitobj(), *addinv(); +extern boolean hmon(); +extern struct monst youmonst; +extern char *Doname(); +extern char *nomovemsg; + +static +drop(obj) register struct obj *obj; { + if(!obj) return(0); + if(obj->olet == '$') { /* pseudo object */ + register 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((char *) 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((struct obj *) 0); + } + pline("You dropped %s.", doname(obj)); + dropx(obj); + return(1); +} + +dodrop() { + return(drop(getobj("0$#", "drop"))); +} + +/* Called in several places - should not produce texts */ +dropx(obj) +register struct obj *obj; +{ + freeinv(obj); + dropy(obj); +} + +dropy(obj) +register 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 */ +doddrop() { + return(ggetobj("drop", drop, 0)); +} + +dodown() +{ + 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); +} + +doup() +{ + 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); +} + +goto_level(newlevel, at_stairs) +register int newlevel; +register boolean at_stairs; +{ + register fd; + register boolean up = (newlevel < dlevel); + + if(newlevel <= 0) done("escaped"); /* in fact < 0 is impossible */ + if(newlevel > MAXLEVEL) newlevel = MAXLEVEL; /* strange ... */ + if(newlevel == dlevel) return; /* this can happen */ + + glo(dlevel); +#ifdef DGK + /* Use O_TRUNC to force the file to be shortened if it already + * exists and is currently longer. + */ + fd = open(lock, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, FMASK); +#else + fd = creat(lock, FMASK); +#endif + 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. + */ +#ifdef DGK + pline("Cannot create level file '%s'.", lock); +#else + pline("A mysterious force prevents you from going %s.", + up ? "up" : "down"); +#endif + return; + } + +#ifdef DGK + if (!savelev(fd, dlevel, COUNT)) { + (void) close(fd); + (void) unlink(lock); + pline("HACK is out of disk space for making levels!"); + pline("You can save, quit, or continue playing."); + return; + } +#endif + + 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 */ + (void) inshop(); /* probably was a trapdoor */ + +#ifdef DGK + savelev(fd,dlevel, WRITE); +#else + savelev(fd,dlevel); +#endif + (void) close(fd); + + dlevel = newlevel; + if(maxdlevel < dlevel) + maxdlevel = dlevel; + glo(dlevel); + + /* If the level has no where yet, it hasn't been made + */ + if(!fileinfo[dlevel].where) + mklev(); + else { + extern int hackpid; + + /* If not currently accessible, swap it in. + */ + if (fileinfo[dlevel].where != ACTIVE) + swapin_file(dlevel); +#ifdef DGK + if((fd = open(lock, O_RDONLY | O_BINARY)) < 0) { +#else + if((fd = open(lock,0)) < 0) { +#endif + pline("Cannot open %s .", lock); + pline("Probably someone removed it."); + done("tricked"); + } + getlev(fd, hackpid, dlevel); + (void) 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"); + } + } + { register 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"); + } + (void) inshop(); + initrack(); + + losedogs(); + { register struct monst *mtmp; + if(mtmp = m_at(u.ux, u.uy)) mnexto(mtmp); /* riv05!a3 */ + } + flags.nscrinh = 0; + setsee(); + seeobjs(); /* make old cadavers disappear - riv05!a3 */ + docrt(); + pickup(1); + read_engr_at(u.ux,u.uy); +} + +donull() { + return(1); /* Do nothing, but let other things happen */ +} + +dopray() { + nomovemsg = "You finished your prayer."; + nomul(-3); + return(1); +} + +struct monst *bhit(), *boomhit(); +dothrow() +{ + register struct obj *obj; + register struct monst *mon; + register 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((struct obj *) 0); + } + else if(obj->quan > 1) + (void) 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(obj)); + } else if(obj->otyp == EGG) { + pline("\"Splash!\""); + obfree(obj, Null(obj)); + } else if(obj->olet == POTION_SYM) { + pline("The flask breaks, and you smell a peculiar odor ..."); + potionbreathe(obj); + obfree(obj, Null(obj)); + } else { + dropy(obj); + } + } + return(1); + } else if(obj->otyp == BOOMERANG) { + mon = boomhit(u.dx, u.dy); + if(mon == &youmonst) { /* the thing was caught */ + (void) 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, + (int (*)()) 0, (int (*)()) 0, obj); + } + if(mon) { + /* awake monster if sleeping */ + wakeup(mon); + + if(obj->olet == WEAPON_SYM) { + tmp = -1+u.ulevel+mon->data->ac+abon(); +#ifdef DGK + if(obj->otyp < DART) { /* a USENET bugfix */ +#else + if(obj->otyp < ROCK) { +#endif + 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, (struct obj *) 0); + 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 { + register 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(); + (void) 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(obj, num) register struct obj *obj; register int num; { +register 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); +} + +more_experienced(exp,rexp) +register int exp, rexp; +{ + extern char pl_character[]; + + 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; +} + +set_wounded_legs(side, timex) +register long side; +register int timex; +{ + if(!Wounded_legs || (Wounded_legs & TIMEOUT)) + Wounded_legs |= side + timex; + else + Wounded_legs |= side; +} + +heal_legs() +{ + 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/src/do_name.c b/src/do_name.c @@ -0,0 +1,320 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* do_name.c - version 1.0.3 */ + +#include <stdio.h> +#include "hack.h" +extern char plname[]; + +coord +getpos(force,goal) int force; char *goal; { +register cx,cy,i,c; +extern char sdir[]; /* defined in hack.c */ +extern schar xdir[], ydir[]; /* idem */ +extern char *visctrl(); /* see below */ +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); +} + +do_mname(){ +char buf[BUFSZ]; +coord cc; +register int cx,cy,lth,i; +register struct monst *mtmp, *mtmp2; +extern char *lmonnam(); + cc = getpos(0, "the monster you want to name"); + cx = cc.x; + cy = cc.y; + if(cx < 0) return(0); +#ifdef DGK + if (cx == u.ux && cy == u.uy) { + pline("This ugly monster is called %s and cannot be renamed.", + plname); + return 1; + } + if (!cansee(cx, cy) || !(mtmp = m_at(cx, cy)) || mtmp->mimic) { + pline("I see no monster there."); + return 1; + } +#else + 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); + } +#endif + 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; i<mtmp->mxlth; i++) + ((char *) mtmp2->mextra)[i] = ((char *) mtmp->mextra)[i]; + mtmp2->mnamelth = lth; + (void) strcpy(NAME(mtmp2), buf); + replmon(mtmp,mtmp2); + return(0); +} + +/* + * 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. + */ +do_oname(obj) register struct obj *obj; { +register struct obj *otmp, *otmp2; +register 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; + (void) strcpy(ONAME(otmp2), buf); + + setworn((struct obj *) 0, 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((char *) obj); /* let us hope nobody else saved a pointer */ +} + +ddocall() +{ + register struct obj *obj; + char ch; + +#ifdef REDO + if (!in_doagain) +#endif + pline("Do you want to name an individual object? [ny] "); + switch(ch = readchar()) { + case '\033': + break; + case 'y': +#ifdef REDO + savech(ch); +#endif + obj = getobj("#", "name"); + if(obj) do_oname(obj); + break; + default: +#ifdef REDO + savech(ch); +#endif + obj = getobj("?!=/", "call"); + if(obj) docall(obj); + } + return(0); +} + +docall(obj) +register struct obj *obj; +{ + char buf[BUFSZ]; + struct obj otemp; + register char **str1; + extern char *xname(); + register char *str; + + otemp = *obj; + otemp.quan = 1; + otemp.onamelth = 0; + str = xname(&otemp); + pline("Call %s %s: ", index(vowels,*str) ? "an" : "a", str); + getlin(buf); + clrlin(); + if(!*buf || *buf == '\033') + return; + str = newstring(strlen(buf)+1); + (void) strcpy(str,buf); + str1 = &(objects[obj->otyp].oc_uname); + if(*str1) free(*str1); + *str1 = str; +} + +char *ghostnames[] = { /* these names should have length < PL_NSIZ */ +#ifdef DGK + /* Capitalize the names for asthetics -dgk + */ + "Adri", "Andries", "Andreas", "Bert", "David", "Dirk", "Emile", + "Frans", "Fred", "Greg", "Hether", "Jay", "John", "Jon", "Kay", + "Kenny", "Maud", "Michiel", "Mike", "Peter", "Robert", "Ron", + "Tom", "Wilmar", "Nick Danger" +#else + "adri", "andries", "andreas", "bert", "david", "dirk", "emile", + "frans", "fred", "greg", "hether", "jay", "john", "jon", "kay", + "kenny", "maud", "michiel", "mike", "peter", "robert", "ron", + "tom", "wilmar" +#endif +}; + +char * +xmonnam(mtmp, vb) register struct monst *mtmp; int vb; { +static char buf[BUFSZ]; /* %% */ +extern char *shkname(); + if(mtmp->mnamelth && !vb) { + (void) strcpy(buf, NAME(mtmp)); + return(buf); + } + switch(mtmp->data->mlet) { + case ' ': + { register char *gn = (char *) mtmp->mextra; + if(!*gn) { /* might also look in scorefile */ + gn = ghostnames[rn2(SIZE(ghostnames))]; + if(!rn2(2)) (void) + strcpy((char *) mtmp->mextra, !rn2(5) ? plname : gn); + } + (void) sprintf(buf, "%s's ghost", gn); + } + break; + case '@': + if(mtmp->isshk) { + (void) strcpy(buf, shkname(mtmp)); + break; + } + /* fall into next case */ + default: + (void) sprintf(buf, "the %s%s", + mtmp->minvis ? "invisible " : "", + mtmp->data->mname); + } + if(vb && mtmp->mnamelth) { + (void) strcat(buf, " called "); + (void) strcat(buf, NAME(mtmp)); + } + return(buf); +} + +char * +lmonnam(mtmp) register struct monst *mtmp; { + return(xmonnam(mtmp, 1)); +} + +char * +monnam(mtmp) register struct monst *mtmp; { + return(xmonnam(mtmp, 0)); +} + +char * +Monnam(mtmp) register struct monst *mtmp; { +register char *bp = monnam(mtmp); + if('a' <= *bp && *bp <= 'z') *bp += ('A' - 'a'); + return(bp); +} + +char * +amonnam(mtmp,adj) +register struct monst *mtmp; +register char *adj; +{ + register char *bp = monnam(mtmp); + static char buf[BUFSZ]; /* %% */ + + if(!strncmp(bp, "the ", 4)) bp += 4; + (void) sprintf(buf, "the %s %s", adj, bp); + return(buf); +} + +char * +Amonnam(mtmp, adj) +register struct monst *mtmp; +register char *adj; +{ + register char *bp = amonnam(mtmp,adj); + + *bp = 'T'; + return(bp); +} + +char * +Xmonnam(mtmp) register struct monst *mtmp; { +register char *bp = Monnam(mtmp); + if(!strncmp(bp, "The ", 4)) { + bp += 2; + *bp = 'A'; + } + return(bp); +} + +char * +visctrl(c) +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/src/do_wear.c b/src/do_wear.c @@ -0,0 +1,344 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* do_wear.c - version 1.0.3 */ + +#include <stdio.h> +#include "hack.h" +extern char *nomovemsg; +extern char quitchars[]; +extern char *Doname(); + +off_msg(otmp) register struct obj *otmp; { + pline("You were wearing %s.", doname(otmp)); +} + +doremarm() { + register 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); + } + (void) armoroff(otmp); + return(1); +} + +doremring() { + if(!uleft && !uright){ + pline("Not wearing any ring."); + return(0); + } + if(!uleft) + return(dorr(uright)); + if(!uright) + return(dorr(uleft)); + if(uleft && uright) while(1) { + char answer; + + pline("What ring, Right or Left? [ rl?]"); + if(index(quitchars, (answer = readchar()))) + return(0); + switch(answer) { + case 'l': + case 'L': + return(dorr(uleft)); + case 'r': + case 'R': + return(dorr(uright)); + case '?': + (void) doprring(); + /* might look at morc here %% */ + } + } + /* NOTREACHED */ +#ifdef lint + return(0); +#endif /* lint /**/ +} + +dorr(otmp) register struct obj *otmp; { + if(cursed(otmp)) return(0); + ringoff(otmp); + off_msg(otmp); + return(1); +} + +cursed(otmp) register struct obj *otmp; { + if(otmp->cursed){ + pline("You can't. It appears to be cursed."); + return(1); + } + return(0); +} + +armoroff(otmp) register struct obj *otmp; { +register int delay = -objects[otmp->otyp].oc_delay; + if(cursed(otmp)) return(0); + setworn((struct obj *) 0, 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); +} + +doweararm() { + register struct obj *otmp; + register int delay; + register 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((struct obj *) 0); + delay = -objects[otmp->otyp].oc_delay; + if(delay){ + nomul(delay); + nomovemsg = "You finished your dressing manoeuvre."; + } + otmp->known = 1; + return(1); +} + +dowearring() { + register 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(index(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((struct obj *) 0); + 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_CHANG: + 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); +} + +ringoff(obj) +register struct obj *obj; +{ +register long mask; + mask = obj->owornmask & W_RING; + setworn((struct obj *) 0, obj->owornmask); + if(!(u.uprops[PROP(obj->otyp)].p_flgs & mask)) + impossible("Strange... I didnt 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; +#ifdef DGK + case RIN_PROTECTION_FROM_SHAPE_CHANG: + /* If you're no longer protected, let the chameleons + * change shape again -dgk + */ + restartcham(); + break; +#endif /* DGK /**/ + } +} + +find_ac(){ +register 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; + } +} + +glibr(){ +register 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(obj)){ + ringoff(uleft); + dropx(otmp); + } + if((otmp = uright) != Null(obj)){ + ringoff(uright); + dropx(otmp); + } + } + if((otmp = uwep) != Null(obj)){ + /* Note: at present also cursed weapons fall */ + setuwep((struct obj *) 0); + dropx(otmp); + pline("Your weapon %sslips from your hands.", + xfl ? "also " : ""); + } +} + +struct obj * +some_armor(){ +register 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); +} + +corrode_armor(){ +register 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/src/dog.c b/src/dog.c @@ -0,0 +1,426 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* dog.c - version 1.0.3 */ + +#include "hack.h" +#include "mfndpos.h" +extern struct monst *makemon(); +#include "edog.h" +#include "mkroom.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) }; + + +makedog(){ +register struct monst *mtmp = makemon(&li_dog,u.ux,u.uy); + if(!mtmp) return; /* dogs were genocided */ + initedog(mtmp); +} + +initedog(mtmp) register 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 = 0; +struct monst *fallen_down = 0; /* monsters that fell through a trapdoor */ + /* they will appear on the next level @ goes to, even if he goes up! */ + +losedogs(){ +register 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); + } +} + +keepdogs(){ +register struct monst *mtmp; + for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) + if(dist(mtmp->mx,mtmp->my) < 3 && follower(mtmp) + && !mtmp->msleep && !mtmp->mfroz) { +#ifdef DGK + /* Bug "fix" for worm changing levels collapsing dungeon + */ + if (mtmp->data->mlet == 'w') { + if (canseemon(mtmp) || (Blind && Telepat)) + pline("The worm can't fit in stairwell!"); + continue; + } +#endif + relmon(mtmp); + mtmp->nmon = mydogs; + mydogs = mtmp; + unpmon(mtmp); + keepdogs(); /* we destroyed the link, so use recursion */ + return; /* (admittedly somewhat primitive) */ + } +} + +fall_down(mtmp) register 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 +dogfood(obj) register 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) */ +dog_move(mtmp, after) register struct monst *mtmp; { +#ifdef REGBUG +int nx,ny,omx,omy,appr,nearer,j; +#else +register int nx,ny,omx,omy,appr,nearer,j; +#endif /* REGBUG */ +int udist,chi,i,whappr; +register struct monst *mtmp2; +register struct permonst *mdat = mtmp->data; +register 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) return(0); /* dog is still eating */ + 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) < 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(!index("0_", obj->olet)){ + if((otyp = dogfood(obj)) <= CADAVER){ + nix = omx; + niy = omy; + goto eatobj; + } + if(obj->owt < 10*mtmp->data->mlevel) + if(rn2(20) < 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 */ +#ifdef LINT + gx = gy = 0; /* suppress 'used before set' message */ +#endif /* LINT /**/ + 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 && 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 appr = 1; /* gtyp != UNDEF */ + if(mtmp->mconf) appr = 0; + + if(gx == u.ux && gy == u.uy && (dogroom != uroom || dogroom < 0)){ + extern coord *gettrack(); + register 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) return(0); /* hit only once each move */ + + 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){ + (void) 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 */ +inroom(x,y) xchar x,y; { +#ifndef QUEST + register 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 */ +} + +tamedog(mtmp, obj) +register struct monst *mtmp; +register struct obj *obj; +{ + register 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 || index(" &@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, (struct obj *) 0); + } + mtmp2 = newmonst(sizeof(struct edog) + mtmp->mnamelth); + *mtmp2 = *mtmp; + mtmp2->mxlth = sizeof(struct edog); + if(mtmp->mnamelth) (void) strcpy(NAME(mtmp2), NAME(mtmp)); + initedog(mtmp2); + replmon(mtmp,mtmp2); + return(1); +} diff --git a/src/eat.c b/src/eat.c @@ -0,0 +1,496 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* eat.c - version 1.0.3 */ + +#include "hack.h" +char POISONOUS[] = "ADKSVabhks"; +extern char *nomovemsg; +extern int (*afternmv)(); +extern int (*occupation)(); +extern char *occtxt; +extern struct obj *splitobj(), *addinv(); + +/* 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 + +char *hu_stat[] = { + "Satiated", + " ", + "Hungry ", + "Weak ", + "Fainting", + "Fainted ", + "Starved " +}; + +init_uhunger(){ + u.uhunger = 900; + u.uhs = NOT_HUNGRY; +} + +#define TTSZ SIZE(tintxts) +struct { 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; + +opentin(){ + register 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(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); +} + +Meatdone(){ + u.usym = '@'; + prme(); +} + +doeat(){ + register struct obj *otmp; + register struct objclass *ftmp; + register 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) + (void) splitobj(otmp, 1); + freeobj(otmp); + addtobill(otmp); + otmp = addinv(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, (char *) 0)); + } 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) { + register struct obj *obj; + extern struct obj *splitobj(); + + 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; +#ifdef DGK + set_occupation(opentin, "opening the tin", 0); +#else + occupation = opentin; + occtxt = "opening the tin"; +#endif + return(1); + } + ftmp = &objects[otmp->otyp]; + multi = -ftmp->oc_delay; + if(otmp->otyp >= CORPSE && eatcorpse(otmp)) goto eatx; +#ifdef DGK + if(!rn2(7) && otmp->otyp != FORTUNE_COOKIE && otmp->otyp != DEAD_LIZARD) { +#else + if(!rn2(7) && otmp->otyp != FORTUNE_COOKIE) { +#endif /* DGK /**/ + 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!"); +#ifdef DGK + /* Have lesshungry() report when you're nearly full so all eating + * warns when you're about to choke. + */ + lesshungry(ftmp->nutrition); +#else + 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."; +#endif /* DGK /**/ + 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); +#ifdef DGK + /* Relief from cockatrices -dgk */ + if (otmp->otyp == DEAD_LIZARD) { + if (Stoned) { + Stoned = 0; + pline("You feel more limber!"); + } + if (Confusion > 2) + Confusion = 2; + } +#else + if(otmp->otyp == DEAD_LIZARD && (Confusion > 2)) + Confusion = 2; +#endif /* DGK /**/ + 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]; + (void) sprintf(msgbuf, "You finished eating the %s.", + ftmp->oc_name); + nomovemsg = msgbuf; + } + useup(otmp); + return(1); +} + +/* called in main.c */ +gethungry(){ + --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 */ +morehungry(num) register num; { + u.uhunger -= num; + newuhs(TRUE); +} + +/* called after eating something (and after drinking fruit juice) */ +lesshungry(num) register num; { + u.uhunger += num; +#ifdef DGK + /* Have lesshungry() report when you're nearly full so all eating + * warns when you're about to choke. + */ + if (u.uhunger >= 1500) { + pline("You're having a hard time getting all of it down."); + multi -= 2; + nomovemsg = "You're finally finished."; + } +#endif /* DGK /**/ + newuhs(FALSE); +} + +unfaint(){ + u.uhs = FAINTING; + flags.botl = 1; +} + +newuhs(incr) boolean incr; { + register 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)) +poisonous(otmp) +register struct obj *otmp; +{ + return(index(POISONOUS, CORPSE_I_TO_C(otmp->otyp)) != 0); +} + +/* returns 1 if some text was printed */ +eatcorpse(otmp) register struct obj *otmp; { +register char let = CORPSE_I_TO_C(otmp->otyp); +register 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(index(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(index("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/src/edog.h b/src/edog.h @@ -0,0 +1,12 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* 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/src/end.c b/src/end.c @@ -0,0 +1,596 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* end.c - version 1.0.3 */ + +#include <stdio.h> +#include <signal.h> +#include "hack.h" +#define Sprintf (void) sprintf +extern char plname[], pl_character[]; +extern char *itoa(), *ordin(), *eos(); +extern boolean female; /* should have been flags.female */ +xchar maxdlevel = 1; + +done1() +{ + int (*fn)(); + + fn = signal(SIGINT,SIG_IGN); + pline("Really quit?"); + if(readchar() != 'y') { + (void) signal(SIGINT, fn); + clrlin(); + (void) fflush(stdout); + if(multi > 0) nomul(0); + return(0); + } + done("quit"); + /* NOTREACHED */ +} + +done_in_by(mtmp) register 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! */ +done(st1) +register char *st1; +{ +#ifdef WIZARD + if(wizard && *st1 == 'd'){ + u.uswldtim = 0; + if(u.uhpmax < 0) u.uhpmax = 100; /* arbitrary */ + 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 + 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(!index("bcd", *st1)) killer = st1; + paybill(); + clearlocks(); + if(flags.toplin == 1) more(); + if(index("bcds", *st1)){ +#ifdef WIZARD + if(!wizard) +#endif + savebones(); + if(!flags.notombstone) + outrip(); + } + if(*st1 == 'c') killer = st1; /* after outrip() */ + end_screen(); + 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') { + extern struct monst *mydogs; + register struct monst *mtmp; + register struct obj *otmp; +#ifdef DGK + long i; +#else + register int i; +#endif + register unsigned worthlessct = 0; + boolean has_amulet = FALSE; + + killer = st1; + keepdogs(); + mtmp = mydogs; + if(mtmp) { + printf("You"); + while(mtmp) { + printf(" and %s", monnam(mtmp)); + if(mtmp->mtame) + u.urexp += mtmp->mhp; + mtmp = mtmp->nmon; + } + printf("\nescaped from the dungeon with %ld points,\n", + u.urexp); + } else + 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; +#ifdef DGK + i = (long) otmp->quan * + objects[otmp->otyp].g_val; +#else + i = otmp->quan*objects[otmp->otyp].g_val; +#endif + if(i == 0) { + worthlessct += otmp->quan; + continue; + } + u.urexp += i; +#ifdef DGK + printf(" %s (worth %ld Zorkmids),\n", +#else + printf("\t%s (worth %d Zorkmids),\n", +#endif + doname(otmp), i); + } else if(otmp->olet == AMULET_SYM) { + otmp->known = 1; + i = (otmp->spe < 0) ? 2 : 5000; + u.urexp += i; +#ifdef DGK + printf(" %s (worth %d Zorkmids),\n", +#else + printf("\t%s (worth %d Zorkmids),\n", +#endif + doname(otmp), i); + if(otmp->spe >= 0) { + has_amulet = TRUE; + killer = "escaped (with amulet)"; + } + } + } + if(worthlessct) +#ifdef DGK + printf(" %u worthless piece%s of coloured glass,\n", +#else + printf("\t%u worthless piece%s of coloured glass,\n", +#endif + worthlessct, plur(worthlessct)); + if(has_amulet) u.urexp *= 2; + } else + printf("You %s on dungeon level %d with %ld points,\n", + st1, dlevel, u.urexp); + printf("and %ld piece%s of gold, after %ld move%s.\n", + u.ugold, plur(u.ugold), moves, plur(moves)); + printf("You were level %u with a maximum of %d hit points when you %s.\n", + u.ulevel, u.uhpmax, st1); + if(*st1 == 'e'){ + getret(); /* all those pieces of coloured glass ... */ + cls(); + } +#ifdef WIZARD + if(!wizard) +#endif + topten(); + exit(0); +} + +#define newttentry() (struct toptenentry *) alloc(sizeof(struct toptenentry)) +#define NAMSZ 10 +#define DTHSZ 40 +#define PERSMAX 3 +#define POINTSMIN 1 /* must be > 0 */ +#define ENTRYMAX 100 /* must be >= 10 */ +#ifndef MSDOS +#define PERS_IS_UID /* delete for PERSMAX per name; now per uid */ +#endif +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; + +topten(){ + int uid = getuid(); + int rank, rank0 = -1, rank1 = 0; + int occ_cnt = PERSMAX; + register struct toptenentry *t0, *t1, *tprev; + char *recfile = RECORD; + char *reclock = "record_lock"; + int sleepct = 300; + FILE *rfile; + register flg = 0; + extern char *getdate(); + if(!(rfile = fopen(recfile,"r"))){ + puts("Cannot open record file!"); + return; + } + (void) 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 = (female ? 'F' : 'M'); + t0->uid = uid; + (void) strncpy(t0->name, plname, NAMSZ); + (t0->name)[NAMSZ] = 0; + (void) strncpy(t0->death, killer, DTHSZ); + (t0->death)[DTHSZ] = 0; + (void) strcpy(t0->date, getdate()); + + /* assure minimum number of points */ + if(t0->points < POINTSMIN) + t0->points = 0; + + t1 = tt_head = newttentry(); + tprev = 0; + /* 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 == 0) + 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; + 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 */ + (void) fclose(rfile); + if(!(rfile = fopen(recfile,"w"))){ + puts("Cannot write record file\n"); + return; + } + + 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; + 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(rank > flags.end_top && + (rank < rank0-flags.end_around || rank > rank0+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-flags.end_around && + rank0 > flags.end_top+flags.end_around+1 && + !flags.end_own) + (void) putchar('\n'); + if(rank != rank0) + (void) outentry(rank, t1, 0); + else if(!rank1) + (void) outentry(rank, t1, 1); + else { + int t0lth = outentry(0, t0, -1); + int t1lth = outentry(rank, t1, t0lth); + if(t1lth > t0lth) t0lth = t1lth; + (void) outentry(0, t0, t0lth); + } + } + if(rank0 >= rank) + (void) outentry(0, t0, 1); + (void) fclose(rfile); +unlock: + (void) unlink(reclock); +} + +outheader() { +char linebuf[BUFSZ]; +register char *bp; + (void) strcpy(linebuf, "Number Points Name"); + bp = eos(linebuf); + while(bp < linebuf + COLNO - 9) *bp++ = ' '; + (void) strcpy(bp, "Hp [max]"); + puts(linebuf); +} + +/* so>0: standout line; so=0: ordinary line; so<0: no output, return lth */ +int +outentry(rank,t1,so) register struct toptenentry *t1; { +boolean quit = FALSE, killed = FALSE, starv = FALSE; +char linebuf[BUFSZ]; + linebuf[0] = 0; + if(rank) Sprintf(eos(linebuf), "%3d", rank); + else Sprintf(eos(linebuf), " "); +#ifdef DGK + Sprintf(eos(linebuf), " %6ld %10s", t1->points, t1->name); +#else + Sprintf(eos(linebuf), " %6ld %8s", t1->points, t1->name); +#endif + 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"), killed = TRUE; + Sprintf(eos(linebuf), " on%s level %d", + (killed || starv) ? "" : " dungeon", t1->level); + if(t1->maxlvl != t1->level) + Sprintf(eos(linebuf), " [max %d]", t1->maxlvl); + if(quit && t1->death[4]) Sprintf(eos(linebuf), t1->death + 4); + } + if(killed) Sprintf(eos(linebuf), " by %s%s", + (!strncmp(t1->death, "trick", 5) || !strncmp(t1->death, "the ", 4)) + ? "" : + index(vowels,*t1->death) ? "an " : "a ", + t1->death); + Sprintf(eos(linebuf), "."); + if(t1->maxhp) { + register char *bp = eos(linebuf); + char hpbuf[10]; + int hppos; + Sprintf(hpbuf, (t1->hp > 0) ? itoa(t1->hp) : "-"); + hppos = COLNO - 7 - strlen(hpbuf); + if(bp <= linebuf + hppos) { + while(bp < linebuf + hppos) *bp++ = ' '; + (void) strcpy(bp, hpbuf); + Sprintf(eos(bp), " [%d]", t1->maxhp); + } + } + if(so == 0) puts(linebuf); + else if(so > 0) { + register char *bp = eos(linebuf); + if(so >= COLNO) so = COLNO-1; + while(bp < linebuf + so) *bp++ = ' '; + *bp = 0; + standoutbeg(); + fputs(linebuf,stdout); + standoutend(); + (void) putchar('\n'); + } + return(strlen(linebuf)); +} + +char * +itoa(a) int a; { +static char buf[12]; + Sprintf(buf,"%d",a); + return(buf); +} + +char * +ordin(n) int n; { +register int d = n%10; + return((d==0 || d>3 || n/10==1) ? "th" : (d==1) ? "st" : + (d==2) ? "nd" : "rd"); +} + +clearlocks(){ +#ifdef DGK + eraseall(levels, alllevels); + if (ramdisk) + eraseall(permbones, alllevels); +#else +register x; + for(x = maxdlevel; x >= 0; x--) { + glo(x); + (void) unlink(lock); /* not all levels need be present */ + } +#endif +} + +char * +eos(s) +register char *s; +{ + while(*s) s++; + return(s); +} + +/* it is the callers responsibility to check that there is room for c */ +charcat(s,c) register char *s, 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). + */ +prscore(argc,argv) int argc; char **argv; { + extern char *hname; + char **players; + int playerct; + int rank; + register struct toptenentry *t1, *t2; + char *recfile = RECORD; + FILE *rfile; + register flg = 0; + register int i; + 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] && index("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(); + } + (void) 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) + (void) outentry(rank, t1, 0); + break; + } + } + free((char *) t1); + } +} diff --git a/src/engrave.c b/src/engrave.c @@ -0,0 +1,310 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* engrave.c - version 1.0.3 */ + +#include "hack.h" + +extern char *nomovemsg; +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; + +struct engr * +engr_at(x,y) register xchar x,y; { +register struct engr *ep = head_engr; + while(ep) { + if(x == ep->engr_x && y == ep->engr_y) + return(ep); + ep = ep->nxt_engr; + } + return((struct engr *) 0); +} + +sengr_at(s,x,y) register char *s; register xchar x,y; { +register struct engr *ep = engr_at(x,y); +register char *t; +register int n; + if(ep && ep->engr_time <= moves) { + t = ep->engr_txt; +/* + if(!strcmp(s,t)) return(1); +*/ + n = strlen(s); + while(*t) { + if(!strncmp(s,t,n)) return(1); + t++; + } + } + return(0); +} + +u_wipe_engr(cnt) +register int cnt; +{ + if(!u.uswallow && !Levitation) + wipe_engr_at(u.ux, u.uy, cnt); +} + +wipe_engr_at(x,y,cnt) register xchar x,y,cnt; { +register struct engr *ep = engr_at(x,y); +register int lth,pos; +char ch; + if(ep){ + if(ep->engr_type != DUST) { + 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); + } +} + +read_engr_at(x,y) register int x,y; { +register 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); + } +} + +make_engr_at(x,y,s) +register int x,y; +register char *s; +{ + register struct engr *ep; + + if(ep = engr_at(x,y)) + del_engr(ep); + ep = (struct engr *) + 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); + (void) strcpy(ep->engr_txt, s); + ep->engr_time = 0; + ep->engr_type = DUST; + ep->engr_lth = strlen(s) + 1; +} + +doengrave(){ +register int len; +register char *sp; +register struct engr *ep, *oep = engr_at(u.ux,u.uy); +char buf[BUFSZ]; +xchar type; +int spct; /* number of leading spaces */ +register 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 = 0; + 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, (char *) 0)); + 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 = 0; + } + 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 = (struct engr *) 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) { + (void) strcpy(sp, oep->engr_txt); + (void) strcat(sp, buf); + del_engr(oep); + } else + (void) 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); +} + +save_engravings(fd) int fd; { + register 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)); +#ifdef DGK + if (!count_only) +#endif + head_engr = 0; +} + +rest_engravings(fd) int fd; { +register struct engr *ep; +unsigned lth; + head_engr = 0; + while(1) { + mread(fd, (char *) &lth, sizeof(unsigned)); + if(lth == 0) return; + ep = (struct engr *) 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; + } +} + +del_engr(ep) register struct engr *ep; { +register 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((char *) ep); +} diff --git a/src/eshk.h b/src/eshk.h @@ -0,0 +1,24 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* 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/src/fight.c b/src/fight.c @@ -0,0 +1,385 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* fight.c - version 1.0.3 */ + +#include <stdio.h> +#include "hack.h" +extern struct permonst li_dog, dog, la_dog; +extern char *exclam(), *xname(); +extern struct obj *mkobj_at(); + +static boolean far_noise; +static long noisetime; + +/* hitmm returns 0 (miss), 1 (hit), or 2 (kill) */ +hitmm(magr,mdef) register struct monst *magr,*mdef; { +register struct permonst *pa = magr->data, *pd = mdef->data; +int hit; +schar tmp; +boolean vis; + if(index("Eauy", pa->mlet)) return(0); + if(magr->mfroz) return(0); /* riv05!a3 */ + tmp = pd->ac + pa->mlevel; + if(mdef->mconf || mdef->mfroz || mdef->msleep){ + tmp += 4; + if(mdef->msleep) mdef->msleep = 0; + } + hit = (tmp > rnd(20)); + if(hit) 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); + (void) sprintf(buf,"%s %s", Monnam(magr), + hit ? "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(hit){ + 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); + hit = 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); + hit = 2; + } + } + return(hit); +} + +/* drop (perhaps) a cadaver and remove monster */ +mondied(mdef) register struct monst *mdef; { +register struct permonst *pd = mdef->data; + if(letter(pd->mlet) && rn2(3)){ + (void) 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 */ +monstone(mdef) register struct monst *mdef; { + extern char mlarge[]; + if(index(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); +} + + +fightm(mtmp) register struct monst *mtmp; { +register 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 */ +thitu(tlev,dam,name) +register tlev,dam; +register 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',&"; + +boolean +hmon(mon,obj,thrown) /* return TRUE if mon still alive */ +register struct monst *mon; +register struct obj *obj; +register thrown; +{ + register tmp; + boolean 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(index(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((struct obj *) 0, obj->owornmask); + obfree(obj, (struct obj *) 0); + 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((struct obj *) 0, obj->owornmask); + obfree(obj, (struct obj *) 0); + 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(index(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 */ +attack(mtmp) +register struct monst *mtmp; +{ + schar tmp; + boolean malive = TRUE; + register 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); + +#ifdef DGK + /* This section of code provides protection against accidentally + * hitting peaceful (like '@') and tame (like 'd') monsters. + * There is protection only if you're not blind, confused or + * invisible. + */ + if (flags.confirm && (mtmp->mpeaceful || mtmp->mtame) + && !Confusion && !Invisible) + if (Blind ? Telepat : (!mtmp->minvis || See_invisible)) { + pline("Really attack?"); + (void) fflush(stdout); + if (readchar() != 'y') { + flags.move = 0; + return(TRUE); + } + } +#endif + if(mtmp->mimic){ + if(!u.ustuck && !mtmp->mflee) u.ustuck = mtmp; +#ifdef DGK + if (levl[u.ux+u.dx][u.uy+u.dy].scrsym == symbol.door) + pline("The door actually was a Mimic."); + else if (levl[u.ux+u.dx][u.uy+u.dy].scrsym == '$') + pline("The chest was a Mimic!"); + else + pline("Wait! That's a Mimic!"); +#else + 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!"); + } +#endif + wakeup(mtmp); /* clears mtmp->mimic */ + return(TRUE); + } + + wakeup(mtmp); + + if(mtmp->mhide && mtmp->mundetected){ + register 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 && + index("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/src/flag.h b/src/flag.h @@ -0,0 +1,53 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* flag.h - version 1.0.3 */ + +struct flag { + unsigned ident; /* social security number for each monster */ + unsigned debug; /* in debugging mode */ +#define wizard flags.debug + unsigned toplin; /* a top line (message) has been printed */ + /* 0: top line empty; 2: no --More-- reqd. */ + unsigned cbreak; /* in cbreak mode, rogue format */ + unsigned standout; /* use standout for --More-- */ + unsigned nonull; /* avoid sending nulls to the terminal */ + unsigned time; /* display elapsed 'time' */ + unsigned nonews; /* suppress news printing */ + unsigned notombstone; + unsigned end_top, end_around; /* describe desired score list */ + unsigned end_own; /* idem (list all own scores) */ + unsigned no_rest_on_space; /* spaces are ignored */ + unsigned beginner; + unsigned move; + unsigned invlet_constant; /* let objects keep their + inventory symbol */ + unsigned mv; + unsigned run; /* 0: h (etc), 1: H (etc), 2: fh (etc) */ + /* 3: FH, 4: ff+, 5: ff-, 6: FF+, 7: FF- */ + unsigned nopick; /* do not pickup objects */ + unsigned echo; /* 1 to echo characters */ + unsigned botl; /* partially redo status line */ + unsigned botlx; /* print an entirely new bottom line */ + unsigned nscrinh; /* inhibit nscr() in pline(); */ + unsigned made_amulet; + unsigned no_of_wizards; /* 0, 1 or 2 (wizard and his shadow) */ + /* reset from 2 to 1, but never to 0 */ + unsigned moonphase; +#define NEW_MOON 0 +#define FULL_MOON 4 + +#ifdef DGK + unsigned silent; /* whether the bell rings or not */ + unsigned confirm; /* confirm before hitting tame monsters */ + unsigned pickup; /* whether you pickup or move and look */ + unsigned IBMBIOS; /* whether we can use a BIOS call for + * redrawing the screen and character input */ + unsigned sortpack; + unsigned DECRainbow; /* Used for DEC Rainbow graphics. */ + unsigned rawio; /* Whether can use rawio (IOCTL call) */ + unsigned extra1; + unsigned extra2; +#endif /* DGK /**/ +}; + +extern struct flag flags; + diff --git a/src/gen.h b/src/gen.h @@ -0,0 +1,15 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* gen.h version 1.0.1: added ONCE flag */ + +struct gen { + struct gen *ngen; + xchar gx,gy; + unsigned gflag; /* 037: trap type; 040: SEEN flag */ + /* 0100: ONCE only */ +#define TRAPTYPE 037 +#define SEEN 040 +#define ONCE 0100 +}; +extern struct gen *fgold, *ftrap; +struct gen *g_at(); +#define newgen() (struct gen *) alloc(sizeof(struct gen)) diff --git a/src/gold.h b/src/gold.h @@ -0,0 +1,12 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* gold.h - version 1.0.2 */ + +struct gold { + struct gold *ngold; + xchar gx,gy; + long amount; +}; + +extern struct gold *fgold; +struct gold *g_at(); +#define newgold() (struct gold *) alloc(sizeof(struct gold)) diff --git a/src/hack.c b/src/hack.c @@ -0,0 +1,830 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.c - version 1.0.3 */ + +#include <stdio.h> +#include "hack.h" + +extern char news0(); +extern char *nomovemsg; +extern char *exclam(); +extern struct obj *addinv(); +extern boolean hmon(); + +/* called on movement: + 1. when throwing ball+chain far away + 2. when teleporting + 3. when walking out of a lit room + */ +unsee() { + register x,y; + register struct rm *lev; + +/* + if(u.udispl){ + u.udispl = 0; + newsym(u.udisx, u.udisy); + } +*/ + if(seehx){ + seehx = 0; + } else + 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]; +#ifdef DGK + if(!lev->lit && lev->scrsym == symbol.room) { +#else + if(!lev->lit && lev->scrsym == '.') { +#endif + lev->scrsym =' '; + lev->new = 1; + on_scr(x,y); + } + } +} + +/* called: + in eat.c: seeoff(0) - blind after eating rotten food + in mon.c: seeoff(0) - blinded by a yellow light + in mon.c: seeoff(1) - swallowed + in do.c: seeoff(0) - blind after drinking potion + in do.c: seeoff(1) - go up or down the stairs + in trap.c:seeoff(1) - fall through trapdoor + */ +seeoff(mode) /* 1 to redo @, 0 to leave them */ +{ /* 1 means misc movement, 0 means blindness */ + register x,y; + register struct rm *lev; + + if(u.udispl && mode){ + u.udispl = 0; + levl[u.udisx][u.udisy].scrsym = news0(u.udisx,u.udisy); + } + if(seehx) { + seehx = 0; + } else + 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]; +#ifdef DGK + if(!lev->lit && lev->scrsym == symbol.room) +#else + if(!lev->lit && lev->scrsym == '.') +#endif + lev->seen = 0; + } + } +} + +static +moverock() { + register xchar rx, ry; + register struct obj *otmp; + register struct trap *ttmp; + + while(otmp = sobj_at(ENORMOUS_ROCK, u.ux+u.dx, u.uy+u.dy)) { + 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)) + 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; + /* pobj(otmp); */ + 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 (-1); + } + } + return (0); +} + +domove() +{ + register struct monst *mtmp; + register struct rm *tmpr,*ust; + struct trap *trap; + register 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]; + u.ux0 = u.ux; + u.uy0 = 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 */ + +#ifdef DGK + /* Don't attack if you're running */ + if (flags.run && !mtmp->mimic + && (Blind ? Telepat : (!mtmp->minvis || See_invisible))) { + nomul(0); + flags.move = 0; + return; + } +#endif + nomul(0); + gethungry(); + if(multi < 0) return; /* we just fainted */ + + /* 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; + } + if (moverock() < 0) 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(u.udispl) { + u.udispl = 0; + newsym(u.ux0,u.uy0); + } +*/ + if(!Blind) { + 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(u.ux0-u.dx,u.uy0-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(u.ux0-u.dx,u.uy0-u.dy); + } + } else { + pru(); + } + if(!flags.nopick) pickup(1); + if(trap) dotrap(trap); /* fall into pit, arrow trap, etc. */ + (void) inshop(); + if(!Blind) read_engr_at(u.ux,u.uy); +} + +movobj(obj, ox, oy) +register struct obj *obj; +register int ox, oy; +{ + /* Some dirty programming to get display right */ + freeobj(obj); + unpobj(obj); + obj->nobj = fobj; + fobj = obj; + obj->ox = ox; + obj->oy = oy; +} + +dopickup(){ + 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); +} + +pickup(all) +{ + register struct gold *gold; + register struct obj *obj, *obj2; + register int wt; + + if(Levitation) return; +#ifdef DGK + if (all && !flags.pickup) { + 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 gold is the only thing here, pick it up. + */ + if (!ct && g_at(u.ux, u.uy)) { + if (flags.run) nomul(0); + 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 (Invisible) newsym(u.ux,u.uy); + } + + /* If there are objects here, take a look. + */ + if (ct) { + if (flags.run) + nomul(0); + nscr(); + if (ct < 5) + dolook(); + else + pline("There are several objects here."); + } + return; + } +#endif + 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) { + register 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(!index("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 */ + extern struct obj *splitobj(); + 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)); + (void) 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 accomodate 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) obj->dknown = 1; /* this is done by prinv(), + 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 */ +lookaround(){ +register x,y,i,x0,y0,m0,i0 = 9; +register int corrct = 0, noturn = 0; +register struct monst *mtmp; +#ifdef lint + /* suppress "used before set" message */ + x0 = y0 = 0; +#endif /* lint /**/ + if(Blind || flags.run == 0) return; + if(flags.run == 1 && levl[u.ux][u.uy].typ == ROOM) return; + 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 mtmp = 0; /* invisible M cannot influence us */ + if(x == u.ux-u.dx && y == u.uy-u.dy) continue; +#ifdef DGK + { + register uchar sym = levl[x][y].scrsym; + + if (sym == symbol.vwall || sym == symbol.hwall + || sym == symbol.room || sym == ' ' || IS_CORNER(sym)) + continue; + else if (sym == symbol.door) { + if(x != u.ux && y != u.uy) continue; + if(flags.run != 1) goto stop; + goto corr; + } else if (sym == symbol.corr) { + corr: + if(flags.run == 1 || flags.run == 3) { + i = DIST(x,y,u.ux+u.dx,u.uy+u.dy); + if(i > 2) continue; + 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++; + continue; + } else if (sym == '^') { + if(flags.run == 1) goto corr; /* if you must */ + if(x == u.ux+u.dx && y == u.uy+u.dy) goto stop; + continue; + } else { /* e.g. objects or trap or stairs */ + if(flags.run == 1) goto corr; + if(mtmp) continue; /* d */ + } + stop: + nomul(0); + return; + } +#else + 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) goto corr; /* if you must */ + 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) break; /* d */ + stop: + nomul(0); + return; + } +#endif + } + 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 */ +monster_nearby() { +register int x,y; +register 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 && !index("Ea", mtmp->data->mlet) && + !mtmp->mfroz && !mtmp->msleep && /* aplvax!jcn */ + (!mtmp->minvis || See_invisible)) + return(1); + } + return(0); +} + +cansee(x,y) xchar x,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); +} + +sgn(a) register int a; { + return((a > 0) ? 1 : (a == 0) ? 0 : -1); +} + +setsee() +{ + register 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) seehx = 0; /* seems necessary elsewhere */ + 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); + } +} + + +nomul(nval) +register nval; +{ +#ifdef DGK + if(multi < nval) return; /* This is a bug fix by ab@unido */ +#else + if(multi < 0) return; +#endif + multi = nval; + flags.mv = flags.run = 0; +} + +abon() +{ + 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) return(1); /* up to 18/50 */ + else if(u.ustr < 118) return(2); + else return(3); +} + +dbon() +{ + 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) return(2); /* up to 18 */ + else if(u.ustr < 94) return(3); /* up to 18/75 */ + else if(u.ustr < 109) return(4); /* up to 18/90 */ + else if(u.ustr < 118) return(5); /* up to 18/99 */ + else return(6); +} + +losestr(num) /* may kill you; cause may be poison or monster like 'A' */ +register num; +{ + u.ustr -= num; + while(u.ustr < 3) { + u.ustr++; + u.uhp -= 6; + u.uhpmax -= 6; + } + flags.botl = 1; +} + +losehp(n,knam) +register n; +register 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"); + } +} + +losehp_m(n,mtmp) +register n; +register struct monst *mtmp; +{ + u.uhp -= n; + flags.botl = 1; + if(u.uhp < 1) + done_in_by(mtmp); +} + +losexp() /* hit by V or W */ +{ + register num; + extern long newuexp(); + + 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; +} + +inv_weight(){ +register struct obj *otmp = invent; +register int wt = (u.ugold + 500)/1000; +register 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); +} + +inv_cnt(){ +register struct obj *otmp = invent; +register int ct = 0; + while(otmp){ + ct++; + otmp = otmp->nobj; + } + return(ct); +} + +long +newuexp() +{ + return(10*(1L << (u.ulevel-1))); +} diff --git a/src/hack.h b/src/hack.h @@ -0,0 +1,159 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* hack.h - version 1.0.3 */ + +#include "config.h" + +#ifdef BSD +#include <strings.h> /* declarations for strcat etc. */ +#else +#include <string.h> /* idem on System V */ +#define index strchr +#define rindex strrchr +#endif /* BSD /**/ + +#define Null(type) ((struct type *) 0) + +#include "objclass.h" + +typedef struct { + xchar x,y; +} coord; + +#include "monst.h" /* uses coord */ +#include "gold.h" +#include "trap.h" +#include "obj.h" +#include "flag.h" + +#ifndef MSDOS +extern char *sprintf(); +#endif /* MSDOS /**/ +#define plur(x) (((x) == 1) ? "" : "s") +#define min(x,y) ((x) < (y) ? (x) : (y)) + +#define BUFSZ 256 /* for getlin buffers */ +#define PL_NSIZ 32 /* name of player, ghost, shopkeeper */ + +#include "rm.h" +#include "permonst.h" + +extern long *alloc(); + +extern xchar xdnstair, ydnstair, xupstair, yupstair; /* stairs up and down. */ + +extern xchar dlevel; +#define newstring(x) (char *) alloc((unsigned)(x)) +#include "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 obj *o_at(), *getobj(), *sobj_at(); + +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; + int (*p_tofn)(); /* called after timeout */ +}; + +struct you { + xchar ux, uy; + schar dx, dy, dz; /* direction of move (or zap or ... ) */ + xchar ux0, uy0; /* initial position FF */ + 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; + unsigned udispl; /* @ on display */ + unsigned ulevel; /* 1 - 14 */ + unsigned utrap; /* trap timeout */ + unsigned utraptype; /* defined if utrap nonzero */ +#define TT_BEARTRAP 0 +#define TT_PIT 1 + unsigned uinshop; /* 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; + char *usick_cause; + struct prop uprops[LAST_RING+10]; + + unsigned uswallow; /* set if swallowed by a monster */ + unsigned uswldtim; /* time you have been swallowed */ + unsigned uhs; /* hunger state - see 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 char *traps[]; +extern char *monnam(), *Monnam(), *amonnam(), *Amonnam(), + *doname(), *aobjnam(); +extern char readchar(); +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 char *save_cm,*killer; + +extern xchar dlevel, maxdlevel; /* dungeon level */ + +extern long moves; + +extern int multi; + + +extern char lock[]; + + +#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 */ diff --git a/src/invent.c b/src/invent.c @@ -0,0 +1,927 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* invent.c - version 1.0.3 */ + +#include <stdio.h> +#include "hack.h" +extern struct obj *splitobj(); +extern struct obj zeroobj; +extern char morc; +extern char quitchars[]; +static char *xprname(); + +#ifndef NOWORM +#include "wseg.h" +extern struct wseg *wsegs[32]; +#endif /* NOWORM /**/ + +#define NOINVSYM '#' + +int lastinvnr = 51; /* 0 ... 51 */ +static +assigninvlet(otmp) +register struct obj *otmp; +{ + boolean inuse[52]; + register int i; + register 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(obj) +register struct obj *obj; +{ + register struct obj *otmp; + + /* merge or attach to end of chain */ + if(!invent) { + invent = obj; + otmp = 0; + } 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); +} + +useup(obj) +register struct obj *obj; +{ + if(obj->quan > 1){ + obj->quan--; + obj->owt = weight(obj); + } else { + setnotworn(obj); + freeinv(obj); + obfree(obj, (struct obj *) 0); + } +} + +freeinv(obj) +register struct obj *obj; +{ + register 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) */ +delobj(obj) register struct obj *obj; { + freeobj(obj); + unpobj(obj); + obfree(obj, (struct obj *) 0); +} + +/* unlink obj from chain starting with fobj */ +freeobj(obj) register struct obj *obj; { + register 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! */ +freegold(gold) register struct gold *gold; { + register 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((char *) gold); +} + +deltrap(trap) +register struct trap *trap; +{ + register struct trap *ttmp; + + if(trap == ftrap) + ftrap = ftrap->ntrap; + else { + for(ttmp = ftrap; ttmp->ntrap != trap; ttmp = ttmp->ntrap) ; + ttmp->ntrap = trap->ntrap; + } + free((char *) trap); +} + +struct wseg *m_atseg; + +struct monst * +m_at(x,y) +register x,y; +{ + register struct monst *mtmp; +#ifndef NOWORM + register struct wseg *wtmp; +#endif /* NOWORM /**/ + + m_atseg = 0; + 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(x,y) +register x,y; +{ + register 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(n,x,y) +register n,x,y; +{ + register struct obj *otmp; + + for(otmp = fobj; otmp; otmp = otmp->nobj) + if(otmp->ox == x && otmp->oy == y && otmp->otyp == n) + return(otmp); + return(0); +} + +carried(obj) register struct obj *obj; { +register struct obj *otmp; + for(otmp = invent; otmp; otmp = otmp->nobj) + if(otmp == obj) return(1); + return(0); +} + +carrying(type) +register int type; +{ + register struct obj *otmp; + + for(otmp = invent; otmp; otmp = otmp->nobj) + if(otmp->otyp == type) + return(TRUE); + return(FALSE); +} + +struct obj * +o_on(id, objchn) unsigned int id; register struct obj *objchn; { + while(objchn) { + if(objchn->o_id == id) return(objchn); + objchn = objchn->nobj; + } + return((struct obj *) 0); +} + +struct trap * +t_at(x,y) +register x,y; +{ + register struct trap *trap = ftrap; + while(trap) { + if(trap->tx == x && trap->ty == y) return(trap); + trap = trap->ntrap; + } + return(0); +} + +struct gold * +g_at(x,y) +register x,y; +{ + register 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 */ +struct obj * +mkgoldobj(q) +register long q; +{ + register 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. + * (struct obj *) 0 error return: no object. + * &zeroobj explicitly no object (as in w-). + */ +struct obj * +getobj(let,word) +register char *let,*word; +{ + register struct obj *otmp; + register char ilet,ilet1,ilet2; + char buf[BUFSZ]; + char lets[BUFSZ]; + register int foo = 0, foo2; + register 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 || index(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; + (void) 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]) { +#ifdef REDO + if (!in_doagain) +#endif + pline("What do you want to %s [*]? ", word); + } else { +#ifdef REDO + if (!in_doagain) +#endif + pline("What do you want to %s [%s or ?*]? ", + word, buf); + } + + + cnt = 0; + ilet = readchar(); + while(digit(ilet) && allowcnt) { +#ifdef REDO + if (ilet != '?' && ilet != '*') + savech(ilet); +#endif + cnt = 10*cnt + (ilet - '0'); + allowcnt = 2; /* signal presence of cnt */ + ilet = readchar(); + } + if(digit(ilet)) { + pline("No count allowed with this command."); + continue; + } + if(index(quitchars,ilet)) + return((struct obj *)0); + if(ilet == '-') { + return(allownone ? &zeroobj : (struct obj *) 0); + } + 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((char *) 0); + if(!(ilet = morc)) continue; + /* ... */ + } +#ifdef REDO + if (ilet != '?' && ilet != '*') + savech(ilet); +#endif + 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) ; + } + 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 && !index(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) { + register struct obj *obj; + obj = splitobj(otmp, (int) cnt); + if(otmp == uwep) setuwep(obj); + } + } + return(otmp); +} + +ckunpaid(otmp) register struct obj *otmp; { + return( otmp->unpaid ); +} + +/* interactive version of getobj - used for Drop and Identify */ +/* return the number of times fn was called successfully */ +ggetobj(word, fn, max) +char *word; +int (*fn)(), max; +{ +char buf[BUFSZ]; +register char *ip; +register char sym; +register int oletct = 0, iletct = 0; +register boolean allflag = FALSE; +char olets[20], ilets[20]; +int (*ckfn)() = (int (*)()) 0; +xchar allowgold = (u.ugold && !strcmp(word, "drop")) ? 1 : 0; /* BAH */ + if(!invent && !allowgold){ + pline("You have nothing to %s.", word); + return(0); + } else { + register struct obj *otmp = invent; + register int uflg = 0; + + if(allowgold) ilets[iletct++] = '$'; + ilets[iletct] = 0; + while(otmp) { + if(!index(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(index("!%?[()=*/\"0", sym)){ + if(!index(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. + */ +askchain(objchn, olets, allflag, fn, ckfn, max) +struct obj *objchn; +register char *olets; +int allflag; +int (*fn)(), (*ckfn)(); +int max; +{ +register struct obj *otmp, *otmp2; +register char sym, ilet; +register int cnt = 0; +#ifdef DGK + /* changes so the askchain is interrogated in the order specified. + * For example, if a person specifies =/ then first all rings will be + * asked about followed by all wands -dgk + */ +nextclass: +#endif + ilet = 'a'-1; + for(otmp = objchn; otmp; otmp = otmp2){ + if(ilet == 'z') ilet = 'A'; else ilet++; + otmp2 = otmp->nobj; +#ifdef DGK + if (olets && *olets && otmp->olet != *olets) continue; +#else + if(olets && *olets && !index(olets, otmp->olet)) continue; +#endif + if(ckfn && !(*ckfn)(otmp)) continue; + if(!allflag) { + pline(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; + } + } +#ifdef DGK + if (olets && *olets && *++olets) + goto nextclass; +#endif + pline(cnt ? "That was all." : "No applicable objects."); +ret: + return(cnt); +} + +obj_to_let(obj) /* should of course only be called for things in invent */ +register struct obj *obj; +{ + register struct obj *otmp; + register 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); +} + +prinv(obj) +register struct obj *obj; +{ + pline(xprname(obj, obj_to_let(obj))); +} + +static char * +xprname(obj,let) +register struct obj *obj; +register char let; +{ + static char li[BUFSZ]; + + (void) sprintf(li, "%c - %s.", + flags.invlet_constant ? obj->invlet : let, + doname(obj)); + return(li); +} + +ddoinv() +{ + doinv((char *) 0); + return(0); +} + +#ifdef DGK +char inv_order[] = "\")[%?/=!(*0_`"; /* to be safe, include _ and ` */ +extern char *let_to_name(); +#endif + +/* called with 0 or "": all objects in inventory */ +/* otherwise: all objects with (serial) letter in lets */ +doinv(lets) +register char *lets; +{ + register struct obj *otmp; + register char ilet; + int ct = 0; + char any[BUFSZ]; +#ifdef DGK + char *invlet = inv_order; + int classcount = 0; +#endif /* DGK /**/ + + morc = 0; /* just to be sure */ + + if(!invent){ + pline("Not carrying anything."); + return; + } + + cornline(0, (char *) 0); +#ifdef DGK +nextclass: + classcount = 0; + ilet = 'a'; + for(otmp = invent; otmp; otmp = otmp->nobj) { + if(flags.invlet_constant) ilet = otmp->invlet; + if(!lets || !*lets || index(lets, ilet)) { + if (!flags.sortpack || otmp->olet == *invlet) { + if (flags.sortpack && !classcount) { + cornline(1, let_to_name(*invlet)); + classcount++; + } + cornline(1, xprname(otmp, ilet)); + any[ct++] = ilet; + } + } + if(!flags.invlet_constant) if(++ilet > 'z') ilet = 'A'; + } + if (flags.sortpack && *++invlet) goto nextclass; +#else + ilet = 'a'; + for(otmp = invent; otmp; otmp = otmp->nobj) { + if(flags.invlet_constant) ilet = otmp->invlet; + if(!lets || !*lets || index(lets, ilet)) { + cornline(1, xprname(otmp, ilet)); + any[ct++] = ilet; + } + if(!flags.invlet_constant) if(++ilet > 'z') ilet = 'A'; + } +#endif /* DGK /**/ + any[ct] = 0; + cornline(2, any); +} + +dotypeinv () /* free after Robert Viduya */ +/* Changed to one type only, so he doesnt have to type cr */ +{ + char c, ilet; + char stuff[BUFSZ]; + register int stct; + register 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 (!index (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) { +#ifdef REDO + if (!in_doagain) +#endif + pline ("What type of object [%s] do you want an inventory of? ", + stuff); + c = readchar(); +#ifdef REDO + savech(c); +#endif + if(index(quitchars,c)) return(0); + } else + c = stuff[0]; + + if(c == '$') + return(doprgold()); + + if(c == 'x' || c == 'X') { + if(billx) + (void) 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 */ +dolook() { + register struct obj *otmp, *otmp0; + register struct gold *gold; + 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]; + + (void) 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, (char *) 0); + } + if(ct > 1) + cornline(2, (char *) 0); + return(!!Blind); +} + +stackobj(obj) register struct obj *obj; { +register 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 */ +merged(otmp,obj,lose) register struct obj *otmp, *obj; { + if(obj->otyp == otmp->otyp && + obj->unpaid == otmp->unpaid && + obj->spe == otmp->spe && + obj->dknown == otmp->dknown && + obj->cursed == otmp->cursed && + (index("%*?!", 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.] + */ +extern int (*occupation)(); +extern char *occtxt; +static long goldcounted; + +countgold(){ + 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 */ +} + +doprgold(){ + 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 --- */ + +doprwep(){ + if(!uwep) pline("You are empty handed."); + else prinv(uwep); + return(0); +} + +doprarm(){ + if(!uarm && !uarmg && !uarms && !uarmh) + pline("You are not wearing any armor."); + else { + char lets[6]; + register 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); +} + +doprring(){ + if(!uleft && !uright) + pline("You are not wearing any rings."); + else { + char lets[3]; + register 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); +} + +digit(c) char c; { + return(c >= '0' && c <= '9'); +} diff --git a/src/lev.c b/src/lev.c @@ -0,0 +1,559 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* lev.c - version 1.0.3 */ + +#include <stdio.h> +#include "hack.h" +#include "mkroom.h" + +extern struct monst *restmonchn(); +extern struct obj *restobjchn(); +extern struct obj *billobjs; +extern char *itoa(); +extern int hackpid; +extern xchar dlevel; +extern char nul[], SAVEF[]; + +#ifndef NOWORM +#include "wseg.h" +extern struct wseg *wsegs[32], *wheads[32]; +extern long wgrowtime[32]; +#endif /* NOWORM /**/ + +#ifdef DGK +struct finfo fileinfo[MAXLEVEL+1]; +long bytes_counted; +int count_only; +#else +boolean level_exists[MAXLEVEL+1]; +#endif + +#ifdef DGK +savelev(fd, lev, mode) +int fd, mode; +xchar lev; +{ + if (mode & COUNT) { + count_only = TRUE; + bytes_counted = 0; + savelev0(fd, lev); + while (bytes_counted > freediskspace(levels)) + if (!swapout_oldest()) + return FALSE; + } + if (mode & WRITE) { + count_only = FALSE; + bytes_counted = 0; + savelev0(fd, lev); + } + fileinfo[lev].where = ACTIVE; + fileinfo[lev].time = moves; + fileinfo[lev].size = bytes_counted; + return TRUE; +} + +savelev0(fd,lev) +#else +savelev(fd,lev) +#endif +int fd; +xchar lev; +{ +#ifndef NOWORM + register struct wseg *wtmp, *wtmp2; + register tmp; +#endif /* NOWORM /**/ + + if(fd < 0) panic("Save on bad file!"); /* impossible */ +#ifndef DGK + if(lev >= 0 && lev <= MAXLEVEL) + level_exists[lev] = TRUE; +#endif + +#ifdef DGK + bwrite(fd, (char *) &vmajor, sizeof(vmajor)); + bwrite(fd, (char *) &vminor, sizeof(vminor)); +#endif + bwrite(fd,(char *) &hackpid,sizeof(hackpid)); + bwrite(fd,(char *) &lev,sizeof(lev)); + bwrite(fd,(char *) levl,sizeof(levl)); +#ifdef DGK + bwrite(fd, (char *) &symbol, sizeof(symbol)); +#endif + 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); + save_engravings(fd); + bwrite(fd,(char *) rooms,sizeof(rooms)); + bwrite(fd,(char *) doors,sizeof(doors)); +#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)); + } +#ifdef DGK + if (!count_only) +#endif + wsegs[tmp] = 0; + } + bwrite(fd,(char *) wgrowtime,sizeof(wgrowtime)); +#endif /* NOWORM /**/ +#ifdef DGK + if (count_only) + return; +#endif + billobjs = 0; + fgold = 0; + ftrap = 0; + fmon = 0; + fobj = 0; +} + +bwrite(fd,loc,num) +register fd; +register char *loc; +register unsigned num; +{ + + bytes_counted += num; +#ifdef DGK + if (!count_only) +#endif + if (write(fd, loc, (unsigned) num) != num) + panic("bwrite: disk error"); +} + +saveobjchn(fd,otmp) +register fd; +register struct obj *otmp; +{ + register 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)); +#ifdef DGK + if (!count_only) +#endif + free((char *) otmp); + otmp = otmp2; + } + bwrite(fd, (char *) &minusone, sizeof(int)); +} + +#ifdef MSDOS +/* We don't want to save any pointers in any files, so convert + * the pointers to indices before writing the monsters to disk -dgk + */ +savemonchn(fd,mtmp) +register fd; +register struct monst *mtmp; +{ + register struct monst *mtmp2; + unsigned xl; + int minusone = -1; + struct permonst *permonstp; + int monsindex; + extern struct permonst li_dog, dog, la_dog, hell_hound, pm_guard; + extern struct permonst pm_eel; + + while(mtmp) { + mtmp2 = mtmp->nmon; + xl = mtmp->mxlth + mtmp->mnamelth; + bwrite(fd, (char *) &xl, sizeof(int)); + /* store an index where the pointer used to be */ + permonstp = mtmp->data; + if (permonstp == &li_dog) + monsindex = INDEX_LITTLEDOG; + else if (permonstp == &dog) + monsindex = INDEX_DOG; + else if (permonstp == &la_dog) + monsindex = INDEX_LARGEDOG; + else if (permonstp == &hell_hound) + monsindex = INDEX_HELLHOUND; + else if (permonstp == &pm_guard) + monsindex = INDEX_GUARD; + else if (permonstp > &pm_eel || permonstp < &mons[0]) { + msmsg("Unsupported monster pointer in savemonchn\n"); + goto next; + } else + monsindex = permonstp - &mons[0]; + *((int *)&mtmp->data) = monsindex; + bwrite(fd, (char *) mtmp, xl + sizeof(struct monst)); + mtmp->data = permonstp; /* restore the pointer */ + if(mtmp->minvent) saveobjchn(fd,mtmp->minvent); +#ifdef DGK + if (!count_only) +#endif + free((char *) mtmp); +next: + mtmp = mtmp2; + } + bwrite(fd, (char *) &minusone, sizeof(int)); +} +#else + +savemonchn(fd,mtmp) +register fd; +register struct monst *mtmp; +{ + register 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((char *) mtmp); + mtmp = mtmp2; + } + bwrite(fd, (char *) &minusone, sizeof(int)); +} +#endif + +savegoldchn(fd,gold) +register fd; +register struct gold *gold; +{ + register struct gold *gold2; + while(gold) { + gold2 = gold->ngold; + bwrite(fd, (char *) gold, sizeof(struct gold)); +#ifdef DGK + if (!count_only) +#endif + free((char *) gold); + gold = gold2; + } + bwrite(fd, nul, sizeof(struct gold)); +} + +savetrapchn(fd,trap) +register fd; +register struct trap *trap; +{ + register struct trap *trap2; + while(trap) { + trap2 = trap->ntrap; + bwrite(fd, (char *) trap, sizeof(struct trap)); +#ifdef DGK + if (!count_only) +#endif + free((char *) trap); + trap = trap2; + } + bwrite(fd, nul, sizeof(struct trap)); +} + +getlev(fd,pid,lev) +int fd,pid; +xchar lev; +{ + register struct gold *gold; + register struct trap *trap; +#ifndef NOWORM + register struct wseg *wtmp; +#endif /* NOWORM /**/ + register tmp; + long omoves; + int hpid; + xchar dlvl; +#ifdef DGK + struct symbols osymbol; + int ovmajor, ovminor, x, y, up, dn, lt, rt; + uchar osym, nsym; +#endif + +#ifdef MSDOS + setmode(fd,O_BINARY); +#endif +#ifdef DGK + /* Not yet used */ + mread(fd, (char *) &ovmajor, sizeof(ovmajor)); + mread(fd, (char *) &ovminor, sizeof(ovminor)); +#endif + /* 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)); +#ifdef DGK + /* Corners are poorly implemented. They only exist in the + * scrsym field of each dungeon element. So we have to go + * through the previous level, looking for scrsym with the + * old corner values, checking to make sure that they are + * where corners should be, then replace them with the scrsym + * of the new GRAPHICS character set. Ugly. + */ + mread(fd, (char *) &osymbol, sizeof(osymbol)); + if (memcmp((char *) &osymbol, (char *) &symbol, sizeof (symbol))) { + for (x = 0; x < COLNO; x++) + for (y = 0; y < ROWNO; y++) { + osym = levl[x][y].scrsym; + nsym = 0; + switch (levl[x][y].typ) { + case 0: + case SCORR: + break; + case ROOM: + if (osym == osymbol.room) + nsym = symbol.room; + break; + case DOOR: + if (osym == osymbol.door) + nsym = symbol.door; + break; + case CORR: + if (osym == osymbol.corr) + nsym = symbol.corr; + break; + case VWALL: + if (osym == osymbol.vwall) + nsym = symbol.vwall; + break; + case SDOOR: + if (osym == osymbol.vwall) + nsym = symbol.vwall; + else if (osym == osymbol.hwall) + nsym = symbol.hwall; + break; + /* Now the ugly stuff */ + case HWALL: + up = (y > 0) ? levl[x][y-1].typ : 0; + dn = (y < ROWNO-1) ?levl[x][y+1].typ : 0; + lt = (x > 0) ? levl[x-1][y].typ : 0; + rt = (x < COLNO-1) ?levl[x+1][y].typ : 0; + up = up && (up == VWALL || up == DOOR + || up == SDOOR); + dn = dn && (dn == VWALL || dn == DOOR + || dn == SDOOR); + lt = lt && (lt == HWALL || lt == DOOR + || lt == SDOOR); + rt = rt && (rt == HWALL || rt == DOOR + || rt == SDOOR); + if (rt && dn && osym == osymbol.tlcorn) + nsym = symbol.tlcorn; + else if (lt && dn && osym == osymbol.trcorn) + nsym = symbol.trcorn; + else if (rt && up && osym == osymbol.blcorn) + nsym = symbol.blcorn; + else if (lt && up && osym == osymbol.brcorn) + nsym = symbol.brcorn; + else if (osym == osymbol.hwall) + nsym = symbol.hwall; + break; + default: + break; + } + if (nsym) + levl[x][y].scrsym = nsym; + } + } +#endif + 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; + register struct monst *mtmp, *mtmp2; + extern char genocided[]; + + for(mtmp = fmon; mtmp; mtmp = mtmp2) { + long newhp; /* tmoves may be very large */ + + mtmp2 = mtmp->nmon; + if(index(genocided, mtmp->data->mlet)) { + mondead(mtmp); + continue; + } + + if(mtmp->mtame && tmoves > 250) { + mtmp->mtame = 0; + mtmp->mpeaceful = 0; + } + + newhp = mtmp->mhp + + (index(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((char *) 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((char *) trap); + fobj = restobjchn(fd); + billobjs = restobjchn(fd); + rest_engravings(fd); + mread(fd, (char *)rooms, sizeof(rooms)); + mread(fd, (char *)doors, sizeof(doors)); +#ifndef NOWORM + mread(fd, (char *)wsegs, sizeof(wsegs)); + for(tmp = 1; tmp < 32; tmp++) if(wsegs[tmp]){ + wheads[tmp] = wsegs[tmp] = wtmp = newseg(); + while(1) { + 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 /**/ +} + +mread(fd, buf, len) +register fd; +register char *buf; +register unsigned len; +{ + register int rlen; + extern boolean restoring; + + rlen = read(fd, buf, (int) len); + if(rlen != len){ + pline("Read %d instead of %u bytes.\n", rlen, len); + if(restoring) { + (void) unlink(SAVEF); + error("Error restoring old game."); + } + panic("Error reading level file."); + } +} + +mklev() +{ + extern boolean in_mklev; + + if(getbones()) return; + + in_mklev = TRUE; + makelevel(); + in_mklev = FALSE; +} + + +#ifdef DGK +swapin_file(lev) { + char to[PATHLEN], from[PATHLEN]; + + sprintf(from, "%s%s", permbones, alllevels); + sprintf(to, "%s%s", levels, alllevels); + name_file(from, lev); + name_file(to, lev); + while (fileinfo[lev].size > freediskspace(to)) + if (!swapout_oldest()) + return FALSE; +#ifdef WIZARD + if (wizard) { + pline("Swapping in `%s'", from); + fflush(stdout); + } +#endif + copyfile(from, to); + (void) unlink(from); + fileinfo[lev].where = ACTIVE; + return TRUE; +} + + +swapout_oldest() { + char to[PATHLEN], from[PATHLEN]; + int i, oldest; + long oldtime; + + if (!ramdisk) + return FALSE; + for (i = 1, oldtime = 0, oldest = 0; i <= maxdlevel; i++) + if (fileinfo[i].where == ACTIVE + && (!oldtime || fileinfo[i].time < oldtime)) { + oldest = i; + oldtime = fileinfo[i].time; + } + if (!oldest) + return FALSE; + sprintf(from, "%s%s", levels, alllevels); + sprintf(to, "%s%s", permbones, alllevels); + name_file(from, oldest); + name_file(to, oldest); +#ifdef WIZARD + if (wizard) { + pline("Swapping out `%s'.", from); + fflush(stdout); + } +#endif + copyfile(from, to); + unlink(from); + fileinfo[oldest].where = SWAPPED; + return TRUE; +} + +copyfile(from, to) +char *from, *to; +{ + char buf[BUFSIZ]; + int nfrom, nto, fdfrom, fdto; + + if ((fdfrom = open(from, O_RDONLY | O_BINARY | O_CREAT, FMASK)) < 0) + panic("Can't copy from %s !?", from); + if ((fdto = open(to, O_WRONLY | O_BINARY | O_CREAT, FMASK)) < 0) + panic("Can't copy to %s", to); + do { + nfrom = read(fdfrom, buf, BUFSIZ); + nto = write(fdto, buf, nfrom); + if (nto != nfrom) + panic("Copyfile failed!"); + } while (nfrom == BUFSIZ); + close(fdfrom); + close(fdto); +} +#endif diff --git a/src/main.c b/src/main.c @@ -0,0 +1,494 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* main.c - version 1.0.3 */ + +#include <stdio.h> +#include <signal.h> +#include "hack.h" + +char orgdir[PATHLEN], *getcwd(); + +extern struct permonst mons[CMNUM+2]; +extern char genocided[], fut_geno[]; +extern char *getlogin(), *getenv(); +extern char plname[PL_NSIZ], pl_character[PL_CSIZ]; + +int (*afternmv)(), done1(), (*occupation)(); +int occtime; +char *occtxt; /* defined when occupation != NULL */ + +char SAVEF[FILENAME]; +char *hname = "hack"; +char obuf[BUFSIZ]; /* BUFSIZ is defined in stdio.h */ +int hackpid; /* not used anymore, but kept in for save files */ + +extern char *nomovemsg; +extern long wailmsg; + +main(argc,argv) +int argc; +char *argv[]; +{ + register int fd; + register char *dir; +#ifdef MSDOS + static void moveloop(); /* a helper function for MSC optimizer */ + + /* Save current directory and make sure it gets restored when + * the game is exited. + */ + int (*funcp)(); + + if (getcwd(orgdir, sizeof orgdir) == NULL) { + xputs("hack: current directory path too long\n"); + _exit(1); + } + funcp = exit; /* Kludge to get around LINT_ARGS of signal. + * This will produce a compiler warning, but that's OK. + */ + signal(SIGINT, funcp); /* restore original directory */ +#endif +#ifdef DGK + initoptions(); + if (!hackdir[0]) + (void) strcpy(hackdir, orgdir); + dir = hackdir; +#else + 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 /* DGK */ + + /* + * Now we know the directory containing 'record' and + * may do a prscore(). + */ + if(argc > 1 && !strncmp(argv[1], "-s", 2)) { + chdirx(dir,0); + prscore(argc, argv); + exit(0); + } + + /* + * It seems he really wants to play. + * Remember tty modes, to be restored on exit. + */ + gettty(); + setbuf(stdout,obuf); + setrandom(); + startup(); + cls(); + u.uhp = 1; /* prevent RIP on early quits */ + u.ux = FAR; /* prevent nscr() */ + + /* + * We cannot do chdir earlier, otherwise gethdate will fail. + */ + chdirx(dir,1); + + /* + * Process options. + */ + while(argc > 1 && argv[1][0] == '-'){ + argv++; + argc--; + switch(argv[0][1]){ +#ifdef WIZARD + case 'D': +# ifdef MSDOS + wizard = TRUE; +# else + if(!strcmp(getlogin(), WIZARD)) + wizard = TRUE; + else + printf("Sorry.\n"); +# endif + break; +#endif + case 'u': + if(argv[0][2]) + (void) strncpy(plname, argv[0]+2, sizeof(plname)-1); + else if(argc > 1) { + argc--; + argv++; + (void) strncpy(plname, argv[0], sizeof(plname)-1); + } else + printf("Player name expected after -u\n"); + break; +#ifdef DGK + /* Person does not want to use a ram disk + */ + case 'R': + ramdisk = FALSE; + break; +#endif + default: + /* allow -T for Tourist, etc. */ + (void) strncpy(pl_character, argv[0]+1, + sizeof(pl_character)-1); + + /* printf("Unknown option: %s\n", *argv); */ + } + } + +#ifdef DGK + set_lock_and_bones(); + copybones(FROMPERM); +#endif +#ifdef WIZARD + if (wizard) + (void) strcpy(plname, "wizard"); + else +#endif + if (!*plname) + askname(); + plnamesuffix(); /* strip suffix from name; calls askname() */ + /* again if suffix was whole name */ + /* accepts any suffix */ +#ifdef WIZARD + if(wizard) { + register char *sfoo; +# ifndef DGK + /* lock is set in read_config_file */ + (void) strcpy(lock,plname); +# endif + if(sfoo = getenv("MAGIC")) + while(*sfoo) { + switch(*sfoo++) { + case 'n': (void) srand(*sfoo++); + break; + } + } + if(sfoo = getenv("GENOCIDED")){ + if(*sfoo == '!'){ + register struct permonst *pm = mons; + register char *gp = genocided; + + while(pm < mons+CMNUM+2){ + if(!index(sfoo, pm->mlet)) + *gp++ = pm->mlet; + pm++; + } + *gp = 0; + } else + (void) strcpy(genocided, sfoo); + (void) strcpy(fut_geno, genocided); + } + } +#endif /* WIZARD */ + start_screen(); +#ifdef DGK + strncat(SAVEF, plname, 8); + strcat(SAVEF, ".sav"); + cls(); + if (saveDiskPrompt(1) && ((fd = open(SAVEF, 0)) >= 0)) { +#else + (void) sprintf(SAVEF, "save/%d%s", getuid(), plname); + regularize(SAVEF+5); /* avoid . or / in name */ + if((fd = open(SAVEF,0)) >= 0 && + (uptodate(fd) || unlink(SAVEF) == 666)) { +#endif /* DGK */ + (void) signal(SIGINT,done1); + pline("Restoring old save file..."); + (void) fflush(stdout); + if(!dorecover(fd)) + goto not_recovered; + pline("Hello %s, welcome to %s!", plname, hname); + flags.move = 0; + } else { +not_recovered: +#ifdef DGK + gameDiskPrompt(); +#endif + fobj = fcobj = invent = 0; + fmon = fallen_down = 0; + ftrap = 0; + fgold = 0; + flags.ident = 1; + init_objects(); + u_init(); + + (void) signal(SIGINT,done1); + mklev(); + u.ux = xupstair; + u.uy = yupstair; + (void) inshop(); + setsee(); + flags.botlx = 1; + /* Fix bug with dog not being made because a monster + * was on the level 1 staircase + */ + { + struct monst *mtmp; + + if (mtmp = m_at(u.ux, u.uy)) + mnexto(mtmp); + } + makedog(); + { register struct monst *mtmp; + if(mtmp = m_at(u.ux, u.uy)) mnexto(mtmp); /* riv05!a3 */ + } + seemons(); + docrt(); + + /* give welcome message before pickup messages */ + pline("Hello %s, welcome to %s!", plname, hname); + + 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."); + if(!u.uluck) u.uluck++; + } else if(flags.moonphase == NEW_MOON) { + pline("Be careful! New moon tonight."); + } + + initrack(); + (void) signal(SIGINT, SIG_IGN); +#ifdef MSDOS + /* Help for Microsoft optimizer. Otherwise main is too large -dgk*/ + moveloop(); +} + +static void +moveloop() +{ + char ch; + int abort; +#endif /* MSDOS */ + for(;;) { + if(flags.move) { /* actual time passed */ + + settrack(); + + if(moves%2 == 0 || + (!(Fast & ~INTRINSIC) && (!Fast || rn2(3)))) { + extern struct monst *makemon(); + movemon(); + if(!rn2(70)) + (void) makemon((struct permonst *)0, 0, 0); + } + if(Glib) glibr(); + timeout(); + ++moves; +#ifndef DGK + if(flags.time) flags.botl = 1; +#endif + 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) (void) dosearch(); + gethungry(); + invault(); + amulet(); + } + if(multi < 0) { + if(!++multi){ + pline(nomovemsg ? nomovemsg : + "You can move again."); + nomovemsg = 0; + if(afternmv) (*afternmv)(); + afternmv = 0; + } + } + + find_ac(); + if(!flags.mv || Blind) { + seeobjs(); + seemons(); + nscr(); + } +#ifdef DGK + if(flags.time) flags.botl = 1; +#endif + if(flags.botl || flags.botlx) bot(); + + flags.move = 1; + + if(multi >= 0 && occupation) { +#ifdef DGK + abort = 0; + if (kbhit()) { + if ((ch = getchar()) == ABORT) + abort++; + else + pushch(ch); + } + if (abort || monster_nearby()) + stop_occupation(); + else if ((*occupation)() == 0) + occupation = 0; + if (!(++occtime % 7)) + (void) fflush(stdout); +#else + if (monster_nearby()) + stop_occupation(); + else if ((*occupation)() == 0) + occupation = 0; +#endif + continue; + } + + if(multi > 0) { + 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) { + rhack((char *) 0); + } + if(multi && multi%7 == 0) + (void) fflush(stdout); + } +} + +#ifndef DGK +/* This function is unnecessary and incompatible with the #define + * of glo(x) in config.h -dgk + */ +glo(foo) +register foo; +{ + /* construct the string xlock.n */ + register char *tf; + + tf = lock; + while(*tf && *tf != '.') tf++; + (void) sprintf(tf, ".%d", foo); +} +#endif + +/* + * 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. + */ +askname(){ +register int c,ct; + printf("\nWho are you? "); + (void) fflush(stdout); + ct = 0; + while((c = getchar()) != '\n'){ +#ifdef MSDOS + msmsg("%c", c); +#endif + 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 < sizeof(plname)-1) plname[ct++] = c; + } + plname[ct] = 0; + if(ct == 0) askname(); +} + +/*VARARGS1*/ +impossible(s,x1,x2) +register char *s; +{ + pline(s,x1,x2); + pline("Program in disorder - perhaps you'd better Quit."); +} + +#ifdef CHDIR +chdirx(dir, wr) +char *dir; +boolean wr; +{ + + if(dir && chdir(dir) < 0) { + error("Cannot chdir to %s.", dir); + } + +#ifdef DGK + /* Change the default drive as well. + */ + chdrive(dir); +#endif + + /* 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) { + register fd; + + if(dir == NULL) + dir = "."; + if((fd = open(RECORD, 2)) < 0) { +#ifdef DGK + char tmp[PATHLEN]; + + strcpy(tmp, dir); + append_slash(tmp); + msmsg("Warning: cannot write %s%s\n", tmp, RECORD); + getreturn("to continue"); +#else + printf("Warning: cannot write %s/%s", dir, RECORD); + getret(); +#endif + } else + (void) close(fd); + } +} +#endif /* CHDIR /**/ + +stop_occupation() +{ + if(occupation) { + pline("You stop %s.", occtxt); + occupation = 0; +#ifdef DGK + multi = 0; + pushch(0); +#endif + } +} diff --git a/src/makefile b/src/makefile @@ -0,0 +1,98 @@ +# +# Makefile for PC HACK version 3.6 written using +# Microsoft(tm) C v4.0 +# +# Large memory model, register bug, remove stack probes: +WIZARD= +V = 360 +CFLAGS = -AL -DLINT_ARGS -DVER=$V -Ox -Za + +# The game name +GAME = hack.exe + +# The game directory +GAMEDIR = \games + +# Required libraries +LIBS = ltermcap + +# All object modules +OBJS = decl.obj apply.obj bones.obj cmd.obj do.obj \ + do_name.obj do_wear.obj dog.obj eat.obj end.obj \ + engrave.obj fight.obj hack.obj invent.obj \ + lev.obj main.obj makemon.obj mhitu.obj mklev.obj \ + mkmaze.obj mkobj.obj mkshop.obj mon.obj \ + monst.obj o_init.obj objnam.obj options.obj \ + pager.obj potion.obj pri.obj \ + read.obj rip.obj rumors.obj save.obj \ + search.obj shk.obj shknam.obj steal.obj \ + termcap.obj timeout.obj topl.obj track.obj trap.obj \ + tty.obj unix.obj u_init.obj vault.obj wield.obj \ + wizard.obj worm.obj worn.obj zap.obj \ + version.obj rnd.obj alloc.obj msdos.obj + +# The main target +# +$(GAME) : $(OBJS) + link $(OBJS), $(GAME),, $(LIBS) /NOIG /STACK:4000 /CP:1; + +# Other dependencies +# +decl.obj : hack.h mkroom.h +apply.obj : hack.h edog.h mkroom.h +bones.obj : hack.h +hack.obj : hack.h +cmd.obj : hack.h msdos.h +do.obj : hack.h +do_name.obj : hack.h +do_wear.obj : hack.h +dog.obj : hack.h mfndpos.h edog.h mkroom.h +eat.obj : hack.h +end.obj : hack.h +engrave.obj : hack.h +fight.obj : hack.h +invent.obj : hack.h wseg.h +ioctl.obj : config.h +lev.obj : hack.h mkroom.h wseg.h +main.obj : hack.h +makemon.obj : hack.h +mhitu.obj : hack.h +mklev.obj : hack.h mkroom.h +mkmaze.obj : hack.h mkroom.h +mkobj.obj : hack.h +mkshop.obj : hack.h mkroom.h eshk.h +mon.obj : hack.h mfndpos.h +monst.obj : hack.h eshk.h +o_init.obj : config.h objects.h onames.h +objnam.obj : hack.h +options.obj : config.h hack.h +pager.obj : hack.h +potion.obj : hack.h +pri.obj : hack.h wseg.h +read.obj : hack.h +rip.obj : hack.h +rumors.obj : config.h +save.obj : hack.h +search.obj : hack.h +shk.obj : hack.h mfndpos.h mkroom.h eshk.h +shknam.obj : hack.h +steal.obj : hack.h +termcap.obj : config.h flag.h +timeout.obj : hack.h +topl.obj : hack.h +track.obj : hack.h +trap.obj : hack.h mkroom.h +tty.obj : hack.h msdos.h +unix.obj : hack.h mkroom.h +u_init.obj : hack.h +vault.obj : hack.h mkroom.h +wield.obj : hack.h +wizard.obj : hack.h +worm.obj : hack.h wseg.h +worn.obj : hack.h +zap.obj : hack.h +msdos.obj : msdos.h +hack.h : config.h objclass.h monst.h gold.h trap.h obj.h flag.h rm.h permonst.h onames.h + touch hack.h +objects.h : config.h objclass.h + touch objects.h diff --git a/src/makemon.c b/src/makemon.c @@ -0,0 +1,244 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* makemon.c - version 1.0.2 */ + +#include "hack.h" +extern char fut_geno[]; +extern char *index(); +extern struct obj *mkobj_at(), *mksobj(), *mkobj(); +struct monst zeromonst; +extern boolean in_mklev; + +/* + * 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(ptr,x,y) +register struct permonst *ptr; +{ + register struct monst *mtmp; + register tmp, ct; + boolean anything = (!ptr); + + if(x != 0 || y != 0) if(m_at(x,y)) return((struct monst *) 0); + if(ptr){ + if(index(fut_geno, ptr->mlet)) return((struct monst *) 0); + } else { + ct = CMNUM - strlen(fut_geno); + if(index(fut_geno, 'm')) ct++; /* make only 1 minotaur */ + if(index(fut_geno, '@')) ct++; + if(ct <= 0) return(0); /* no more monsters! */ + 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(index(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; 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) + (void) mkobj_at(0, mtmp->mx, mtmp->my); + } + if(ptr->mlet == ':') { +#ifdef DGK + /* If you're protected with a ring, don't create + * any shape-changing chameleons -dgk + */ + if (Protection_from_shape_changers) + mtmp->cham = 0; + else { + mtmp->cham = 1; + (void) newcham(mtmp, + &mons[dlevel+14+rn2(CMNUM-14-dlevel)]); + } +#else + mtmp->cham = 1; + (void) newcham(mtmp, &mons[dlevel+14+rn2(CMNUM-14-dlevel)]); +#endif + } + if(ptr->mlet == 'I' || ptr->mlet == ';') + mtmp->minvis = 1; + if(ptr->mlet == 'L' || ptr->mlet == 'N' + || (in_mklev && index("&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 enexto(); + coord mm; + register int cnt = rnd(10); + mm.x = x; + mm.y = y; + while(cnt--) { + mm = enexto(mm.x, mm.y); + (void) makemon(ptr, mm.x, mm.y); + } + } +#ifdef DGK + m_initinv(mtmp); +#endif + return(mtmp); +} + +#ifdef DGK +/* Give some monsters an initial inventory to use */ +m_initinv(mtmp) +struct monst *mtmp; +{ + struct obj *otmp; + + switch (mtmp->data->mlet) { + case 'K': + if (!rn2(4)) { + otmp = mksobj(DART); + otmp->quan = 2 + rnd(12); + mpickobj(mtmp, otmp); + } + break; + case 'C': + if (rn2(2)) { + otmp = mksobj(CROSSBOW); + otmp->cursed = rn2(2); + mpickobj(mtmp, otmp); + otmp = mksobj(CROSSBOW_BOLT); + otmp->quan = 2 + rnd(12); + mpickobj(mtmp, otmp); + } + break; + default: + break; + } +} +#endif + +coord +enexto(xx,yy) +register xchar xx,yy; +{ + register 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)] ); +} + +goodpos(x,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) + )); +} + +rloc(mtmp) +struct monst *mtmp; +{ + register tx,ty; + register char ch = mtmp->data->mlet; + +#ifndef NOWORM + if(ch == 'w' && mtmp->mx) return; /* do not relocate worms */ +#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(let,x,y) +char let; +register int x,y; +{ + register int ct; + register 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/src/mfndpos.h b/src/mfndpos.h @@ -0,0 +1,12 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* 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/src/mhitu.c b/src/mhitu.c @@ -0,0 +1,379 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* mhitu.c - version 1.0.3 */ + +#include "hack.h" +extern struct monst *makemon(); + +/* + * mhitu: monster hits you + * returns 1 if monster dies (e.g. 'y', 'F'), 0 otherwise + */ +mhitu(mtmp) +register struct monst *mtmp; +{ + register struct permonst *mdat = mtmp->data; + register 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! */ +#ifdef DGK + /* If the swallowing monster changes into a monster + * that is not capable of swallowing you, you get + * regurgitated - dgk + */ + pline("You get regurgitated!"); + u.ux = mtmp->mx; + u.uy = mtmp->my; + u.uswallow = 0; + u.ustuck = 0; + mnexto(mtmp); + setsee(); + docrt(); + break; +#else + pline("The mysterious monster totally digests you."); + u.uhp = 0; +#endif /* DGK /**/ + } + 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(!index("1&DuxynNF",mdat->mlet)) + tmp = hitu(mtmp,d(mdat->damn,mdat->damd)); + else + tmp = 0; + if(index(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)) return(1); /* he disappeared */ + break; + case '&': + if(!mtmp->cham && !mtmp->mcan && !rn2(13)) { + (void) makemon(PM_DEMON,u.ux,u.uy); + } else { + (void) hitu(mtmp,d(2,6)); + (void) hitu(mtmp,d(2,6)); + (void) hitu(mtmp,rnd(3)); + (void) hitu(mtmp,rnd(3)); + (void) 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': + (void) 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; + /* pline("You get turned to stone!"); */ + /* done_in_by(mtmp); */ + } + } + break; + case 'D': + if(rn2(6) || mtmp->mcan) { + (void) hitu(mtmp,d(3,10)); + (void) hitu(mtmp,rnd(8)); + (void) 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': + (void) hitu(mtmp,d(2, (flags.moonphase == FULL_MOON) ? 3 : 4)); + break; + case 'e': + (void) 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){ + (void) hitu(mtmp,rnd(4)); + (void) 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 { + (void) hitu(mtmp,d(2,6)); + (void) 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 + (void) hitu(mtmp,d(2,4)); + break; + case 'Q': + (void) hitu(mtmp,rnd(2)); + (void) 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); + } + (void) hitu(mtmp,rnd(8)); + (void) hitu(mtmp,rnd(8)); + break; + case 'T': + (void) hitu(mtmp,rnd(6)); + (void) hitu(mtmp,rnd(6)); + break; + case 't': + if(!rn2(5)) rloc(mtmp); + break; + case 'u': + mtmp->mflee = 1; + break; + case 'U': + (void) hitu(mtmp,d(3,4)); + (void) 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': + (void) hitu(mtmp,rnd(5)); + (void) hitu(mtmp,rnd(5)); + (void) hitu(mtmp,rnd(5)); + break; + case 'x': + { register 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': + (void) hitu(mtmp,rnd(6)); + break; + } + if(u.uhp < 1) done_in_by(mtmp); + return(0); +} + +hitu(mtmp,dam) +register struct monst *mtmp; +register dam; +{ + register tmp, res; + + nomul(0); + if(u.uswallow) return(0); + + if(mtmp->mhide && mtmp->mundetected) { + mtmp->mundetected = 0; + if(!Blind) { + register struct obj *obj; + extern char * Xmonnam(); + if(obj = o_at(mtmp->mx,mtmp->my)) + 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/src/mklev.c b/src/mklev.c @@ -0,0 +1,790 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* mklev.c - version 1.0.3 */ + +#include "hack.h" + +extern char *getlogin(), *getenv(); +extern struct monst *makemon(); +extern struct obj *mkobj_at(); +extern struct trap *maketrap(); + +#define somex() ((rand()%(croom->hx-croom->lx+1))+croom->lx) +#define somey() ((rand()%(croom->hy-croom->ly+1))+croom->ly) + +#include "mkroom.h" +#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; +int comp(); +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 */ + +makelevel() +{ + register struct mkroom *croom, *troom; + register unsigned tryct; +#ifdef REGBUG + int x, y; +#else + register x,y; +#endif /* REGBUG /**/ + + 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; + (void) 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)) (void) + makemon((struct permonst *) 0, 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)) { + (void) mkobj_at(0, somex(), somey()); + tryct = 0; + while(!rn2(5)) { + if(++tryct > 100){ + printf("tryct overflow4\n"); + break; + } + (void) 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(); + } + } + +#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(); +} + +makerooms() { +register struct rectangle *rsp; +register 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 */ +} + +addrs(lowx,lowy,hix,hiy) +register int lowx,lowy,hix,hiy; +{ + register struct rectangle *rsp; + register 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); + } +} + +addrsx(lx,ly,hx,hy,discarded) +register int lx,ly,hx,hy; +boolean discarded; /* piece of a discarded area */ +{ + register 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; +} + +comp(x,y) +register struct mkroom *x,*y; +{ + if(x->lx < y->lx) return(-1); + return(x->lx > y->lx); +} + +coord +finddpos(xl,yl,xh,yh) { + coord ff; + register 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] */ +okdoor(x,y) +register x,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); +} + +dodoor(x,y,aroom) +register x,y; +register struct mkroom *aroom; +{ + if(doorindex >= DOORMAX) { + impossible("DOORMAX exceeded?"); + return; + } + if(!okdoor(x,y) && nxcor) + return; + dosdoor(x,y,aroom,rn2(8) ? DOOR : SDOOR); +} + +dosdoor(x,y,aroom,type) +register x,y; +register struct mkroom *aroom; +register type; +{ + register struct mkroom *broom; + register tmp; + + if(!IS_WALL(levl[x][y].typ)) /* avoid SDOORs with '+' as scrsym */ + type = DOOR; + levl[x][y].typ = type; + if(type == DOOR) +#ifdef DGK + levl[x][y].scrsym = symbol.door; +#else + levl[x][y].scrsym ='+'; +#endif /* DGK /**/ + 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() */ +maker(lowx,ddx,lowy,ddy) +schar lowx,ddx,lowy,ddy; +{ + register struct mkroom *croom; + register x, y, hix = lowx+ddx, hiy = lowy+ddy; + register 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; + +#ifdef DGK + for(x = lowx-1; x <= hix+1; x++) + for(y = lowy-1; y <= hiy+1; y += (hiy-lowy+2)) { + levl[x][y].scrsym = symbol.hwall; + 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 = symbol.vwall; + levl[x][y].typ = VWALL; + } + for(x = lowx; x <= hix; x++) + for(y = lowy; y <= hiy; y++) { + levl[x][y].scrsym = symbol.room; + levl[x][y].typ = ROOM; + } + levl[lowx-1][lowy-1].scrsym = symbol.tlcorn; + levl[hix+1][lowy-1].scrsym = symbol.trcorn; + levl[lowx-1][hiy+1].scrsym = symbol.blcorn; + levl[hix+1][hiy+1].scrsym = symbol.brcorn; +#else + 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; + } +#endif /* DGK /**/ + + smeq[nroom] = nroom; + croom++; + croom->hx = -1; + nroom++; + return(1); +} + +makecorridors() { + register 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); + } +} + +join(a,b) +register a,b; +{ + coord cc,tt; + register tx, ty, xx, yy; + register struct rm *crm; + register struct mkroom *croom, *troom; + register 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; +#ifdef DGK + crm->scrsym = symbol.corr; +#else + crm->scrsym = CORR_SYM; +#endif + if(nxcor && !rn2(50)) + (void) 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) { + register 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) { + register 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]; +} + +make_niches() +{ + register int ct = rnd(nroom/2 + 1); + while(ct--) makeniche(FALSE); +} + +makevtele() +{ + makeniche(TRUE); +} + +makeniche(with_trap) +boolean with_trap; +{ + register struct mkroom *aroom; + register struct rm *rm; + register int vct = 8; + coord dd; + register dy,xx,yy; + register struct trap *ttmp; + + if(doorindex < DOORMAX) + while(vct--) { + aroom = &rooms[rn2(nroom-1)]; + if(aroom->rtype != 0) continue; /* not an ordinary room */ + 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; +#ifdef DGK + rm->scrsym = symbol.corr; +#else + rm->scrsym = CORR_SYM; +#endif /* DGK /**/ + if(rn2(7)) + dosdoor(xx, yy, aroom, rn2(5) ? SDOOR : DOOR); + else { + mksobj_at(SCR_TELEPORTATION, xx, yy+dy); + if(!rn2(3)) (void) mkobj_at(0, xx, yy+dy); + } + } + return; + } +} + +/* make a trap somewhere (in croom if mazeflag = 0) */ +mktrap(num,mazeflag,croom) +#ifdef REGBUG +int num,mazeflag; +struct mkroom *croom; +{ + struct trap *ttmp; + int kind,nopierc,nomimic,fakedoor,fakegold,tryct = 0; + xchar mx,my; +#else +register num,mazeflag; +register struct mkroom *croom; +{ + register struct trap *ttmp; + register int kind,nopierc,nomimic,fakedoor,fakegold,tryct = 0; + register xchar mx,my; +#endif /* REGBUG /**/ + extern char fut_geno[]; + + if(!num || num >= TRAPNUM) { + nopierc = (dlevel < 4) ? 1 : 0; + nomimic = (dlevel < 9 || goldseen ) ? 1 : 0; + if(index(fut_geno, 'M')) nomimic = 1; + kind = rn2(TRAPNUM - nopierc - nomimic); + /* note: PIERC = 7, MIMIC = 8, TRAPNUM = 9 */ + } else kind = num; + + if(kind == MIMIC) { + register 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) { + extern coord mazexy(); + 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)) { + mtmp->mimic = 1; + mtmp->mappearance = +#ifdef DGK + fakegold ? '$' : fakedoor ? symbol.door : +#else + fakegold ? '$' : fakedoor ? '+' : +#endif /* DGK /**/ + (mazeflag && rn2(2)) ? AMULET_SYM : + "=/)%?![<>" [ rn2(9) ]; + } + return; + } + + do { + if(++tryct > 200) + return; + if(mazeflag){ + extern coord mazexy(); + 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/src/mkmaze.c b/src/mkmaze.c @@ -0,0 +1,187 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* mkmaze.c - version 1.0.2 */ + +#include "hack.h" +#include "mkroom.h" /* not really used */ +extern struct monst *makemon(); +extern struct permonst pm_wizard; +extern struct obj *mkobj_at(); +extern coord mazexy(); +struct permonst hell_hound = + { "hell hound", 'd', 12, 14, 2, 3, 6, 0 }; + +makemaz() +{ + int x,y; + register 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) { + register 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; + } + (void) mkobj_at(AMULET_SYM, zx, zy); + flags.made_amulet = 1; + walkfrom(zx+4, zy); + if(mtmp = makemon(&hell_hound, zx, zy)) + mtmp->msleep = 1; + if(mtmp = makemon(PM_WIZARD, zx+1, zy)) { + mtmp->msleep = 1; + flags.no_of_wizards = 1; + } + } else { + mm = mazexy(); + zx = mm.x; + zy = mm.y; + walkfrom(zx,zy); + (void) mksobj_at(WAN_WISHING, zx, zy); + (void) 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) { +#ifdef DGK + case HWALL: + levl[x][y].scrsym = symbol.hwall; + break; + case ROOM: + levl[x][y].scrsym = symbol.room; + break; +#else + case HWALL: + levl[x][y].scrsym = '-'; + break; + case ROOM: + levl[x][y].scrsym = '.'; + break; +#endif /* DGK /**/ + } + } + for(x = rn1(8,11); x; x--) { + mm = mazexy(); + (void) mkobj_at(rn2(2) ? GEM_SYM : 0, mm.x, mm.y); + } + for(x = rn1(10,2); x; x--) { + mm = mazexy(); + (void) mkobj_at(ROCK_SYM, mm.x, mm.y); + } + mm = mazexy(); + (void) makemon(PM_MINOTAUR, mm.x, mm.y); + for(x = rn1(5,7); x; x--) { + mm = mazexy(); + (void) makemon((struct permonst *) 0, 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,(struct mkroom *) 0); + mm = mazexy(); + levl[(xupstair = mm.x)][(yupstair = mm.y)].scrsym = '<'; + levl[xupstair][yupstair].typ = STAIRS; + xdnstair = ydnstair = 0; +} + +#ifdef DGK +/* Make the mazewalk iterative by faking a stack. This is needed to + * ensure the mazewalk is successful in the limited stack space of + * the program. This iterative version uses the mimumum amount of stack + * that is totally safe. + */ +walkfrom(x,y) +int x,y; +{ +#define CELLS (ROWNO * COLNO) / 4 /* a maze cell is 4 squares */ + char mazex[CELLS + 1], mazey[CELLS + 1]; /* char's are OK */ + int q, a, dir, pos; + int dirs[4]; + + pos = 1; + mazex[pos] = (char) x; + mazey[pos] = (char) y; + while (pos) { + x = (int) mazex[pos]; + y = (int) mazey[pos]; + levl[x][y].typ = ROOM; + q = 0; + for (a = 0; a < 4; a++) + if(okay(x, y, a)) dirs[q++]= a; + if (!q) + pos--; + else { + dir = dirs[rn2(q)]; + move(&x, &y, dir); + levl[x][y].typ = ROOM; + move(&x, &y, dir); + pos++; + if (pos > CELLS) + panic("Overflow in walkfrom"); + mazex[pos] = (char) x; + mazey[pos] = (char) y; + } + } +} +#else + +walkfrom(x,y) int x,y; { +register int q,a,dir; +int dirs[4]; + levl[x][y].typ = ROOM; + while(1) { + 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); + } +} +#endif /* DGK */ + +move(x,y,dir) +register int *x, *y; +register int dir; +{ + switch(dir){ + case 0: --(*y); break; + case 1: (*x)++; break; + case 2: (*y)++; break; + case 3: --(*x); break; + } +} + +okay(x,y,dir) +int x,y; +register 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(){ + coord mm; + mm.x = 3 + 2*rn2(COLNO/2 - 2); + mm.y = 3 + 2*rn2(ROWNO/2 - 2); + return mm; +} diff --git a/src/mkobj.c b/src/mkobj.c @@ -0,0 +1,148 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* mkobj.c - version 1.0.3 */ + +#include "hack.h" + +char mkobjstr[] = "))[[!!!!????%%%%/=**))[[!!!!????%%%%/=**(%"; +struct obj *mkobj(), *mksobj(); + +struct obj * +mkobj_at(let,x,y) +register let,x,y; +{ + register struct obj *otmp = mkobj(let); + otmp->ox = x; + otmp->oy = y; + otmp->nobj = fobj; + fobj = otmp; + return(otmp); +} + +mksobj_at(otyp,x,y) +register otyp,x,y; +{ + register struct obj *otmp = mksobj(otyp); + otmp->ox = x; + otmp->oy = y; + otmp->nobj = fobj; + fobj = otmp; +} + +struct obj * +mkobj(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(otyp) +register otyp; +{ + register 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 = index("/=!?*", 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); +} + +letter(c) { + return(('@' <= c && c <= 'Z') || ('a' <= c && c <= 'z')); +} + +weight(obj) +register struct obj *obj; +{ +register int wt = objects[obj->otyp].oc_weight; + return(wt ? wt*obj->quan : (obj->quan + 1)/2); +} + +mkgold(num,x,y) +register long num; +{ + register struct gold *gold; + register long amount = (num ? num : 1 + (rnd(dlevel+2) * rnd(30))); + + if(gold = g_at(x,y)) + 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/src/mkroom.h b/src/mkroom.h @@ -0,0 +1,26 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* 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/src/mkshop.c b/src/mkshop.c @@ -0,0 +1,273 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* mkshop.c - version 1.0.3 */ + +#include "hack.h" +#include "mkroom.h" +#include "eshk.h" +#define ESHK ((struct eshk *)(&(shk->mextra[0]))) + +extern struct monst *makemon(); +extern struct obj *mkobj_at(); +extern int nroom; +extern char shtypes[]; /* = "=/)%?!["; 8 types: 7 specialized, 1 mixed */ +schar shprobs[] = { 3,3,5,5,10,10,14,50 }; /* their probabilities */ + +mkshop(){ +register struct mkroom *sroom; +register int sh,sx,sy,i = -1; +register char let; +int roomno; +register struct monst *shk; +#ifdef WIZARD + /* first determine shoptype */ + if(wizard){ + extern char *getenv(); + register 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 == 1) break; + } + + if(i < 0) { /* shoptype not yet determined */ + register int j; + + for(j = rn2(100), i = 0; (j -= shprobs[i])>= 0; i++) + if(!shtypes[i]) break; /* superfluous */ + 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) { + register int j = sroom->doorct; + extern int doorindex; + + 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++){ + register 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; + } + (void) mkobj_at(let, sx, sy); + } +} + +mkzoo(type) +int type; +{ + register struct mkroom *sroom; + register struct monst *mon; + register int sh,sx,sy,i; + int goldlim = 500 * dlevel; + int moct = 0; + struct permonst *morguemon(); + + 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 : (struct permonst *) 0, + 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; + } + } +} + +struct permonst * +morguemon() +{ + extern struct permonst pm_ghost; + register 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); +} + +mkswamp() /* Michiel Huisjes & Fred de Wilde */ +{ + register struct mkroom *sroom; + register int sx,sy,i,eelct = 0; + extern struct permonst pm_eel; + + 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)) { + (void) makemon(PM_EEL, sx, sy); + eelct++; + } + } + } +} + +nexttodoor(sx,sy) +register sx,sy; +{ + register dx,dy; + register 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); +} + +has_dnstairs(sroom) +register struct mkroom *sroom; +{ + return(sroom->lx <= xdnstair && xdnstair <= sroom->hx && + sroom->ly <= ydnstair && ydnstair <= sroom->hy); +} + +has_upstairs(sroom) +register struct mkroom *sroom; +{ + return(sroom->lx <= xupstair && xupstair <= sroom->hx && + sroom->ly <= yupstair && yupstair <= sroom->hy); +} + +isbig(sroom) +register struct mkroom *sroom; +{ + register int area = (sroom->hx - sroom->lx) * (sroom->hy - sroom->ly); + return( area > 20 ); +} + +dist2(x0,y0,x1,y1){ + return((x0-x1)*(x0-x1) + (y0-y1)*(y0-y1)); +} + +sq(a) int a; { + return(a*a); +} diff --git a/src/mon.c b/src/mon.c @@ -0,0 +1,890 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* mon.c - version 1.0.3 */ + +#include "hack.h" +#include "mfndpos.h" +#define NULL (char *) 0 +extern struct monst *makemon(); +extern struct obj *mkobj_at(); + +int warnlevel; /* used by movemon and dochugw */ +long lastwarntime; +int lastwarnlev; +char *warnings[] = { + "white", "pink", "red", "ruby", "purple", "black" +}; + +movemon() +{ + register struct monst *mtmp; + register int fr; + + warnlevel = 0; + + while(1) { + /* 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){ + register char *rr; +# ifdef MSDOS + if (Warning & LEFT_RING && Warning & RIGHT_RING) + rr = "Both your rings glow"; + else if (Warning & RIGHT_RING) + rr = "Your right ring glows"; + else if (Warning & LEFT_RING) + rr = "Your left ring glows"; + else + rr = "Your fingertips glow"; +# else + 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; + } +# endif + pline("%s %s!", rr, warnings[warnlevel]); + lastwarntime = moves; + lastwarnlev = warnlevel; + } + + dmonsfree(); /* remove all dead monsters */ +} + +justswld(mtmp,name) +register struct monst *mtmp; +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(); +} + +youswld(mtmp,dam,die,name) +register struct monst *mtmp; +register dam,die; +char *name; +{ + if(mtmp != u.ustuck) return; + kludge("%s digests you!",name); + u.uhp -= dam; + if(u.uswldtim++ >= die){ /* a3 */ + pline("It totally digests you!"); + u.uhp = -1; + } + if(u.uhp < 1) done_in_by(mtmp); + /* flags.botlx = 1; /* should we show status line ? */ +} + +dochugw(mtmp) register struct monst *mtmp; { +register x = mtmp->mx; +register y = mtmp->my; +register d = dochug(mtmp); +register dd; + if(!d) /* 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(d); +} + +/* returns 1 if monster died moving, 0 otherwise */ +dochug(mtmp) +register struct monst *mtmp; +{ + register struct permonst *mdat; + register tmp, nearby, scared, onscary; + + if(mtmp->cham && !rn2(6)) + (void) 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) || index(MREGEN, mdat->mlet)) && + mtmp->mhp < mtmp->mhpmax) + mtmp->mhp++; + + if(mtmp->mfroz) return(0); /* frozen monsters don't do anything */ + + 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))) && + (!index("NL",mdat->mlet) || !rn2(50)) && + (Aggravate_monster || index("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 && index("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); + onscary = (sengr_at("Elbereth", u.ux, u.uy) || + sobj_at(SCR_SCARE_MONSTER, u.ux, u.uy)); + scared = (nearby && onscary); + if(scared && !mtmp->mflee) { + mtmp->mflee = 1; + mtmp->mfleetim = (rn2(7) ? rnd(10) : rnd(100)); + } + + if(!nearby || + mtmp->mflee || scared || + mtmp->mconf || + (mtmp->minvis && !rn2(3)) || + (index("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); + + /* A bug which prevented fast monsters from hitting you when + * they caught up to you. -dgk + */ + nearby = (dist(mtmp->mx, mtmp->my) < 3); + scared = (nearby && onscary); + if(scared && !mtmp->mflee) { + mtmp->mflee = 1; + mtmp->mfleetim = (rn2(7) ? rnd(10) : rnd(100)); + } + } + + if(!index("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); +} + +m_move(mtmp,after) +register struct monst *mtmp; +{ +#ifdef REGBUG + struct monst *mtmp2; + int nx,ny,omx,omy,appr,nearer,cnt,i,j; +#else + register struct monst *mtmp2; + register nx,ny,omx,omy,appr,nearer,cnt,i,j; +#endif + xchar gx,gy,nix,niy,chcnt; + schar chi; + boolean likegold, likegems, 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) return(2); /* he died */ + if(i == 1) return(0); /* still in trap, so didnt move */ + } + 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 + + /* 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 */ +#ifdef DGK + /* Add arrow and bolt throwing monsters */ + if (index("D1KC", msym)) + if (!inrange(mtmp)) /* inrange returns 1 if OK for mon */ + return 0; /* to move after it zaps or throws */ +#else + if(index("D1", msym)) + inrange(mtmp); +#endif + + 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 || + (index("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')) { + extern coord *gettrack(); + register 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 = (index("LOD", msym) != NULL); + likegems = (index("ODu", msym) != NULL); + likeobjs = mtmp->mhide; +#define SRCHRADIUS 25 + { xchar mind = SRCHRADIUS; /* not too far away */ + register int dd; + if(likegold){ + register 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){ + register 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) : + index(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){ + (void) 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); +} + +mpickgold(mtmp) register struct monst *mtmp; { +register struct gold *gold; + while(gold = g_at(mtmp->mx, mtmp->my)){ + mtmp->mgold += gold->amount; + freegold(gold); + if(levl[mtmp->mx][mtmp->my].scrsym == '$') + newsym(mtmp->mx, mtmp->my); + } +} + +mpickgems(mtmp) register struct monst *mtmp; { +register 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 */ +mfndpos(mon,poss,info,flag) +register struct monst *mon; +coord poss[9]; +int info[9], flag; +{ + register int x,y,nx,ny,cnt = 0,ntyp; + register 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)){ + 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 */ + { register struct trap *ttmp = t_at(nx, ny); + register 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); +} + +dist(x,y) int x,y; { + return((x-u.ux)*(x-u.ux) + (y-u.uy)*(y-u.uy)); +} + +poisoned(string, pname) +register char *string, *pname; +{ + register 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"); + } +} + +mondead(mtmp) +register 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 + monfree(mtmp); +} + +/* called when monster is moved to larger structure */ +replmon(mtmp,mtmp2) +register struct monst *mtmp, *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); +} + +relmon(mon) +register struct monst *mon; +{ + register struct monst *mtmp; + + if(mon == fmon) fmon = fmon->nmon; + else { + for(mtmp = fmon; mtmp->nmon != mon; mtmp = mtmp->nmon) ; + 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 */ + +monfree(mtmp) register struct monst *mtmp; { + mtmp->nmon = fdmon; + fdmon = mtmp; +} + +dmonsfree(){ +register struct monst *mtmp; + while(mtmp = fdmon){ + fdmon = mtmp->nmon; + free((char *) mtmp); + } +} + +unstuck(mtmp) +register 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; + } +} + +killed(mtmp) +register struct monst *mtmp; +{ +#ifdef lint +#define NEW_SCORING +#endif /* lint /**/ + register int tmp,tmp2,nk,x,y; + register struct permonst *mdat = mtmp->data; + extern long newuexp(); + + if(mtmp->cham) mdat = PM_CHAMELEON; + 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) { + extern char fut_geno[]; + u.nr_killed[tmp]++; + if((nk = u.nr_killed[tmp]) > MAXMONNO && + !index(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(index("AcsSDXaeRTVWU&In:P", mdat->mlet)) + tmp += 2*mdat->mlevel; + if(index("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 + if(!letter(tmp) || (!index("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(index("NTVm&",mdat->mlet) || rn2(5)) { + register struct obj *obj2 = mkobj_at(tmp,x,y); + if(cansee(x,y)) + atl(x,y,obj2->olet); + stackobj(obj2); + } +} + +kludge(str,arg) +register char *str,*arg; +{ + if(Blind) { + if(*str == '%') pline(str,"It"); + else pline(str,"it"); + } else pline(str,arg); +} + +rescham() /* force all chameleons to become normal */ +{ + register struct monst *mtmp; + + for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) + if(mtmp->cham) { + mtmp->cham = 0; + (void) newcham(mtmp, PM_CHAMELEON); + } +} + +#ifdef DGK +/* Let the chameleons change again -dgk */ +restartcham() +{ + register struct monst *mtmp; + + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) + if (mtmp->data->mlet == ':') + mtmp->cham = 1; +} +#endif + +newcham(mtmp,mdat) /* make a chameleon look like a new monster */ + /* returns 1 if the monster actually changed */ +register struct monst *mtmp; +register struct permonst *mdat; +{ + register mhp, hpn, hpd; + + if(mdat == mtmp->data) return(0); /* still the same monster */ +#ifndef NOWORM + if(mtmp->wormno) wormdead(mtmp); /* throw tail away */ +#endif + 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; +#ifdef DGK + /* only snakes and scorpions can hide under things -dgk */ + mtmp->mhide = (mdat->mlet == 'S' || mdat->mlet == 's') ? 1 : 0; + if (!mtmp->mhide) + mtmp->mundetected = 0; +#endif +#ifndef NOWORM + if(mdat->mlet == 'w' && getwn(mtmp)) initworm(mtmp); + /* perhaps we should clear mtmp->mtame here? */ +#endif + unpmon(mtmp); /* necessary for 'I' and to force pmon */ + pmon(mtmp); + return(1); +} + +mnexto(mtmp) /* Make monster mtmp next to you (if possible) */ +struct monst *mtmp; +{ + extern coord enexto(); + coord mm; + mm = enexto(u.ux, u.uy); + mtmp->mx = mm.x; + mtmp->my = mm.y; + pmon(mtmp); +} + +ishuman(mtmp) register struct monst *mtmp; { + return(mtmp->data->mlet == '@'); +} + +setmangry(mtmp) register 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 procent correct: now a snake may hide under an + invisible object */ +canseemon(mtmp) +register struct monst *mtmp; +{ + return((!mtmp->minvis || See_invisible) + && (!mtmp->mhide || !o_at(mtmp->mx,mtmp->my)) + && cansee(mtmp->mx, mtmp->my)); +} diff --git a/src/monst.c b/src/monst.c @@ -0,0 +1,76 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* monst.c - version 1.0.2 */ + +#include "hack.h" +#include "eshk.h" +extern char plname[PL_NSIZ]; + +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 +}; +struct permonst pm_eel = { "giant eel", ';', 15, 6, -3, 3, 6, 0 }; diff --git a/src/monst.h b/src/monst.h @@ -0,0 +1,60 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* monst.h - version 1.0.2 */ + +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) (struct monst *) alloc((unsigned)(xl) + sizeof(struct monst)) + +extern struct monst *fmon; +extern struct monst *fallen_down; +struct monst *m_at(); + +/* 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/src/msdos.c b/src/msdos.c @@ -0,0 +1,758 @@ +/* An assortment of MSDOS functions. + */ + +#include <stdio.h> +#include "hack.h" + +#ifdef MSDOS +# include <dos.h> + +void +flushout() +{ + (void) fflush(stdout); +} + +getuid() { + return 1; +} + +char * +getlogin() { + return ((char *) NULL); +} + +tgetch() { + char ch, popch(); + static char DOSgetch(), BIOSgetch(); + + if (!(ch = popch())) { +# ifdef DGK + /* BIOSgetch can use the numeric key pad on IBM compatibles. */ + if (flags.IBMBIOS) + ch = BIOSgetch(); + else +# endif + ch = DOSgetch(); + } + return ((ch == '\r') ? '\n' : ch); +} + +# define DIRECT_INPUT 0x7 +static char +DOSgetch() { + union REGS regs; + + regs.h.ah = DIRECT_INPUT; + intdos(&regs, &regs); + if (!regs.h.al) { /* an extended code -- not yet supported */ + regs.h.ah = DIRECT_INPUT; + intdos(&regs, &regs); /* eat the next character */ + regs.h.al = 0; /* and return a 0 */ + } + return (regs.h.al); +} + + +# ifdef DGK +# include <ctype.h> +# include <fcntl.h> + +# define Sprintf (void) sprintf +# define PATHSEP ';' + + +static char * +getcomspec(warn) { + return getenv("COMSPEC"); +} + +# ifdef SHELL +# include <process.h> +dosh() { + extern char orgdir[]; + char *comspec; + + if (comspec = getcomspec()) { + settty("To return to HACK, type \"exit\" at the DOS prompt.\n"); + chdirx(orgdir, 0); + if (spawnl(P_WAIT, comspec, comspec, NULL) < 0) { + printf("\nCan't spawn %s !\n", comspec); + flags.toplin = 0; + more(); + } + chdirx(hackdir, 0); + start_screen(); + docrt(); + } else + pline("No COMSPEC !? Can't exec COMMAND.COM"); + return(0); +} +# endif /* SHELL */ + +/* Normal characters are output when the shift key is not pushed. + * Shift characters are output when either shift key is pushed. + */ +# define KEYPADHI 83 +# define KEYPADLOW 71 +# define iskeypad(x) (KEYPADLOW <= (x) && (x) <= KEYPADHI) +static struct { + char normal, shift; + } keypad[KEYPADHI - KEYPADLOW + 1] = { + {'y', 'Y'}, /* 7 */ + {'k', 'K'}, /* 8 */ + {'u', 'U'}, /* 9 */ + {'m', CTRL('P')}, /* - */ + {'h', 'H'}, /* 4 */ + {'g', 'g'}, /* 5 */ + {'l', 'L'}, /* 6 */ + {'p', 'P'}, /* + */ + {'b', 'B'}, /* 1 */ + {'j', 'J'}, /* 2 */ + {'n', 'N'}, /* 3 */ + {'i', 'I'}, /* Ins */ + {'.', ':'} /* Del */ +}; + +/* BIOSgetch gets keys directly with a BIOS call. + */ +# define SHIFT (0x1 | 0x2) +# define KEYBRD_BIOS 0x16 + +static char +BIOSgetch() { + unsigned char scan, shift, ch; + union REGS regs; + + /* Get scan code. + */ + regs.h.ah = 0; + int86(KEYBRD_BIOS, &regs, &regs); + ch = regs.h.al; + scan = regs.h.ah; + + /* Get shift status. + */ + regs.h.ah = 2; + int86(KEYBRD_BIOS, &regs, &regs); + shift = regs.h.al; + + /* If scan code is for the keypad, translate it. + */ + if (iskeypad(scan)) { + if (shift & SHIFT) + ch = keypad[scan - KEYPADLOW].shift; + else + ch = keypad[scan - KEYPADLOW].normal; + } + return ch; +} + +dotogglepickup() { + flags.pickup = !flags.pickup; + pline("Pickup: %s.", flags.pickup ? "ON" : "OFF"); + return (0); +} + + +/* Convert from a symbol to a string for printing object classes + */ +/* Names from objects.h + * 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 }; + */ +extern char obj_symbols[]; +static char *names[] = {"Illegal objects", "Amulets", "Comestibles", "Weapons", + "Tools", "Iron balls", "Chains", "Rocks", "Armor", + "Potions", "Scrolls", "Wands", "Rings", "Gems"}; +char * +let_to_name(let) +char let; +{ + char *pos = index(obj_symbols, let); + extern char *US, *UE; + + /* buffer is not checked for overflow. + */ + static char buf[100]; + + if (pos == NULL) + pos = obj_symbols; + if (!US || !UE) + return names[pos - obj_symbols]; + else { + Sprintf(buf, "%s%s%s", US, names[pos - obj_symbols], UE); + return buf; + } +} + +/* construct the string file.level */ +void +name_file(file, level) +char *file; +int level; +{ + char *tf; + + if (tf = rindex(file, '.')) + Sprintf(tf+1, "%d", level); +} + + +# define FINDFIRST 0x4E00 +# define FINDNEXT 0x4F00 +# define GETDTA 0x2F00 +# define SETFILETIME 0x5701 +# define GETSWITCHAR 0x3700 +# define FREESPACE 0x36 + +static char +switchar() +{ + union REGS regs; + + regs.x.ax = GETSWITCHAR; + intdos(&regs, &regs); + return regs.h.dl; +} + +long +freediskspace(path) +char *path; +{ + union REGS regs; + + regs.h.ah = FREESPACE; + if (path[0] && path[1] == ':') + regs.h.dl = (toupper(path[0]) - 'A') + 1; + else + regs.h.dl = 0; + intdos(&regs, &regs); + if (regs.x.ax == 0xFFFF) + return -1L; /* bad drive number */ + else + return ((long) regs.x.bx * regs.x.cx * regs.x.ax); +} + +/* Functions to get filenames using wildcards + */ +static +findfirst(path) +char *path; +{ + union REGS regs; + struct SREGS sregs; + + regs.x.ax = FINDFIRST; + regs.x.cx = 0; /* normal files */ + regs.x.dx = FP_OFF(path); + sregs.ds = FP_SEG(path); + intdosx(&regs, &regs, &sregs); + return !regs.x.cflag; +} + +static +findnext() { + union REGS regs; + + regs.x.ax = FINDNEXT; + intdos(&regs, &regs); + return !regs.x.cflag; +} + +/* Get disk transfer area */ +static char * +getdta() { + union REGS regs; + struct SREGS sregs; + char *ret; + + regs.x.ax = GETDTA; + intdosx(&regs, &regs, &sregs); + FP_OFF(ret) = regs.x.bx; + FP_SEG(ret) = sregs.es; + return ret; +} + +long +filesize(file) +char *file; +{ + char *dta; + + if (findfirst(file)) { + dta = getdta(); + return (* (long *) (dta + 26)); + } else + return -1L; +} + +void +eraseall(path, files) +char *path, *files; +{ + char *getdta(), *dta, buf[PATHLEN]; + + dta = getdta(); + Sprintf(buf, "%s%s", path, files); + if (findfirst(buf)) + do { + Sprintf(buf, "%s%s", path, dta + 30); + (void) unlink(buf); + } while (findnext()); +} + +/* Rewritten for version 3.3 to be faster + */ +void +copybones(mode) { + char from[PATHLEN], to[PATHLEN], last[13], copy[8]; + char *frompath, *topath, *dta, *comspec; + int status; + long fs; + extern saveprompt; + + if (!ramdisk) + return; + + /* Find the name of the last file to be transferred + */ + frompath = (mode != TOPERM) ? permbones : levels; + dta = getdta(); + last[0] = '\0'; + Sprintf(from, "%s%s", frompath, allbones); + if (findfirst(from)) + do { + strcpy(last, dta + 30); + } while (findnext()); + + topath = (mode == TOPERM) ? permbones : levels; + if (last[0]) { + Sprintf(copy, "%cC copy", switchar()); + + /* Remove any bones files in `to' directory. + */ + eraseall(topath, allbones); + + /* Copy `from' to `to' */ + Sprintf(to, "%s%s", topath, allbones); + comspec = getcomspec(); + status =spawnl(P_WAIT, comspec, comspec, copy, from, + to, "> nul", NULL); + } else + return; + + /* See if the last file got there. If so, remove the ramdisk bones + * files. + */ + Sprintf(to, "%s%s", topath, last); + if (findfirst(to)) { + if (mode == TOPERM) + eraseall(frompath, allbones); + return; + } + + /* Last file didn't get there. + */ + Sprintf(to, "%s%s", topath, allbones); + msmsg("Cannot copy `%s' to `%s' -- %s\n", from, to, + (status < 0) ? "can't spawn COMSPEC !" : + (freediskspace(topath) < filesize(from)) ? + "insufficient disk space." : "bad path(s)?"); + if (mode == TOPERM) { + msmsg("Bones will be left in `%s'\n", + *levels ? levels : hackdir); + return; + } else { + /* Remove all bones files on the RAMdisk */ + eraseall(levels, allbones); + playwoRAMdisk(); + } +} + +playwoRAMdisk() { + msmsg("Do you wish to play without a RAMdisk (y/n) ? "); + + /* Set ramdisk false *before* exit'ing (because msexit calls + * copybones) + */ + ramdisk = FALSE; + if (getchar() != 'y') { + settty("Be seeing you ...\n"); + exit(0); + } + set_lock_and_bones(); + return; +} + +saveDiskPrompt(start) { + extern saveprompt; + char buf[BUFSIZ], *bp; + int fd; + + if (saveprompt) { + /* Don't prompt if you can find the save file */ + if ((fd = open(SAVEF, 0)) >= 0) { + (void) close(fd); + return 1; + } + remember_topl(); + home(); + cl_end(); + msmsg("If save file is on a SAVE disk, put that disk in now.\n"); + cl_end(); + msmsg("File name (default `%s'%s) ? ", SAVEF, + start ? "" : ", <Esc> cancels save"); + getlin(buf); + home(); + cl_end(); + curs(1, 2); + cl_end(); + if (!start && *buf == '\033') + return 0; + + /* Strip any whitespace. Also, if nothing was entered except + * whitespace, do not change the value of SAVEF. + */ + for (bp = buf; *bp; bp++) + if (!isspace(*bp)) { + strncpy(SAVEF, bp, PATHLEN); + break; + } + } + return 1; +} + +/* Return 1 if the record file was found */ +static +record_exists() { + int fd; + + if ((fd = open(RECORD, 0)) >= 0) { + close(fd); + return TRUE; + } + return FALSE; +} + +/* Return 1 if the comspec was found */ +static +comspec_exists() { + int fd; + char *comspec; + + if (comspec = getcomspec()) + if ((fd = open(comspec, 0)) >= 0) { + close(fd); + return TRUE; + } + return FALSE; +} + +/* Prompt for game disk, then check for record file. + */ +void +gameDiskPrompt() { + extern saveprompt; + + if (saveprompt) { + if (record_exists() && comspec_exists()) + return; + (void) putchar('\n'); + getreturn("when the GAME disk has been put in"); + } + if (comspec_exists() && record_exists()) + return; + + if (!comspec_exists()) + msmsg("\n\nWARNING: can't find comspec `%s'!\n", getcomspec()); + if (!record_exists()) + msmsg("\n\nWARNING: can't find record file `%s'!\n", RECORD); + msmsg("If the GAME disk is not in, put it in now.\n"); + getreturn("to continue"); +} + +/* Read configuration */ +void +read_config_file() { + char tmp_ramdisk[PATHLEN], tmp_levels[PATHLEN]; + char buf[BUFSZ], *bufp; + FILE *fp, *fopenp(), *fopen(); + extern char plname[]; + extern int saveprompt; + + tmp_ramdisk[0] = 0; + tmp_levels[0] = 0; + if ((fp = fopen(configfile, "r")) == NULL + && (fp = fopenp(configfile, "r", NULL)) == NULL) { + msmsg("Warning: no configuration file!\n"); + getreturn("to continue"); + return; + } + while (fgets(buf, BUFSZ, fp)) { + if (*buf == '#') + continue; + + /* remove trailing whitespace + */ + bufp = index(buf, '\n'); + while (bufp > buf && isspace(*bufp)) + bufp--; + if (bufp == buf) + continue; /* skip all-blank lines */ + else + *(bufp + 1) = 0; /* 0 terminate line */ + + /* find the '=' */ + if (!(bufp = strchr(buf, '='))) { + msmsg("Bad option line: '%s'\n", buf); + getreturn("to continue"); + continue; + } + + /* skip whitespace between '=' and value */ + while (isspace(*++bufp)) + ; + + /* Go through possible variables */ + if (!strncmp(buf, "HACKDIR", 4)) { + strncpy(hackdir, bufp, PATHLEN); + + } else if (!strncmp(buf, "RAMDISK", 3)) { + strncpy(tmp_ramdisk, bufp, PATHLEN); + + } else if (!strncmp(buf, "LEVELS", 4)) { + strncpy(tmp_levels, bufp, PATHLEN); + + } else if (!strncmp(buf, "OPTIONS", 4)) { + parseoptions(bufp, TRUE); + if (plname[0]) /* If a name was given */ + plnamesuffix(); /* set the character class */ + + } else if (!strncmp(buf, "SAVE", 4)) { + char *ptr; + if (ptr = index(bufp, ';')) { + *ptr = '\0'; + if (*(ptr+1) == 'n' || *(ptr+1) == 'N') + saveprompt = FALSE; + } + (void) strncpy(SAVEF, bufp, PATHLEN); + append_slash(SAVEF); + + } else if (!strncmp(buf, "GRAPHICS", 4)) { + struct symbols s; + + if (sscanf(bufp, "%u%u%u%u%u%u%u%u%u", &s.vwall, + &s.hwall, &s.tlcorn, &s.trcorn, &s.blcorn, + &s.brcorn, &s.door, &s.room, &s.corr) == 9) + symbol = s; + else { + msmsg("GRAPHICS did not contain 9 values\n"); + getreturn("to continue"); + } + } else { + msmsg("Bad option line: '%s'\n", buf); + getreturn("to continue"); + } + } + fclose(fp); + + strcpy(permbones, tmp_levels); + if (tmp_ramdisk[0]) { + strcpy(levels, tmp_ramdisk); + if (strcmpi(permbones, levels)) /* if not identical */ + ramdisk = TRUE; + } else + strcpy(levels, tmp_levels); + strcpy(bones, levels); +} + +FILE * +fopenp(name, mode, pathname) +char *name, *mode, *pathname; +{ + char buffer[BUFSIZ], *buf, *bufp, *pathp, *getenv(), lastch; + FILE *fp; + + /* If pathname is given, use it instead of buf so the calling + * process knows the path we found name under + */ + if (pathname) + buf = pathname; + else + buf = buffer; + + /* Try the default directory first. If the file can't be opened, + * start looking along the path. + */ + pathp = getenv("PATH"); + while (pathp && *pathp) { + bufp = buf; + while (*pathp && *pathp != PATHSEP) + lastch = *bufp++ = *pathp++; + if (lastch != '\\' && lastch != '/') + *bufp++ = '\\'; + strcpy(bufp, name); + if (fp = fopen(buf, mode)) + return fp; + if (*pathp) + pathp++; + } + return NULL; +} + +/* Set names for bones[] and lock[] + */ +void +set_lock_and_bones() { + if (!ramdisk) { + strcpy(levels, permbones); + strcpy(bones, permbones); + } + append_slash(permbones); + append_slash(levels); + append_slash(bones); + strcat(bones, allbones); + strcpy(lock, levels); + strcat(lock, alllevels); +} + +/* Add a backslash to any name not ending in /, \ or : There must + * be room for the \ + */ +void +append_slash(name) +char *name; +{ + char *ptr; + + if (!*name) + return; + ptr = name + (strlen(name) - 1); + if (*ptr != '\\' && *ptr != '/' && *ptr != ':') { + *++ptr = '\\'; + *++ptr = '\0'; + } +} + + +void +getreturn(str) +char *str; +{ + int ch; + + msmsg("Hit <RETURN> %s.", str); + while ((ch = getchar()) != '\n') + ; +} + +void +msmsg(fmt, a1, a2, a3) +char *fmt; +long a1, a2, a3; +{ + printf(fmt, a1, a2, a3); + flushout(); +} + +/* Chdrive() changes the default drive. + */ +#define SELECTDISK 0x0E +void +chdrive(str) +char *str; +{ + char *ptr; + union REGS inregs; + char drive; + + if ((ptr = index(str, ':')) != NULL) { + drive = toupper(*(ptr - 1)); + inregs.h.ah = SELECTDISK; + inregs.h.dl = drive - 'A'; + intdos(&inregs, &inregs); + } +} + +/* Use the IOCTL DOS function call to change stdin and stdout to raw + * mode. For stdin, this prevents MSDOS from trapping ^P, thus + * freeing us of ^P toggling 'echo to printer'. + * Thanks to Mark Zbikowski (markz@microsoft.UUCP). + */ + +# define DEVICE 0x80 +# define RAW 0x20 +# define IOCTL 0x44 +# define STDIN fileno(stdin) +# define STDOUT fileno(stdout) +# define GETBITS 0 +# define SETBITS 1 + +static unsigned old_stdin, old_stdout, ioctl(); + +disable_ctrlP() { + if (!flags.rawio) + return; + old_stdin = ioctl(STDIN, GETBITS, 0); + old_stdout = ioctl(STDOUT, GETBITS, 0); + if (old_stdin & DEVICE) + ioctl(STDIN, SETBITS, old_stdin | RAW); + if (old_stdout & DEVICE) + ioctl(STDOUT, SETBITS, old_stdout | RAW); +} + +enable_ctrlP() { + if (!flags.rawio) + return; + if (old_stdin) + (void) ioctl(STDIN, SETBITS, old_stdin); + if (old_stdout) + (void) ioctl(STDOUT, SETBITS, old_stdout); +} + +static unsigned +ioctl(handle, mode, setvalue) +unsigned setvalue; +{ + union REGS regs; + + regs.h.ah = IOCTL; + regs.h.al = mode; + regs.x.bx = handle; + regs.h.dl = setvalue; + regs.h.dh = 0; /* Zero out dh */ + intdos(&regs, &regs); + return (regs.x.dx); +} + + +# endif /* DGK */ + +/* Chdir back to original directory + */ +# undef exit +void +msexit(code) +{ +# ifdef CHDIR + extern char orgdir[]; +# endif + +# ifdef DGK + flushout(); + enable_ctrlP(); /* in case this wasn't done */ + if (ramdisk) + copybones(TOPERM); +# endif +# ifdef CHDIR + chdir(orgdir); /* chdir, not chdirx */ +# ifdef DGK + chdrive(orgdir); +# endif +# endif + exit(code); +} +#endif /* MSDOS */ diff --git a/src/msdos.h b/src/msdos.h @@ -0,0 +1,55 @@ +/* msdos.h - function declarations for msdos.c */ + +extern char *alllevels, *allbones; +extern char levels[], bones[], permbones[], SAVEF[], hackdir[]; +extern int ramdisk, count_only, in_doagain; + +#define CTRL(ch) (ch & 0x37) +#define ABORT CTRL('A') +#define COUNT 0x1 +#define WRITE 0x2 +#define DOAGAIN 'a' /* Used in tty.c and cmd.c */ +#define LARGEST_INT ((1 << 15) - 1) + +#ifdef LINT_ARGS /* arg. checking enabled */ + +void append_slash(char *); +void chdrive(char *); +int check_then_creat(char *, int); +void copybones(int); +int dosh(); +int dotogglepickup(); +long filesize(char *); +void flushout(); +long freediskspace(char *); +void gameDiskPrompt(); +char * getlogin(); +void getreturn(char *); +char * getenv(); +int getuid(); +char * let_to_name(char); +void msexit(int); +# ifdef MSC30 +void msmsg(char *, ); +# else +void msmsg(char *, ...); +# endif +void name_file(char *, int); +void pushch(char); +void read_config_file(); +#ifdef DGK +int savelev(int, xchar, int); +#endif +int saveDiskPrompt(int); +void set_lock_and_bones(); +int tgetch(); + +#else + +extern long filesize(), freediskspace(); +extern char *getlogin(), *let_to_name(); +extern void append_slash(), chdrive(), copybones(); +extern void gameDiskPrompt(), getreturn(), msexit(), msmsg(), name_file(); +extern void pushch(), read_config_file(), set_lock_and_bones(); + +#endif diff --git a/src/o_init.c b/src/o_init.c @@ -0,0 +1,201 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* o_init.c - version 1.0.3 */ + +#include "config.h" /* for typedefs */ +#include "objects.h" +#include "onames.h" /* for LAST_GEM */ +extern char *index(); + +int +letindex(let) register char let; { +register int i = 0; +register char ch; + while((ch = obj_symbols[i++]) != 0) + if(ch == let) return(i); + return(0); +} + +init_objects(){ +register int i, j, first, last, sum, end, tmp_i; +register char let, *tmp; + /* init base; if probs given check that they add up to 100, + otherwise compute probs; shuffle descriptions */ + end = SIZE(objects); +#ifdef MSDOS + /* Assign indices to all oc_descr_i first */ + for (i = 0; i < end; i++) + objects[i].oc_descr_i = i; +#endif + 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; +#ifdef MSDOS + /* keep track of where the description came from */ + tmp_i = objects[j].oc_descr_i; + objects[j].oc_descr_i = objects[i].oc_descr_i; + objects[i].oc_descr_i = tmp_i; +#endif + } + } + first = last; + } +} + +probtype(let) register char let; { +register int i = bases[letindex(let)]; +register 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); +} + +setgemprobs() +{ + register int j,first; + extern xchar dlevel; + + 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); +} + +oinit() /* level dependent initialization */ +{ + setgemprobs(); +} + +extern long *alloc(); + +savenames(fd) register fd; { +register 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); + } + } +} + +restnames(fd) register fd; { +register int i; +unsigned len; +#ifdef MSDOS + char *oc_descr[NROFOBJECTS + 1], *oc_name; + + mread(fd, (char *) bases, sizeof bases); + + /* Read in objects 1 at a time, correcting oc_name pointer and + * saving pointer to current description. + */ + for (i = 0; i < SIZE(objects); i++) { + oc_name = objects[i].oc_name; + oc_descr[i] = objects[i].oc_descr; + mread(fd, (char *) &objects[i], sizeof (struct objclass)); + objects[i].oc_name = oc_name; + } + + /* Convert from saved indices into pointers */ + for (i = 0; i < SIZE(objects); i++) + objects[i].oc_descr = oc_descr[objects[i].oc_descr_i]; +#else + mread(fd, (char *) bases, sizeof bases); + mread(fd, (char *) objects, sizeof objects); +#endif + for(i=0; i < SIZE(objects); i++) if(objects[i].oc_uname) { + mread(fd, (char *) &len, sizeof len); + objects[i].oc_uname = (char *) alloc(len); + mread(fd, objects[i].oc_uname, len); + } +} + +dodiscovered() /* free after Robert Viduya */ +{ + extern char *typename(); + register int i, end; + int ct = 0; +#ifdef DGK + char class = -1; + extern char *let_to_name(); +#endif + + cornline(0, "Discoveries"); + + end = SIZE(objects); + for (i = 0; i < end; i++) { + if (interesting_to_discover (i)) { + ct++; +#ifdef DGK + if (objects[i].oc_olet != class) { + class = objects[i].oc_olet; + cornline(1, let_to_name(class)); + } +#endif + cornline(1, typename(i)); + } + } + if (ct == 0) { + pline ("You haven't discovered anything yet..."); + cornline(3, (char *) 0); + } else + cornline(2, (char *) 0); + + return(0); +} + +interesting_to_discover(i) +register int i; +{ + return( + objects[i].oc_uname != NULL || + (objects[i].oc_name_known && objects[i].oc_descr != NULL) + ); +} diff --git a/src/obj.h b/src/obj.h @@ -0,0 +1,56 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* 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; +#ifdef DGK + unsigned int owt; + unsigned int quan; /* use oextra for tmp gold objects */ +#else + uchar owt; + uchar quan; +#endif + 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); +#ifdef DGK + Bitfield(no_charge, 1); /* if shopkeeper will not charge for this */ +#endif + 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) (struct obj *) alloc((unsigned)(xl) + sizeof(struct obj)) +#define ONAME(otmp) ((char *) otmp->oextra) +#define OGOLD(otmp) (otmp->oextra[0]) diff --git a/src/objclass.h b/src/objclass.h @@ -0,0 +1,63 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* objclass.h - version 1.0.3 */ + +/* definition of a class of objects */ + +struct objclass { + char *oc_name; /* actual name */ + 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 */ +#ifdef MSDOS + int oc_descr_i; /* where the description comes from */ +#endif +}; + +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: + * ....shk.c: char shtypes[] = "=/)%?!["; + * mklev.c: "=/)%?![<>" + * mkobj.c: char mkobjstr[] = "))[[!!!!????%%%%/=**"; + * apply.c: otmp = getobj("0#%", "put in"); + * eat.c: otmp = getobj("%", "eat"); + * invent.c: if(index("!%?[)=*(0/\"", sym)){ + * invent.c: || index("%?!*",otmp->olet))){ + */ diff --git a/src/objects.h b/src/objects.h @@ -0,0 +1,289 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* objects.h - version 1.0.3 */ + +/* objects have letter " % ) ( 0 _ ` [ ! ? / = * */ +#include "config.h" +#include "objclass.h" +#define NULL (char *)0 + +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 ... - (DART-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/src/objnam.c b/src/objnam.c @@ -0,0 +1,547 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* objnam.c - version 1.0.2 */ + +#include "hack.h" +#define Sprintf (void) sprintf +#define Strcat (void) strcat +#define Strcpy (void) strcpy +#define PREFIX 15 +extern char *eos(); +extern int bases[]; + +char * +strprepend(s,pref) register char *s, *pref; { +register int i = strlen(pref); + if(i > PREFIX) { + pline("WARNING: prefix too short."); + return(s); + } + s -= i; + (void) strncpy(s, pref, i); /* do not copy trailing 0 */ + return(s); +} + +char * +sitoa(a) int a; { +static char buf[13]; + Sprintf(buf, (a < 0) ? "%d" : "+%d", a); + return(buf); +} + +char * +typename(otyp) +register int otyp; +{ +static char buf[BUFSZ]; +register struct objclass *ocl = &objects[otyp]; +register char *an = ocl->oc_name; +register char *dn = ocl->oc_descr; +register char *un = ocl->oc_uname; +register 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(obj) +register struct obj *obj; +{ +static char bufr[BUFSZ]; +register char *buf = &(bufr[PREFIX]); /* leave room for "17 -3 " */ +register int nn = objects[obj->otyp].oc_name_known; +register char *an = objects[obj->otyp].oc_name; +register char *dn = objects[obj->otyp].oc_descr; +register char *un = objects[obj->otyp].oc_uname; +register int pl = (obj->quan != 1); + if(!obj->dknown && !Blind) obj->dknown = 1; /* %% doesnt belong here */ + 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) { + register char *p; + + for(p = buf; *p; p++) { + if(!strncmp(" of ", p, 4)) { + /* pieces of, cloves of, lumps of */ + register 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' && !index(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(obj) +register struct obj *obj; +{ +char prefix[PREFIX]; +register 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 ") && index(vowels, *bp)) + Strcpy(prefix, "an "); + bp = strprepend(bp, prefix); + return(bp); +} + +/* used only in fight.c (thitu) */ +setan(str,buf) +register char *str,*buf; +{ + if(index(vowels,*str)) + Sprintf(buf, "an %s", str); + else + Sprintf(buf, "a %s", str); +} + +char * +aobjnam(otmp,verb) register struct obj *otmp; register char *verb; { +register 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(obj) +register struct obj *obj; +{ + register char *s = doname(obj); + + if('a' <= *s && *s <= 'z') *s -= ('a' - 'A'); + return(s); +} + +char *wrp[] = { "wand", "ring", "potion", "scroll", "gem" }; +char wrpsym[] = { WAND_SYM, RING_SYM, POTION_SYM, SCROLL_SYM, GEM_SYM }; + +struct obj * +readobjnam(bp) register char *bp; { +register char *p; +register int i; +int cnt, spe, spesgn, typ, heavy; +char let; +char *un, *dn, *an; +/* int the = 0; char *oname = 0; */ + cnt = spe = spesgn = typ = heavy = 0; + let = 0; + an = dn = un = 0; + for(p = bp; *p; p++) + if('A' <= *p && *p <= 'Z') *p += 'a'-'A'; + if(!strncmp(bp, "the ", 4)){ +/* the = 1; */ + 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) cnt = 1; /* %% what with "gems" etc. ? */ + + if(*bp == '+' || *bp == '-'){ + spesgn = (*bp++ == '+') ? 1 : -1; + spe = atoi(bp); + while(digit(*bp)) bp++; + while(*bp == ' ') bp++; + } else { + p = rindex(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; +/* oname = p+7; */ + } + 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; i < sizeof(wrpsym); i++) { + register 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)){ + register 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: + { register struct obj *otmp; + extern struct obj *mksobj(); + let = objects[typ].oc_olet; + otmp = mksobj(typ); + if(heavy) + otmp->owt += 15; + if(cnt > 0 && index("%?!*)", 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/src/onames.h b/src/onames.h @@ -0,0 +1,227 @@ +#define STRANGE_OBJECT 0 +#define AMULET_OF_YENDOR 1 +#define FOOD_RATION 2 +#define TRIPE_RATION 3 +#define PANCAKE 4 +#define DEAD_LIZARD 5 +#define FORTUNE_COOKIE 6 +#define CARROT 7 +#define TIN 8 +#define ORANGE 9 +#define APPLE 10 +#define PEAR 11 +#define MELON 12 +#define BANANA 13 +#define CANDY_BAR 14 +#define EGG 15 +#define CLOVE_OF_GARLIC 16 +#define LUMP_OF_ROYAL_JELLY 17 +#define DEAD_HUMAN 18 +#define DEAD_GIANT_ANT 19 +#define DEAD_GIANT_BAT 20 +#define DEAD_CENTAUR 21 +#define DEAD_DRAGON 22 +#define DEAD_FLOATING_EYE 23 +#define DEAD_FREEZING_SPHERE 24 +#define DEAD_GNOME 25 +#define DEAD_HOBGOBLIN 26 +#define DEAD_STALKER 27 +#define DEAD_JACKAL 28 +#define DEAD_KOBOLD 29 +#define DEAD_LEPRECHAUN 30 +#define DEAD_MIMIC 31 +#define DEAD_NYMPH 32 +#define DEAD_ORC 33 +#define DEAD_PURPLE_WORM 34 +#define DEAD_QUASIT 35 +#define DEAD_RUST_MONSTER 36 +#define DEAD_SNAKE 37 +#define DEAD_TROLL 38 +#define DEAD_UMBER_HULK 39 +#define DEAD_VAMPIRE 40 +#define DEAD_WRAITH 41 +#define DEAD_XORN 42 +#define DEAD_YETI 43 +#define DEAD_ZOMBIE 44 +#define DEAD_ACID_BLOB 45 +#define DEAD_GIANT_BEETLE 46 +#define DEAD_COCKATRICE 47 +#define DEAD_DOG 48 +#define DEAD_ETTIN 49 +#define DEAD_FOG_CLOUD 50 +#define DEAD_GELATINOUS_CUBE 51 +#define DEAD_HOMUNCULUS 52 +#define DEAD_IMP 53 +#define DEAD_JAGUAR 54 +#define DEAD_KILLER_BEE 55 +#define DEAD_LEOCROTTA 56 +#define DEAD_MINOTAUR 57 +#define DEAD_NURSE 58 +#define DEAD_OWLBEAR 59 +#define DEAD_PIERCER 60 +#define DEAD_QUIVERING_BLOB 61 +#define DEAD_GIANT_RAT 62 +#define DEAD_GIANT_SCORPION 63 +#define DEAD_TENGU 64 +#define DEAD_UNICORN 65 +#define DEAD_VIOLET_FUNGI 66 +#define DEAD_LONG_WORM 67 +#define DEAD_XAN 68 +#define DEAD_YELLOW_LIGHT 69 +#define DEAD_ZRUTY 70 +#define ARROW 71 +#define SLING_BULLET 72 +#define CROSSBOW_BOLT 73 +#define DART 74 +#define ROCK 75 +#define BOOMERANG 76 +#define MACE 77 +#define AXE 78 +#define FLAIL 79 +#define LONG_SWORD 80 +#define TWO_HANDED_SWORD 81 +#define DAGGER 82 +#define WORM_TOOTH 83 +#define CRYSKNIFE 84 +#define SPEAR 85 +#define BOW 86 +#define SLING 87 +#define CROSSBOW 88 +#define WHISTLE 89 +#define MAGIC_WHISTLE 90 +#define EXPENSIVE_CAMERA 91 +#define ICE_BOX 92 +#define PICK_AXE 93 +#define CAN_OPENER 94 +#define HEAVY_IRON_BALL 95 +#define IRON_CHAIN 96 +#define ENORMOUS_ROCK 97 +#define HELMET 98 +#define PLATE_MAIL 99 +#define SPLINT_MAIL 100 +#define BANDED_MAIL 101 +#define CHAIN_MAIL 102 +#define SCALE_MAIL 103 +#define RING_MAIL 104 +#define STUDDED_LEATHER_ARMOR 105 +#define LEATHER_ARMOR 106 +#define ELVEN_CLOAK 107 +#define SHIELD 108 +#define PAIR_OF_GLOVES 109 +#define POT_RESTORE_STRENGTH 110 +#define POT_BOOZE 111 +#define POT_INVISIBILITY 112 +#define POT_FRUIT_JUICE 113 +#define POT_HEALING 114 +#define POT_PARALYSIS 115 +#define POT_MONSTER_DETECTION 116 +#define POT_OBJECT_DETECTION 117 +#define POT_SICKNESS 118 +#define POT_CONFUSION 119 +#define POT_GAIN_STRENGTH 120 +#define POT_SPEED 121 +#define POT_BLINDNESS 122 +#define POT_GAIN_LEVEL 123 +#define POT_EXTRA_HEALING 124 +#define POT_LEVITATION 125 +#define SCR_MAIL 130 +#define SCR_ENCHANT_ARMOR 131 +#define SCR_DESTROY_ARMOR 132 +#define SCR_CONFUSE_MONSTER 133 +#define SCR_SCARE_MONSTER 134 +#define SCR_BLANK_PAPER 135 +#define SCR_REMOVE_CURSE 136 +#define SCR_ENCHANT_WEAPON 137 +#define SCR_DAMAGE_WEAPON 138 +#define SCR_CREATE_MONSTER 139 +#define SCR_TAMING 140 +#define SCR_GENOCIDE 141 +#define SCR_LIGHT 142 +#define SCR_TELEPORTATION 143 +#define SCR_GOLD_DETECTION 144 +#define SCR_FOOD_DETECTION 145 +#define SCR_IDENTIFY 146 +#define SCR_MAGIC_MAPPING 147 +#define SCR_AMNESIA 148 +#define SCR_FIRE 149 +#define SCR_PUNISHMENT 150 +#define WAN_LIGHT 155 +#define WAN_SECRET_DOOR_DETECTION 156 +#define WAN_CREATE_MONSTER 157 +#define WAN_WISHING 158 +#define WAN_STRIKING 159 +#define WAN_SLOW_MONSTER 160 +#define WAN_SPEED_MONSTER 161 +#define WAN_UNDEAD_TURNING 162 +#define WAN_POLYMORPH 163 +#define WAN_CANCELLATION 164 +#define WAN_TELEPORTATION 165 +#define WAN_MAKE_INVISIBLE 166 +#define WAN_DIGGING 167 +#define WAN_MAGIC_MISSILE 168 +#define WAN_FIRE 169 +#define WAN_SLEEP 170 +#define WAN_COLD 171 +#define WAN_DEATH 172 +#define Adornment u.uprops[0].p_flgs +#define RIN_ADORNMENT 176 +#define Teleportation u.uprops[1].p_flgs +#define RIN_TELEPORTATION 177 +#define Regeneration u.uprops[2].p_flgs +#define RIN_REGENERATION 178 +#define Searching u.uprops[3].p_flgs +#define RIN_SEARCHING 179 +#define See_invisible u.uprops[4].p_flgs +#define RIN_SEE_INVISIBLE 180 +#define Stealth u.uprops[5].p_flgs +#define RIN_STEALTH 181 +#define Levitation u.uprops[6].p_flgs +#define RIN_LEVITATION 182 +#define Poison_resistance u.uprops[7].p_flgs +#define RIN_POISON_RESISTANCE 183 +#define Aggravate_monster u.uprops[8].p_flgs +#define RIN_AGGRAVATE_MONSTER 184 +#define Hunger u.uprops[9].p_flgs +#define RIN_HUNGER 185 +#define Fire_resistance u.uprops[10].p_flgs +#define RIN_FIRE_RESISTANCE 186 +#define Cold_resistance u.uprops[11].p_flgs +#define RIN_COLD_RESISTANCE 187 +#define Protection_from_shape_changers u.uprops[12].p_flgs +#define RIN_PROTECTION_FROM_SHAPE_CHANG 188 +#define Conflict u.uprops[13].p_flgs +#define RIN_CONFLICT 189 +#define Gain_strength u.uprops[14].p_flgs +#define RIN_GAIN_STRENGTH 190 +#define Increase_damage u.uprops[15].p_flgs +#define RIN_INCREASE_DAMAGE 191 +#define Protection u.uprops[16].p_flgs +#define RIN_PROTECTION 192 +#define Warning u.uprops[17].p_flgs +#define RIN_WARNING 193 +#define Teleport_control u.uprops[18].p_flgs +#define RIN_TELEPORT_CONTROL 194 +#define DIAMOND 197 +#define RUBY 198 +#define SAPPHIRE 199 +#define EMERALD 200 +#define TURQUOISE 201 +#define AQUAMARINE 202 +#define TOURMALINE 203 +#define TOPAZ 204 +#define OPAL 205 +#define GARNET 206 +#define AMETHYST 207 +#define AGATE 208 +#define ONYX 209 +#define JASPER 210 +#define JADE 211 +/* #define WORTHLESS_PIECE_OF_BLUE_GLASS 212 */ +/* #define WORTHLESS_PIECE_OF_RED_GLASS 213 */ +/* #define WORTHLESS_PIECE_OF_YELLOW_GLASS 214 */ +/* #define WORTHLESS_PIECE_OF_GREEN_GLASS 215 */ + +#define CORPSE DEAD_HUMAN +#define LAST_GEM (JADE+1) +#define LAST_RING 19 +#define NROFOBJECTS 215 diff --git a/src/options.c b/src/options.c @@ -0,0 +1,336 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* options.c - version 1.0.3 */ + +#include "hack.h" +extern char *eos(); +boolean female; +#ifdef DGK +static boolean set_order; +#endif + +initoptions() +{ + register char *opts; + extern char *getenv(); + + flags.time = flags.nonews = flags.notombstone = flags.end_own = + flags.standout = flags.nonull = FALSE; + flags.invlet_constant = TRUE; + flags.no_rest_on_space = TRUE; + flags.end_top = 5; + flags.end_around = 4; + female = FALSE; /* players are usually male */ + +#ifdef DGK + flags.silent = flags.IBMBIOS = flags.DECRainbow = flags.rawio = FALSE; + flags.confirm = flags.pickup = flags.sortpack = TRUE; + read_config_file(); +#else + if(opts = getenv("HACKOPTIONS")) + parseoptions(opts,TRUE); +#endif +} + +parseoptions(opts, from_env) +register char *opts; +boolean from_env; +{ + register char *op,*op2; + unsigned num; + boolean negated; + + if(op = index(opts, ',')) { + *op++ = 0; + parseoptions(op, from_env); + } + if(op = index(opts, ' ')) { + 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; + } + +#ifndef DGK + if(!strncmp(opts,"standout",4)) { + flags.standout = !negated; + return; + } + + if(!strncmp(opts,"null",4)) { + flags.nonull = negated; + return; + } + + if(!strncmp(opts,"tombstone",4)) { + flags.notombstone = negated; + return; + } +#endif + +#ifdef DGK + if (!strncmp(opts, "sile", 4)) { + flags.silent = !negated; + return; + } + + if (!strncmp(opts, "conf", 4)) { + flags.confirm = !negated; + return; + } + + if (!strncmp(opts, "pick", 4)) { + flags.pickup = !negated; + return; + } + + if (!strncmp(opts, "IBMB", 4)) { + flags.IBMBIOS = !negated; + return; + } + + if (!strncmp(opts, "rawi", 4)) { + if (from_env) + flags.rawio = !negated; + else + pline("'rawio' only settable from %s.", configfile); + return; + } + + if (!strncmp(opts, "DECR", 4)) { + flags.DECRainbow = !negated; + return; + } + + if (!strncmp(opts, "sort", 4)) { + flags.sortpack = !negated; + return; + } + + /* + * the order to list the pack + */ + if (!strncmp(opts,"packorder",4)) { + register char *sp, *tmp; + extern char inv_order[]; + int tmpend; + + op = index(opts,':'); + if(!op) goto bad; + op++; /* skip : */ + + /* Missing characters in new order are filled in at the end + * from inv_order. + */ + for (sp = op; *sp; sp++) + if (!index(inv_order, *sp)) + goto bad; /* bad char in order */ + else if (index(sp + 1, *sp)) + goto bad; /* dup char in order */ + tmp = (char *) alloc(strlen(inv_order) + 1); + (void) strcpy(tmp, op); + for (sp = inv_order, tmpend = strlen(tmp); *sp; sp++) + if (!index(tmp, *sp)) { + tmp[tmpend++] = *sp; + tmp[tmpend] = 0; + } + (void) strcpy(inv_order, tmp); + free(tmp); + set_order = TRUE; + return; + } +#endif + + if(!strncmp(opts,"time",4)) { + flags.time = !negated; + flags.botl = 1; + return; + } + + if(!strncmp(opts,"restonspace",4)) { + flags.no_rest_on_space = negated; + return; + } + +#ifndef DGK + if(!strncmp(opts,"fixinv",4)) { + if(from_env) + flags.invlet_constant = !negated; + else + pline("The fixinvlet option must be in HACKOPTIONS."); + return; + } +#endif + + if(!strncmp(opts,"male",4)) { + female = negated; + return; + } + if(!strncmp(opts,"female",4)) { + female = !negated; + return; + } + + /* name:string */ + if(!strncmp(opts,"name",4)) { + extern char plname[PL_NSIZ]; + if(!from_env) { +#ifdef DGK + pline("'name' only settable from %s.", configfile); +#else + pline("The playername can be set only from HACKOPTIONS."); +#endif + return; + } + op = index(opts,':'); + if(!op) goto bad; + (void) strncpy(plname, op+1, sizeof(plname)-1); + return; + } + + /* endgame:5t[op] 5a[round] o[wn] */ + if(!strncmp(opts,"endgame",3)) { + op = index(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)) ; + if(*op == '/') op++; + } + return; + } +bad: + if(!from_env) { + if(!strncmp(opts, "help", 4)) { + pline("%s%s%s%s", +#ifdef DGK + +"To set options use OPTIONS=<options> in ", configfile, +" or give the command \"O\" followed by the line <options> while playing. ", +"Here <options> is a list of options separated by commas." ); + pline("%s%s", +"Boolean options are confirm, pickup, rawio, silent, sortpack, time, IBMBIOS,", +" and DECRainbow. These can be negated by prefixing them with '!' or \"no\"." ); + pline("%s%s%s", +"The compound options are name, as in OPTIONS=name:Merlin-W, packorder, ", +"which lists the order that items should appear in your pack (the ", +"default is: packorder:\")[%?/=!(*0 ), and endgame." ); + pline("%s%s%s", +"Endgame is followed by a description of which parts of the scorelist ", +"you wish to see. You might for example say: ", +"\"endgame:own scores/5 top scores/4 around my score\"." ); + +#else + +"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'." ); + +#endif /* DGK /**/ + return; + } + pline("Bad option: %s.", opts); + pline("Type \"O help<cr>\" for help."); + return; + } +#ifdef DGK + printf("Bad syntax in OPTIONS in %s.", configfile); +#else + puts("Bad syntax in HACKOPTIONS."); + puts("Use for example:"); + puts( +"HACKOPTIONS=\"!restonspace,notombstone,endgame:own/5 topscorers/4 around me\"" + ); +#endif + getret(); +} + +doset() +{ + char buf[BUFSZ]; +#ifdef DGK + extern char inv_order[]; +#endif + + pline("What options do you want to set? "); + getlin(buf); + if(!buf[0] || buf[0] == '\033') { +#ifdef DGK + (void) strcpy(buf,"OPTIONS="); +#else + (void) strcpy(buf,"HACKOPTIONS="); +#endif +#ifndef DGK + (void) strcat(buf,female ? "female," : "male,"); + if(flags.standout) (void) strcat(buf,"standout,"); + if(flags.nonull) (void) strcat(buf,"nonull,"); +#endif + if(flags.time) (void) strcat(buf,"time,"); +#ifndef DGK + if(flags.notombstone) (void) strcat(buf,"!tombstone,"); + if(flags.invlet_constant) (void) strcat(buf,"fixinv,"); + if(!flags.no_rest_on_space) (void) strcat(buf,"restonspace,"); +#endif +#ifdef DGK + if (flags.confirm) (void) strcat(buf,"confirm,"); + if (set_order){ + (void) strcat(buf, "packorder: "); + (void) strcat(buf, inv_order); + (void) strcat(buf, ","); + } + if (flags.pickup) (void) strcat(buf,"pickup,"); + if (flags.rawio) (void) strcat(buf,"rawio,"); + if (flags.silent) (void) strcat(buf,"silent,"); + if (flags.sortpack) (void) strcat(buf,"sortpack,"); + if (flags.IBMBIOS) (void) strcat(buf,"IBMBIOS,"); + if (flags.DECRainbow) (void) strcat(buf,"DECRainbow,"); +#endif + if(flags.end_top != 5 || flags.end_around != 4 || flags.end_own){ + (void) sprintf(eos(buf), "endgame: %u topscores/%u around me", + flags.end_top, flags.end_around); + if(flags.end_own) (void) strcat(buf, "/own scores"); + } else { + register char *eop = eos(buf); + if(*--eop == ',') *eop = 0; + } + pline(buf); + } else + parseoptions(buf, FALSE); + return(0); +} diff --git a/src/pager.c b/src/pager.c @@ -0,0 +1,305 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* pager.c - version 1.0.3 */ + +/* This file contains the command routine dowhatis() and a pager. */ + +#include <stdio.h> +#include "hack.h" +extern int CO, LI; /* usually COLNO and ROWNO+2 */ +extern char *CD; +extern char quitchars[]; +extern char *getenv(), *getlogin(); +int done1(); + +dowhatis() +{ + FILE *fp; + char bufr[BUFSZ+6]; + register char *buf = &bufr[6], *ep, q; + extern char readchar(); + + if(!(fp = fopen("data","r"))) + pline("Cannot open data file!"); + else { + pline("Specify what? "); + q = readchar(); +#ifdef DGK + if (index(quitchars, q)) + return(0); +#endif + if(q != '\t') + while(fgets(buf,BUFSZ,fp)) + if(*buf == q) { + ep = index(buf, '\n'); + if(ep) *ep = 0; + /* else: bad data file */ + /* Expand tab 'by hand' */ + if(buf[1] == '\t'){ + buf = bufr; + buf[0] = q; + (void) strncpy(buf+1, " ", 7); + } + pline(buf); + if(ep[-1] == ';') { + pline("More info? "); + if(readchar() == 'y') { + page_more(fp,1); /* does fclose() */ + return(0); + } + } + (void) fclose(fp); /* kopper@psuvax1 */ + return(0); + } + pline("I've never heard of such things."); + (void) fclose(fp); + } + return(0); +} + +/* simple pager, also used from dohelp() */ +page_more(fp,strip) +FILE *fp; +int strip; /* nr of chars to be stripped from each line (0 or 1) */ +{ + register char *bufr, *ep; +#ifdef DGK + /* There seems to be a bug in ANSI.SYS The first tab character + * after a clear screen sequence is not expanded correctly. Thus + * expand the tabs by hand -dgk + */ + int tabstop = 8, spaces; + char buf[BUFSIZ], *bufp, *bufrp; + + set_pager(0); + bufr = (char *) alloc((unsigned) CO); + while (fgets(buf, BUFSIZ, fp) && (!strip || *buf == '\t')){ + bufp = buf; + bufrp = bufr; + while (*bufp && *bufp != '\n') { + if (*bufp == '\t') { + spaces = tabstop - (bufrp - bufr) % tabstop; + while (spaces--) + *bufrp++ = ' '; + bufp++; + } else + *bufrp++ = *bufp++; + } + *bufrp = '\0'; +#else + set_pager(0); + bufr = (char *) alloc((unsigned) CO); + bufr[CO-1] = 0; + while(fgets(bufr,CO-1,fp) && (!strip || *bufr == '\t')){ + ep = index(bufr, '\n'); + if(ep) + *ep = 0; +#endif /* DGK /**/ + if(page_line(bufr+strip)) { + set_pager(2); + goto ret; + } + } + set_pager(1); +ret: + free(bufr); + (void) fclose(fp); +} + +static boolean whole_screen = TRUE; +#define PAGMIN 12 /* minimum # of lines for page below level map */ + +set_whole_screen() { /* called in termcap as soon as LI is known */ + whole_screen = (LI-ROWNO-2 <= PAGMIN || !CD); +} + +set_pager(mode) +register 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(); + } + } +} + +page_line(s) /* returns 1 if we should quit */ +register char *s; +{ + extern char morc; + + 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 + */ + +cornline(mode, text) +int mode; +char *text; +{ + static struct line { + struct line *next_line; + char *line_text; + } *texthead, *texttail; + static int maxlen; + static int linect; + register struct line *tl; + + if(mode == 0) { + texthead = 0; + maxlen = 0; + linect = 0; + if(text) { + cornline(1, text); /* title */ + cornline(1, ""); /* blank line */ + } + return; + } + + if(mode == 1) { + register int len; + + if(!text) return; /* superfluous, just to be sure */ + linect++; + len = strlen(text); + if(len > maxlen) + maxlen = len; + tl = (struct line *) + alloc((unsigned)(len + sizeof(struct line) + 1)); + tl->next_line = 0; + tl->line_text = (char *)(tl + 1); + (void) 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(texthead->line_text); + else + if(mode == 2) { + register int curline, lth; + + if(flags.toplin == 1) more(); /* ab@unido */ + 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) { + texthead = tl->next_line; + free((char *) tl); + } +} + +dohelp() +{ + char c; + + pline ("Long or short help? "); + while (((c = readchar ()) != 'l') && (c != 's') && !index(quitchars,c)) + bell (); + if (!index(quitchars, c)) + (void) page_file((c == 'l') ? HELP : SHELP, FALSE); + return(0); +} + +page_file(fnam, silent) /* return: 0 - cannot open fnam; 1 - otherwise */ +register char *fnam; +boolean silent; +{ + FILE *f; /* free after Robert Viduya */ + + if ((f = fopen (fnam, "r")) == (FILE *) 0) { + if(!silent) + pline ("Cannot open %s.", fnam); + return(0); + } + page_more(f, 0); + return(1); +} diff --git a/src/permonst.h b/src/permonst.h @@ -0,0 +1,25 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* permonst.h - version 1.0.2 */ + +struct permonst { + char *mname,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/src/potion.c b/src/potion.c @@ -0,0 +1,386 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* potion.c - version 1.0.3 */ + +#include "hack.h" +extern int float_down(); +extern char *nomovemsg; +extern struct monst youmonst; +extern struct monst *makemon(); + +dodrink() { + register struct obj *otmp,*objs; + register struct monst *mtmp; + register 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) Blind = 1; /* see on next move */ + 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) break; /* > 118 is impossible */ + 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); +} + +pluslvl() +{ + register num; + + pline("You feel more experienced."); + num = rnd(10); + u.uhpmax += num; + u.uhp += num; + if(u.ulevel < 14) { + extern long newuexp(); + + u.uexp = newuexp()+1; + pline("Welcome to experience level %u.", ++u.ulevel); + } + flags.botl = 1; +} + +strange_feeling(obj,txt) +register struct obj *obj; +register char *txt; +{ + if(flags.beginner) + pline("You have a strange feeling for a moment, then it passes."); + else + pline(txt); + if(!objects[obj->otyp].oc_name_known && !objects[obj->otyp].oc_uname) + docall(obj); + useup(obj); +} + +char *bottlenames[] = { + "bottle", "phial", "flagon", "carafe", "flask", "jar", "vial" +}; + +potionhit(mon, obj) +register struct monst *mon; +register struct obj *obj; +{ + extern char *xname(); + register 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(obj)); +} + +potionbreathe(obj) +register 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. + */ +dodip(){ + register 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); +} + +ghost_from_bottle(){ + extern struct permonst pm_ghost; + register 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/src/pri.c b/src/pri.c @@ -0,0 +1,746 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* pri.c - version 1.0.3 */ + +#include <stdio.h> +#include "hack.h" +xchar scrlx, scrhx, scrly, scrhy; /* corners of new area on screen */ + +extern char *hu_stat[]; /* in eat.c */ +extern char *CD; + +swallowed() +{ + 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; + +panic(str,a1,a2,a3,a4,a5,a6) +char *str; +{ + if(panicking++) exit(1); /* avoid loops - this should never happen*/ + cls(); + home(); + puts(" Suddenly, the dungeon collapses."); + fputs(" ERROR: ", stdout); + printf(str,a1,a2,a3,a4,a5,a6); +#ifdef DEBUG +#ifdef UNIX + if(!fork()) + abort(); /* generate core dump */ +#endif /* UNIX /**/ +#endif /* DEBUG /**/ + more(); /* contains a fflush() */ + done("panicked"); +} + +atl(x,y,ch) +register x,y; +{ + register 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); +} + +on_scr(x,y) +register x,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 +*/ + +tmp_at(x,y) schar x,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(); + 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 */ +Tmp_at(x,y) schar x,y; { +static char let; +static xchar cnt; +static coord tc[COLNO]; /* but watch reflecting beams! */ +register 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(); + 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 --- */ + } +} + +setclipped(){ + error("Hack needs a screen of size at least %d by %d.\n", + ROWNO+2, COLNO); +} + +#ifdef DGK +static int multipleAts; /* TRUE if we have many at()'s to do */ +static int DECgraphics; /* The graphics mode toggle */ +#define DECgraphicsON() if (!DECgraphics) (void) putchar('\16'), DECgraphics = 1 +#define DECgraphicsOFF() if (DECgraphics) (void) putchar('\17'), DECgraphics = 0 +#endif + +at(x,y,ch) +register xchar x,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); +#ifdef DGK + if (flags.DECRainbow) { + /* If there are going to be many at()s in a row without + * intervention, only change the graphics mode when the + * character changes between graphic and regular. + */ + if (multipleAts) { + if (ch & 0x80) { + DECgraphicsON(); + putchar(ch ^ 0x80); /* Strip 8th bit */ + } else { + DECgraphicsOFF(); + putchar(ch); + } + /* Otherwise, we don't know how many at()s will be happening + * before printing of normal strings, so change to graphics + * mode when necessary, then change right back. + */ + } else { + if (ch & 0x80) { + DECgraphicsON(); + putchar(ch ^ 0x80); /* Strip 8th bit */ + DECgraphicsOFF(); + } else + putchar(ch); + } + } else +# endif + (void) putchar(ch); + curx++; +} + +prme(){ + if(!Invisible) at(u.ux,u.uy,u.usym); +} + +doredraw() +{ + docrt(); + return(0); +} + +docrt() +{ + register x,y; + register struct rm *room; + register 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 --------------------------------- */ + +# ifdef DGK + /* For DEC Rainbows, we must translate each character. + */ + if (flags.DECRainbow) { + multipleAts = TRUE; + DECgraphicsOFF(); + } +# endif + 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); +# ifdef DGK + if (flags.DECRainbow) { + multipleAts = FALSE; + DECgraphicsOFF(); + } +#endif + scrlx = COLNO; + scrly = ROWNO; + scrhx = scrhy = 0; + flags.botlx = 1; + bot(); +} + +docorner(xmin,ymax) register xmin,ymax; { + register x,y; + register struct rm *room; + register 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 */ + +#ifdef DGK + if (flags.DECRainbow) { + multipleAts = TRUE; + DECgraphicsOFF(); + } +#endif + 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); + } + } + } +#ifdef DGK + if (flags.DECRainbow) { + multipleAts = FALSE; + DECgraphicsOFF(); + } +#endif + if(ymax > ROWNO) { + cornbot(xmin-1); + if(ymax > ROWNO+1 && CD) { + curs(1,ROWNO+3); + cl_eos(); + } + } +} + +curs_on_u(){ + curs(u.ux, u.uy+2); +} + +pru() +{ + if(u.udispl && (Invisible || u.udisx != u.ux || u.udisy != u.uy)) + /* if(! levl[u.udisx][u.udisy].new) */ + 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 +#include "wseg.h" +extern struct wseg *m_atseg; +#endif /* NOWORM /**/ + +/* print a position that is visible for @ */ +prl(x,y) +{ + register struct rm *room; + register struct monst *mtmp; + register 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(x,y) +register xchar x,y; +{ + register struct obj *otmp; + register struct trap *ttmp; + struct rm *room; + register 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; +#ifdef DGK + case HWALL: + tmp = room->scrsym; /* OK for corners only */ + if (!IS_CORNER(tmp)) + tmp = symbol.hwall; + break; + case VWALL: + tmp = symbol.vwall; + break; + case LDOOR: + case DOOR: + tmp = symbol.door; + break; + case CORR: + tmp = symbol.corr; + break; + case ROOM: + if(room->lit || cansee(x,y) || Blind) tmp = symbol.room; + else tmp = ' '; + break; +#else + 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; +#endif +/* + case POOL: + tmp = POOL_SYM; + break; +*/ + default: + tmp = ERRCHAR; + } + return(tmp); +} + +newsym(x,y) +register x,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 */ +mnewsym(x,y) +register x,y; +{ + register 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; + } + } +} + +nosee(x,y) +register x,y; +{ + register struct rm *room; + + if(!isok(x,y)) return; + room = &levl[x][y]; +#ifdef DGK + if(room->scrsym == symbol.room && !room->lit && !Blind) { +#else + if(room->scrsym == '.' && !room->lit && !Blind) { +#endif + room->scrsym = ' '; + room->new = 1; + on_scr(x,y); + } +} + +prl1(x,y) +register x,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); + } +} + +nose1(x,y) +register x,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); + } +} + +vism_at(x,y) +register x,y; +{ + register struct monst *mtmp; + + return((x == u.ux && y == u.uy && !Invisible) + ? 1 : + (mtmp = m_at(x,y)) + ? ((Blind && Telepat) || canseemon(mtmp)) : + 0); +} + +#ifdef NEWSCR +pobj(obj) register struct obj *obj; { +register 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 /**/ + +unpobj(obj) register struct obj *obj; { +/* if(obj->odispl){ + if(!vism_at(obj->odx, obj->ody)) + newsym(obj->odx, obj->ody); + obj->odispl = 0; + } +*/ + if(!vism_at(obj->ox,obj->oy)) + newsym(obj->ox,obj->oy); +} + +seeobjs(){ +register 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); + } +} + +seemons(){ +register 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 /**/ + } +} + +pmon(mon) register struct monst *mon; { +register 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 || Protection_from_shape_changers) + ? mon->data->mlet : mon->mappearance); + mon->mdispl = 1; + mon->mdx = mon->mx; + mon->mdy = mon->my; + } +} + +unpmon(mon) register struct monst *mon; { + if(mon->mdispl){ + newsym(mon->mdx, mon->mdy); + mon->mdispl = 0; + } +} + +nscr() +{ + register x,y; + register 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]; +cornbot(lth) +register int lth; +{ + if(lth < sizeof(oldbot)) { + oldbot[lth] = 0; + flags.botl = 1; + } +} + +bot() +{ +register char *ob = oldbot, *nb = newbot; +register int i; +extern char *eos(); + if(flags.botlx) *ob = 0; + flags.botl = flags.botlx = 0; +#define GOLD_ON_BOTL +#ifdef GOLD_ON_BOTL + (void) sprintf(newbot, + "Level %-2d Gold %-5lu Hp %3d(%d) Ac %-2d Str ", + dlevel, u.ugold, u.uhp, u.uhpmax, u.uac); +#else + (void) 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) + (void) strcat(newbot,"18/**"); + else + (void) sprintf(eos(newbot), "18/%02d",u.ustr-18); + } else + (void) sprintf(eos(newbot), "%-2d ",u.ustr); +#ifdef EXP_ON_BOTL + (void) sprintf(eos(newbot), " Exp %2d/%-5lu ", u.ulevel,u.uexp); +#else + (void) sprintf(eos(newbot), " Exp %2u ", u.ulevel); +#endif /* EXP_ON_BOTL /**/ + (void) strcat(newbot, hu_stat[u.uhs]); + if(flags.time) + (void) sprintf(eos(newbot), " %ld", moves); + if(strlen(newbot) >= COLNO) { + register 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); + (void) putchar(*nb ? *nb : ' '); + curx++; + } + if(*ob) ob++; + if(*nb) nb++; + } + (void) strcpy(oldbot, newbot); +} + +#ifdef WAN_PROBING +mstatusline(mtmp) register 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 /**/ + +cls(){ + if(flags.toplin == 1) + more(); + flags.toplin = 0; + + clear_screen(); + + flags.botlx = 1; +} diff --git a/src/read.c b/src/read.c @@ -0,0 +1,527 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* read.c - version 1.0.3 */ + +#include "hack.h" + +extern struct monst *makemon(); +extern struct obj *mkobj_at(); +int identify(); + +doread() { + register struct obj *scroll; + register boolean confused = (Confusion != 0); + register 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) { + case SCR_ENCHANT_ARMOR: + { extern struct obj *some_armor(); + register 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) { + register 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: + { register int ct = 0; + register 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: + { register 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((char *) uchain); + uball->spe = 0; + uball->owornmask &= ~W_BALL; + uchain = uball = (struct obj *) 0; + } + break; + } + case SCR_CREATE_MONSTER: + { register int cnt = 1; + + if(!rn2(73)) cnt += rnd(4); + if(confused) cnt += 12; + while(cnt--) + (void) makemon(confused ? PM_ACID_BLOB : + (struct permonst *) 0, 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: + { register int i,j; + register int bd = confused ? 5 : 1; + register 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)) + (void) tamedog(mtmp, (struct obj *) 0); + break; + } + case SCR_GENOCIDE: + { extern char genocided[], fut_geno[]; + char buf[BUFSZ]; + register 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(!index(fut_geno, *buf)) + charcat(fut_geno, *buf); + if(!index(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 { + register int uroom = inroom(u.ux, u.uy); + tele(); + if(uroom != inroom(u.ux, u.uy)) known = TRUE; + } + 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) { + register 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 { + register 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: + { register ct = 0, ctu = 0; + register struct obj *obj; + register 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 + ); + return(1); + case SCR_MAGIC_MAPPING: + { register struct rm *lev; + register 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; +#ifdef DGK + lev->scrsym = symbol.corr; +#else + lev->scrsym = CORR_SYM; +#endif + } else + if(num == SDOOR) { + lev->typ = DOOR; +#ifdef DGK + lev->scrsym = symbol.door; +#else + lev->scrsym = '+'; +#endif + /* do sth in doors ? */ + } else if(lev->seen) continue; + if(num != ROOM) { + lev->seen = lev->new = 1; + if(lev->scrsym == ' ' || !lev->scrsym) + newsym(zx,zy); + else + on_scr(zx,zy); + } + } + break; + } + case SCR_AMNESIA: + { register 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: + { register int num; + register 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(index("FY", mtmp->data->mlet)) + mtmp->mhp -= 3*num; /* this might well kill 'F's */ +#ifdef ndef + mtmp->mhp -= num + num + num; +#endif + 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); +} + +identify(otmp) /* also called by newmail() */ +register struct obj *otmp; +{ + objects[otmp->otyp].oc_name_known = 1; + otmp->known = otmp->dknown = 1; + prinv(otmp); + return(1); +} + +litroom(on) +register boolean on; +{ + register num,zx,zy; + + /* 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; + } + 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."); + } + +do_it: + 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; +} + +/* Test whether we may genocide all monsters with symbol ch */ +monstersym(ch) /* arnold@ucsfcgl */ +register char ch; +{ + register struct permonst *mp; + extern struct permonst pm_eel; + + /* + * can't genocide certain monsters + */ + if (index("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/src/readme.txt b/src/readme.txt @@ -0,0 +1,89 @@ + + Welcome to the sources for PC HACK (version 3.6). + +Introduction +------------ +This is a version of the public domain program HACK 1.03 (copyright +Stichting Mathematisch Centrum, Amsterdam, 1984, 1985.) implemented +under MSDOS with the Microsoft(tm) C v4.0 compiler. + +You may copy this version of PC HACK and make any changes you want to +it. You may give it away, but you may not sell it. + +The only major change from version 3.51 is to now use termcap, so there +is a termcap.arc file which has the sources for the Fred Fish termcap +library. You should compile these separately and combine the .obj files +into a library called LTERMCAP.LIB which gets linked in with the HACK obj +files. There is a sample TERMCAP file with a termcap entry for the +IBM monochrome monitor. + + +The sources are in ARC format in HACK351S.ARC. The commands: + + C> arc51 e hack351s * *.* + +will unpack the files. + +With a hard disk system, you should be able to type `make' and the sources +will start to be compiled. This takes a long time. A floppy disk system +does not really have enough storage. + + +Compiling +--------- +The LARGE compiler model is used. To add WIZARD mode, add a -DWIZARD +to the MAKEFILE, or a #define WIZARD to the CONFIG.H file. + +The MAKEFILE included with PC HACK 3.6 sources is for my version of MAKE. +It is very similar to UNIX(tm) `make'. See MAKE.DOC for details. + +Linking +------- +I used the Microsoft 8086 Linker version 3.51 + +To link the *.obj files by hand, the command is: + link @linkfile + +Where the contents of the linkfile (not supplied) should be: + +decl.obj apply.obj bones.obj cmd.obj do.obj + +do_name.obj do_wear.obj dog.obj eat.obj end.obj + +engrave.obj fight.obj hack.obj invent.obj lev.obj + +main.obj makemon.obj mhitu.obj mklev.obj + +mkmaze.obj mkobj.obj mkshop.obj mon.obj monst.obj + +o_init.obj objnam.obj options.obj pager.obj + +potion.obj pri.obj read.obj rip.obj rumors.obj + +save.obj search.obj shk.obj shknam.obj steal.obj + +termcap.obj timeout.obj topl.obj track.obj + +trap.obj tty.obj unix.obj u_init.obj vault.obj + +wield.obj wizard.obj worm.obj worn.obj zap.obj + +version.obj rnd.obj alloc.obj msdos.obj +hack.exe + +ltermcap /NOIG /STACK:4000 /CP:1; + + +Differences from UNIX HACK +-------------------------- +Changes that were introduced to port UNIX HACK to the MSDOS environment +are surrounded with `#ifdef MSDOS', `#endif' directives. + +Other changes I have made are surrounded by `#ifdef DGK', `#endif' +directives. It should be possible to compile these sources without +any of my changes by removing the `#define DGK' line from CONFIG.H. + +Also, functions I have added are mainly restricted to the file msdos.c, +although some of them are in other places (ie. wizard.c) + + +Finally +------- +If you have any questions, contact me at one of: + + Don Kneller + UUCP: ...ucbvax!ucsfcgl!kneller + ARPA: kneller@ucsfcgl.ARPA + BITNET: kneller@ucsfcgl.BITNET + USMAIL: D. G. Kneller + 2 Panoramic Way #204 + Berkeley, CA 94704 diff --git a/src/rip.c b/src/rip.c @@ -0,0 +1,78 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* rip.c - version 1.0.2 */ + +#include <stdio.h> +#include "hack.h" + +extern char plname[]; + +static char *rip[] = { +" ----------", +" / \\", +" / REST \\", +" / IN \\", +" / PEACE \\", +" / \\", +" | |", +" | |", +" | |", +" | |", +" | |", +" | 1001 |", +" *| * * * | *", +" _________)/\\\\_//(\\/(/\\)/\\//\\/|_)_______\n", +0 +}; + +outrip(){ + register char **dp = rip; + register char *dpx; + char buf[BUFSZ]; + register x,y; + + cls(); + (void) strcpy(buf, plname); + buf[16] = 0; + center(6, buf); + (void) sprintf(buf, "%ld AU", u.ugold); + center(7, buf); + (void) sprintf(buf, "killed by%s", + !strncmp(killer, "the ", 4) ? "" : + !strcmp(killer, "starvation") ? "" : + index(vowels, *killer) ? " an" : " a"); + center(8, buf); + (void) strcpy(buf, killer); + if(strlen(buf) > 16) { + register 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); + (void) sprintf(buf, "%4d", getyear()); + center(11, buf); + for(y=8; *dp; y++,dp++){ + x = 0; + dpx = *dp; + while(dpx[x]) { + while(dpx[x] == ' ') x++; + curs(x,y); + while(dpx[x] && dpx[x] != ' '){ + curx++; + (void) putchar(dpx[x++]); + } + } + } + getret(); +} + +center(line, text) int line; char *text; { +register char *ip,*op; + ip = text; + op = &rip[line][28 - ((strlen(text)+1)/2)]; + while(*ip) *op++ = *ip++; +} diff --git a/src/rm.h b/src/rm.h @@ -0,0 +1,72 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* rm.h - version 1.0.2 */ + +/* 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. + */ +#define CORR_SYM '#' +#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. + */ +#ifdef MSDOS +/* Save disk space by using unsigned char's instead of unsigned ints + */ +struct rm { + uchar scrsym; + uchar typ:5; + uchar new:1; + uchar seen:1; + uchar lit:1; +}; +#else +struct rm { + char scrsym; + unsigned typ:5; + unsigned new:1; + unsigned seen:1; + unsigned lit:1; +}; +#endif /* MSDOS /**/ +extern struct rm levl[COLNO][ROWNO]; + +#ifdef DGK +#define ACTIVE 1 +#define SWAPPED 2 + +struct finfo { + int where; + long time; + long size; +}; +extern struct finfo fileinfo[]; +#endif diff --git a/src/rnd.c b/src/rnd.c @@ -0,0 +1,30 @@ +/* rnd.c - version 1.0.2 */ + +#define RND(x) ((rand()>>3) % x) + +rn1(x,y) +register x,y; +{ + return(RND(x)+y); +} + +rn2(x) +register x; +{ + return(RND(x)); +} + +rnd(x) +register x; +{ + return(RND(x)+1); +} + +d(n,x) +register n,x; +{ + register tmp = n; + + while(n--) tmp += RND(x); + return(tmp); +} diff --git a/src/rumors.c b/src/rumors.c @@ -0,0 +1,111 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* rumors.c - version 1.0.3 */ + +#include <stdio.h> +#include "hack.h" /* for RUMORFILE and BSD (index) */ +#define RUMORFILE "rumors" +#ifdef DGK +/* Rumors has been entirely rewritten to speed up the access. This is + * essential when working from floppies. Using fseek() the way that's done + * here means rumors following longer rumors are output more often than those + * following shorter rumors. Also, you may see the same rumor more than once + * in a particular game (although the odds are highly against it), but + * this also happens with real fortune cookies. Besides, a person can + * just read the rumor file if they desire. -dgk + */ +long rumors_size; +extern char *index(); +extern long ftell(); + +outrumor() +{ + char line[COLNO]; + char *endp; + char roomer[FILENAME]; + FILE *rumors; + + if (rumors_size < 0) /* We couldn't open RUMORFILE */ + return; + if(rumors = fopen(RUMORFILE, "r")) { + if (!rumors_size) { /* if this is the first outrumor() */ + fseek(rumors, 0L, 2); + rumors_size = ftell(rumors); + } + fseek(rumors, rand() % rumors_size, 0); + fgets(line, COLNO, rumors); + if (!fgets(line, COLNO, rumors)) { /* at EOF ? */ + fseek(rumors, 0L, 0); /* seek back to start */ + fgets(line, COLNO, rumors); + } + if(endp = index(line, '\n')) *endp = 0; + pline("This cookie has a scrap of paper inside! It reads: "); + pline(line); + fclose(rumors); + } else { + pline("Can't open rumors file!"); + rumors_size = -1; /* don't try to open it again */ + } +} + +#else + +#define CHARSZ 8 /* number of bits in a char */ +extern long *alloc(); +extern char *index(); +int n_rumors = 0; +int n_used_rumors = -1; +char *usedbits; + +init_rumors(rumf) register FILE *rumf; { +register int i; + n_used_rumors = 0; + while(skipline(rumf)) n_rumors++; + rewind(rumf); + i = n_rumors/CHARSZ; + usedbits = (char *) alloc((unsigned)(i+1)); + for( ; i>=0; i--) usedbits[i] = 0; +} + +skipline(rumf) register FILE *rumf; { +char line[COLNO]; + while(1) { + if(!fgets(line, sizeof(line), rumf)) return(0); + if(index(line, '\n')) return(1); + } +} + +outline(rumf) register FILE *rumf; { +char line[COLNO]; +register char *ep; + if(!fgets(line, sizeof(line), rumf)) return; + if((ep = index(line, '\n')) != 0) *ep = 0; + pline("This cookie has a scrap of paper inside! It reads: "); + pline(line); +} + +outrumor(){ +register int rn,i; +register FILE *rumf; + if(n_rumors <= n_used_rumors || + (rumf = fopen(RUMORFILE, "r")) == (FILE *) 0) 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)) { + (void) skipline(rumf); + if(!used(i)) rn--; + i++; + } + usedbits[i/CHARSZ] |= (1 << (i % CHARSZ)); + n_used_rumors++; + outline(rumf); +none: + (void) fclose(rumf); +} + +used(i) register int i; { + return(usedbits[i/CHARSZ] & (1 << (i % CHARSZ))); +} + +#endif /* DGK /**/ diff --git a/src/save.c b/src/save.c @@ -0,0 +1,355 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* save.c - version 1.0.3 */ + +#include "hack.h" +extern char genocided[60]; /* defined in Decl.c */ +extern char fut_geno[60]; /* idem */ +#include <signal.h> + +extern char nul[], pl_character[PL_CSIZ], SAVEF[]; +extern long lseek(); +extern struct obj *restobjchn(); +extern struct monst *restmonchn(); + +dosave(){ + if(dosave0(0)) { + clear_screen(); + settty("Be seeing you ...\n"); + exit(0); + } +} + +/* returns 1 if save successful */ +dosave0(hu) int hu; { + register fd, ofd; + int tmp; /* not register ! */ +#ifdef DGK + long fds, needed; + int mode; + extern long bytes_counted; + extern int saveprompt; +#endif + + (void) signal(SIGINT, SIG_IGN); +#ifdef DGK + if (!saveDiskPrompt(0)) + return 0; + fd = open(SAVEF, O_WRONLY | O_BINARY | O_CREAT, FMASK); +#else + fd = creat(SAVEF, FMASK); +#endif + if(fd < 0) { + if(!hu) pline("Cannot open save file. (Continue or Quit)"); + (void) unlink(SAVEF); /* ab@unido */ + return(0); + } + if(flags.moonphase == FULL_MOON) /* ut-sally!fletcher */ + u.uluck--; /* and unido!ab */ +#ifdef DGK + home(); + cl_end(); + msmsg("Saving: "); +#endif +#ifdef DGK + mode = COUNT; +again: + savelev(fd, dlevel, mode); + /* count_only will be set properly by savelev */ +#else + savelev(fd,dlevel); +#endif + 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); +#ifdef DGK + if (mode == COUNT) { + /* make sure there is enough disk space */ + needed = bytes_counted; + for (tmp = 1; tmp <= maxdlevel; tmp++) + if (tmp != dlevel && fileinfo[tmp].where) + needed += fileinfo[tmp].size + (sizeof tmp); + fds = freediskspace(SAVEF); + if (needed > fds) { + pline("There is insufficient space on SAVE disk."); + pline("Require %ld bytes but only have %ld.", needed, + fds); + flushout(); + (void) close(fd); + (void) unlink(SAVEF); + saveprompt = 0; /* allow a disk change */ + return 0; + } + mode = WRITE; + goto again; + } +#endif + for(tmp = 1; tmp <= maxdlevel; tmp++) { + extern int hackpid; +#ifdef DGK + if (tmp == dlevel || !fileinfo[tmp].where) continue; + if (fileinfo[tmp].where != ACTIVE) + swapin_file(tmp); +#else + extern boolean level_exists[]; + if(tmp == dlevel || !level_exists[tmp]) continue; +#endif + glo(tmp); +#ifdef DGK + msmsg("."); +#endif + if((ofd = open(lock, 0)) < 0) { + if(!hu) pline("Error while saving: cannot read %s.", lock); + (void) close(fd); + (void) unlink(SAVEF); + if(!hu) done("tricked"); + return(0); + } + getlev(ofd, hackpid, tmp); + (void) close(ofd); + bwrite(fd, (char *) &tmp, sizeof tmp); /* level number */ +#ifdef DGK + savelev(fd,tmp,WRITE); /* actual level */ +#else + savelev(fd,tmp); /* actual level */ +#endif + (void) unlink(lock); + } + (void) close(fd); + glo(dlevel); + (void) unlink(lock); /* get rid of current level --jgm */ + glo(0); + (void) unlink(lock); + return(1); +} + +dorecover(fd) +register fd; +{ + register nfd; + int tmp; /* not a register ! */ + unsigned mid; /* idem */ + struct obj *otmp; + extern boolean restoring; +#ifdef DGK + struct flag oldflags; + + oldflags = flags; /* Save flags set in the config file */ +#endif + 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 != getuid()) { /* strange ... */ + (void) close(fd); + (void) unlink(SAVEF); + puts("Saved game was not yours."); + restoring = FALSE; + return(0); + } + mread(fd, (char *) &flags, sizeof(struct flag)); +#ifdef DGK + /* Some config file OPTIONS take precedence over those in save file. + */ + flags.rawio = oldflags.rawio; + flags.DECRainbow = oldflags.DECRainbow; + flags.IBMBIOS = oldflags.IBMBIOS; +#endif + 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); +#ifdef DGK + msmsg("\n"); + cl_end(); + msmsg("You got as far as level %d%s.\n", maxdlevel, + flags.debug ? " in WIZARD mode" : ""); + cl_end(); + msmsg("Restoring: "); +#endif + while(1) { + if(read(fd, (char *) &tmp, sizeof tmp) != sizeof tmp) + break; + getlev(fd, 0, tmp); + glo(tmp); +#ifdef DGK + msmsg("."); + nfd = open(lock, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, FMASK); +#else + nfd = creat(lock, FMASK); +#endif + if (nfd < 0) + panic("Cannot open level file %s!\n", lock); +#ifdef DGK + if (!savelev(nfd, tmp, COUNT | WRITE)) { + + /* The savelev can't proceed because the size required + * is greater than the available disk space. + */ + msmsg("\nNot enough space on `%s' to restore your game.\n", + levels); + + /* Remove levels and bones that may have been created. + */ + (void) close(nfd); + eraseall(levels, alllevels); + eraseall(levels, allbones); + + /* Perhaps the person would like to play without a + * RAMdisk. + */ + if (ramdisk) { + /* PlaywoRAMdisk may not return, but if it does + * it is certain that ramdisk will be 0. + */ + playwoRAMdisk(); + (void) lseek(fd, 0L, 0); /* Rewind save file */ + return dorecover(fd); /* and try again */ + } else { + msmsg("Be seeing you ...\n"); + exit(0); + } + } +#else + savelev(nfd,tmp); +#endif + (void) close(nfd); + } + (void) lseek(fd, 0L, 0); + getlev(fd, 0, 0); + (void) close(fd); + (void) 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) { + register 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; + } + setsee(); /* only to recompute seelx etc. - these weren't saved */ +#ifdef DGK + gameDiskPrompt(); +#endif + docrt(); + restoring = FALSE; + return(1); +} + +struct obj * +restobjchn(fd) +register fd; +{ + register struct obj *otmp, *otmp2; + register struct obj *first = 0; + int xl; +#ifdef lint + /* suppress "used before set" warning from lint */ + otmp2 = 0; +#endif /* lint /**/ + while(1) { + 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(fd) +register fd; +{ + register struct monst *mtmp, *mtmp2; + register struct monst *first = 0; + int xl; + int monsindex; + extern struct permonst li_dog, dog, la_dog, hell_hound, pm_guard; + +#ifdef lint + /* suppress "used before set" warning from lint */ + mtmp2 = 0; +#endif /* lint /**/ + while(1) { + 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++; + monsindex = *((int *)&mtmp->data); + if (monsindex == INDEX_LITTLEDOG) + mtmp->data = &li_dog; + else if (monsindex == INDEX_DOG) + mtmp->data = &dog; + else if (monsindex == INDEX_LARGEDOG) + mtmp->data = &la_dog; + else if (monsindex == INDEX_HELLHOUND) + mtmp->data = &hell_hound; + else if (monsindex == INDEX_GUARD) + mtmp->data = &pm_guard; + else if (monsindex < 0) { + msmsg("Monster index %d in restmonchn\n", monsindex); + goto next; + } else + mtmp->data = &mons[monsindex]; + if(mtmp->minvent) + mtmp->minvent = restobjchn(fd); +next: + mtmp2 = mtmp; + } + if(first && mtmp2->nmon){ + impossible("Restmonchn: error reading monchn."); + mtmp2->nmon = 0; + } + return(first); +} diff --git a/src/search.c b/src/search.c @@ -0,0 +1,141 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* search.c - version 1.0.3 */ + +#include "hack.h" + +extern struct monst *makemon(); + +findit() /* returns number of things found */ +{ + int num; + register xchar zx,zy; + register struct trap *ttmp; + register 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; +#ifdef DGK + atl(zx, zy, symbol.door); +#else + atl(zx, zy, '+'); +#endif /* DGK /**/ + num++; + } else if(levl[zx][zy].typ == SCORR) { + levl[zx][zy].typ = CORR; +#ifdef DGK + atl(zx, zy, symbol.corr); +#else + atl(zx, zy, CORR_SYM); +#endif /* DGK /**/ + num++; + } else if(ttmp = t_at(zx, zy)) { + if(ttmp->ttyp == PIERC){ + (void) 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)) if(mtmp->mimic){ + seemimic(mtmp); + num++; + } + } + return(num); +} + +dosearch() +{ + register xchar x,y; + register struct trap *trap; + register 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); + (void) makemon(PM_PIERCER,x,y); + return(1); + } + trap->tseen = 1; + if(!vism_at(x,y)) atl(x,y,'^'); + } + } + } + return(1); +} + +doidtrap() { +register struct trap *trap; +register 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); +} + +wakeup(mtmp) +register struct monst *mtmp; +{ + mtmp->msleep = 0; + setmangry(mtmp); + if(mtmp->mimic) seemimic(mtmp); +} + +/* NOTE: we must check if(mtmp->mimic) before calling this routine */ +seemimic(mtmp) +register struct monst *mtmp; +{ + mtmp->mimic = 0; + mtmp->mappearance = 0; + unpmon(mtmp); + pmon(mtmp); +} diff --git a/src/shk.c b/src/shk.c @@ -0,0 +1,998 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* shk.c - version 1.0.3 */ + +#include "hack.h" +#include "mfndpos.h" +#include "mkroom.h" +#include "eshk.h" + +#define ESHK(mon) ((struct eshk *)(&(mon->mextra[0]))) +#define NOTANGRY(mon) mon->mpeaceful +#define ANGRY(mon) !NOTANGRY(mon) + +extern char plname[], *xname(); +extern struct obj *o_on(), *bp_to_obj(); + +/* 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 = 0; +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 */ + +static setpaid(), findshk(), dopayobj(), getprice(), realhunger(); + +/* + 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 char *shopnam[] = { + "engagement ring", "walking cane", "antique weapon", + "delicatessen", "second hand book", "liquor", + "used armor", "assorted antiques" +}; + +char * +shkname(mtmp) /* called in do_name.c */ +register struct monst *mtmp; +{ + return(ESHK(mtmp)->shknam); +} + +shkdead(mtmp) /* called in mon.c */ +register struct monst *mtmp; +{ + register struct eshk *eshk = ESHK(mtmp); + + if(eshk->shoplevel == dlevel) + rooms[eshk->shoproom].rtype = 0; + if(mtmp == shopkeeper) { + setpaid(); + shopkeeper = 0; + bill = (struct bill_x *) -1000; /* dump core when referenced */ + } +} + +replshk(mtmp,mtmp2) +register struct monst *mtmp, *mtmp2; +{ + if(mtmp == shopkeeper) { + shopkeeper = mtmp2; + bill = &(ESHK(shopkeeper)->bill[0]); + } +} + +static +setpaid(){ /* caller has checked that shopkeeper exists */ + /* either we paid or left the shop or he just died */ +register struct obj *obj; +register 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){ + billobjs = obj->nobj; + free((char *) obj); + } + ESHK(shopkeeper)->billct = 0; +} + +static +addupbill(){ /* delivers result in total */ + /* caller has checked that shopkeeper exists */ +register ct = ESHK(shopkeeper)->billct; +register struct bill_x *bp = bill; + total = 0; + while(ct--){ + total += bp->price * bp->bquan; + bp++; + } +} + +inshop(){ +register roomno = inroom(u.ux,u.uy); + + /* Did we just leave a shop? */ + if(u.uinshop && + (u.uinshop != roomno + 1 || shlevel != dlevel || !shopkeeper)) { +#ifdef DGK + /* This is part of the bugfix for shopkeepers not having their + * bill paid. As reported by ab@unido -dgk + */ + if(shopkeeper) { + if(ESHK(shopkeeper)->billct) { + if (inroom(shopkeeper->mx, shopkeeper->my) == + u.uinshop - 1) + 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 = 0; + shlevel = 0; + } + u.uinshop = 0; +#else + 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); + ESHK(shopkeeper)->robbed += total; + setpaid(); + if((rooms[ESHK(shopkeeper)->shoproom].rtype == GENERAL) + == (rn2(3) == 0)) + ESHK(shopkeeper)->following = 1; + } + shopkeeper = 0; + shlevel = 0; + } +#endif /* DGK /**/ + } + + /* Did we just enter a zoo of some kind? */ + if(roomno >= 0) { + register int rt = rooms[roomno].rtype; + register 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; +#ifndef DGK + /* This is part of the bugfix for shopkeepers not having their + * bill paid. As reported by ab@unido -dgk + */ + } else if(inroom(shopkeeper->mx, shopkeeper->my) != roomno) { + u.uinshop = 0; +#endif /* DGK /**/ + } 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; + (void) 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 +findshk(roomno) +register roomno; +{ +register 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 = 0; + shlevel = 0; + bill = (struct bill_x *) -1000; /* dump core when referenced */ +} + +static struct bill_x * +onbill(obj) register struct obj *obj; { +register 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 */ +obfree(obj,merge) register struct obj *obj, *merge; { +register struct bill_x *bp = onbill(obj); +register 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((char *) obj); +} + +static +pay(tmp,shkp) +long tmp; +register 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; + } +} + +dopay(){ +long ltmp; +register struct bill_x *bp; +register struct monst *shkp; +int pass, tmp; + + multi = 0; + (void) 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){ + 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); +#ifdef MSDOS + *bp = bill[--ESHK(shopkeeper)->billct]; +#else + bill[tmp] = bill[--ESHK(shopkeeper)->billct]; +#endif /* MSDOS /**/ + } + } + 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 +dopayobj(bp) register struct bill_x *bp; { +register 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) { + register 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((char *) obj); + } + return(1); +} + +/* routine called after dying (or quitting) with nonempty bill */ +paybill(){ + 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 */ +struct obj * +bp_to_obj(bp) +register struct bill_x *bp; +{ + register struct obj *obj; + register struct monst *mtmp; + register 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)) + break; + for(mtmp = fallen_down; mtmp; mtmp = mtmp->nmon) + if(obj = o_on(id, mtmp->minvent)) + break; + } + return(obj); +} + +/* called in hack.c when we pickup an object */ +addtobill(obj) register struct obj *obj; { +register 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; + } +#ifdef DGK + if (obj->no_charge) { + obj->no_charge = 0; + return; + } +#endif + 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; +} + +splitbill(obj,otmp) register struct obj *obj, *otmp; { + /* otmp has been split off from obj */ +register struct bill_x *bp; +register 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; + + /* addtobill(otmp); */ + 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++; + } +} + +subfrombill(obj) register struct obj *obj; { +long ltmp; +register int tmp; +register struct obj *otmp; +register 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)) != 0){ + 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) + || index("_0", obj->olet)) { + pline("%s seems not interested.", Monnam(shopkeeper)); +#ifdef DGK + obj->no_charge = 1; +#endif + 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); +#ifdef DGK + if (!ltmp) { + pline("%s gladly accepts %s but cannot pay you at present.", + Monnam(shopkeeper), doname(obj)); + obj->no_charge = 1; + } +#else + if(!ltmp) + pline("%s gladly accepts %s but cannot pay you at present.", + Monnam(shopkeeper), doname(obj)); +#endif + else + pline("You sold %s and got %ld gold piece%s.", doname(obj), ltmp, + plur(ltmp)); +} + +doinvbill(mode) +int mode; /* 0: deliver count 1: paged */ +{ + register struct bill_x *bp; + register struct obj *obj; + long totused, thisused; + char buf[BUFSZ]; + + if(mode == 0) { + register 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) { + register 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 */ + (void) sprintf(buf, "x - %s", doname(obj)); + obj->quan = oquan; /* restore value */ + for(cnt = 0; buf[cnt]; cnt++); + while(cnt < 50) + buf[cnt++] = ' '; + (void) sprintf(&buf[cnt], " %5ld zorkmids", thisused); + if(page_line(buf)) + goto quit; + } + } + (void) 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 +getprice(obj) register struct obj *obj; { +register 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); + 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 +realhunger(){ /* not completely foolproof */ +register tmp = u.uhunger; +register 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); +} + +shkcatch(obj) +register struct obj *obj; +{ + register 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 + */ +shk_move(shkp) +register struct monst *shkp; +{ + register struct monst *mtmp; + register struct permonst *mdat = shkp->data; + register xchar gx,gy,omx,omy,nx,ny,nix,niy; + register schar appr,i; + register int udist; + int z; + schar shkroom,chi,chcnt,cnt; + boolean uondoor, satdoor, avoid, badinv; + coord poss[9]; + int info[9]; + struct obj *ib = 0; + + omx = shkp->mx; + omy = shkp->my; + + if((udist = dist(omx,omy)) < 3) { + if(ANGRY(shkp)) { + (void) 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 */ + register 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){ + (void) 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); +} + +online(x,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? */ +follower(mtmp) +register struct monst *mtmp; +{ + return( mtmp->mtame || index("1TVWZi&, ", mtmp->data->mlet) || + (mtmp->isshk && ESHK(mtmp)->following) ); +} + +/* He is digging in the shop. */ +shopdig(fall) +register 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) { + register 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); + } + } +} diff --git a/src/shknam.c b/src/shknam.c @@ -0,0 +1,140 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* shknam.c - version 1.0.2 */ + +#include "hack.h" + +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 +}; + +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 +}; + +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 +}; + +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 +}; + +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 +}; + +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 +}; + +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 +}; + +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; + 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 } +}; + +findname(nampt, let) char *nampt; char let; { +register struct shk_nx *p = shk_nx; +register char **q; +register 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 (void) strcpy(nampt, "Dirk"); + return; + } + (void) strncpy(nampt, q[i], PL_NSIZ); + nampt[PL_NSIZ-1] = 0; +} diff --git a/src/steal.c b/src/steal.c @@ -0,0 +1,203 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* steal.c - version 1.0.3 */ + +#include "hack.h" + +long /* actually returns something that fits in an int */ +somegold(){ + return( (u.ugold < 100) ? u.ugold : + (u.ugold > 10000) ? rnd(10000) : rnd((int) u.ugold) ); +} + +stealgold(mtmp) register struct monst *mtmp; { +register struct gold *gold = g_at(u.ux, u.uy); +register 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 */ +stealarm(){ + register struct monst *mtmp; + register 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 */ +steal(mtmp) +struct monst *mtmp; +{ + register struct obj *otmp; + register tmp; + register 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((struct obj *) 0, 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++; + (void) armoroff(otmp); + otmp->cursed = curssv; + if(multi < 0){ + extern char *nomovemsg; + extern int (*afternmv)(); + /* + multi = 0; + nomovemsg = 0; + afternmv = 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((struct obj *) 0); + if(otmp->olet == CHAIN_SYM) { + impossible("How come you are carrying that chain?"); + } + if(Punished && otmp == uball){ + Punished = 0; + freeobj(uchain); + free((char *) uchain); + uchain = (struct obj *) 0; + uball->spe = 0; + uball = (struct obj *) 0; /* superfluous */ + } + freeinv(otmp); + pline("%s stole %s.", named ? "She" : Monnam(mtmp), doname(otmp)); + mpickobj(mtmp,otmp); + return((multi < 0) ? 0 : 1); +} + +mpickobj(mtmp,otmp) +register struct monst *mtmp; +register struct obj *otmp; +{ + otmp->nobj = mtmp->minvent; + mtmp->minvent = otmp; +} + +stealamulet(mtmp) +register struct monst *mtmp; +{ + register struct obj *otmp; + + for(otmp = invent; otmp; otmp = otmp->nobj) { + if(otmp->olet == AMULET_SYM) { + /* might be an imitation one */ + if(otmp == uwep) setuwep((struct obj *) 0); + 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 */ +relobj(mtmp,show) +register struct monst *mtmp; +register show; +{ + register 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 = (struct obj *) 0; + if(mtmp->mgold || mtmp->data->mlet == 'L') { + register 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/src/termcap.c b/src/termcap.c @@ -0,0 +1,310 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* termcap.c - version 1.0.3 */ + +#include <stdio.h> +#include "config.h" /* for ROWNO and COLNO */ +#include "flag.h" /* for flags.nonull */ +extern char *tgetstr(), *tgoto(), *getenv(); +extern long *alloc(); + +#ifndef lint +extern /* it is defined in libtermlib (libtermcap) */ +#endif /* lint /**/ + short ospeed; /* terminal baudrate; used by tputs */ +static char tbuf[512]; +static char *HO, *CL, *CE, *UP, *CM, *ND, *XD, *BC, *SO, *SE, *TI, *TE; +static char *VS, *VE; +char *US, *UE; +static int SG; +static char PC = '\0'; +char *CD; /* tested in pri.c: docorner() */ +int CO, LI; /* used in pri.c and whatis.c */ + +startup() +{ + register char *term; + register char *tptr; + char *tbufptr, *pc; + + tptr = (char *) alloc(1024); + + tbufptr = tbuf; + + if(!(term = getenv("TERM"))) +# ifdef MSDOS + term = "ibmpc-mono"; +# else + error("Can't get TERM."); +# endif + if(!strncmp(term, "5620", 4)) + flags.nonull = 1; /* this should be a termcap flag */ + if(tgetent(tptr, term) < 1) + error("Unknown terminal type: %s.", term); + TI = tgetstr("ti", &tbufptr); + TE = tgetstr("te", &tbufptr); + VS = VE = ""; + + if(pc = tgetstr("pc", &tbufptr)) + PC = *pc; + if(!(BC = tgetstr("bc", &tbufptr))) { + if(!tgetflag("bs")) + error("Terminal must backspace."); + BC = tbufptr; + tbufptr += 2; + *BC = '\b'; + } + HO = tgetstr("ho", &tbufptr); + CO = tgetnum("co"); + LI = tgetnum("li"); + if(CO < COLNO || LI < ROWNO+2) + setclipped(); + if(!(CL = tgetstr("cl", &tbufptr))) + error("Hack needs CL."); + ND = tgetstr("nd", &tbufptr); + if(tgetflag("os")) + error("Hack can't have OS."); + CE = tgetstr("ce", &tbufptr); + UP = tgetstr("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("xd", &tbufptr); +/* not: XD = tgetstr("do", &tbufptr); */ + if(!(CM = tgetstr("cm", &tbufptr))) { + if(!UP && !HO) + error("Hack needs CM or UP or HO."); + printf("Playing hack on terminals without cm is suspect...\n"); + getret(); + } + US = SO = tgetstr("so", &tbufptr); + UE = SE = tgetstr("se", &tbufptr); + US = tgetstr("us", &tbufptr); + UE = tgetstr("ue", &tbufptr); + SG = tgetnum("sg"); /* -1: not fnd; else # of spaces left by so */ + if(!SO || !SE || (SG > 0)) SO = SE = 0; + CD = tgetstr("cd", &tbufptr); + set_whole_screen(); /* uses LI and CD */ + if(tbufptr-tbuf > sizeof(tbuf)) error("TERMCAP entry too big...\n"); + free(tptr); +} + +start_screen() +{ + xputs(TI); + xputs(VS); + + /* Select normal ASCII and line drawing character sets. + */ + if (flags.DECRainbow) + xputs("\033(B\033)0"); +} + +end_screen() +{ + clear_screen(); + xputs(VE); + xputs(TE); +} + +/* Cursor movements */ +extern xchar curx, cury; + +curs(x, y) +register int x, y; /* not xchar: perhaps xchar is unsigned and + curx-x would be unsigned as well */ +{ + + 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))) { + (void) putchar('\r'); + curx = 1; + nocmov(x, y); + } else if(!CM) { + nocmov(x, y); + } else + cmov(x, y); +} + +nocmov(x, y) +{ + if (cury > y) { + if(UP) { + while (cury > y) { /* Go up. */ + xputs(UP); + 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(BC); + curx--; + } + } +} + +cmov(x, y) +register x, y; +{ + xputs(tgoto(CM, x-1, y-1)); + cury = y; + curx = x; +} + +xputc(c) char c; { + (void) fputc(c, stdout); +} + +xputs(s) char *s; { +#ifdef MSDOS + fputs(s, stdout); +#else + tputs(s, 1, xputc); +#endif +} + +cl_end() { + 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 */ + register cx = curx, cy = cury; + + while(curx < COLNO) { + xputc(' '); + curx++; + } + curs(cx, cy); + } +} + +clear_screen() { + xputs(CL); + home(); +} + +home() +{ + if(HO) + xputs(HO); + else if(CM) + xputs(tgoto(CM, 0, 0)); + else + curs(1, 1); /* using UP ... */ + curx = cury = 1; +} + +standoutbeg() +{ + if(SO) xputs(SO); +} + +standoutend() +{ + if(SE) xputs(SE); +} + +backsp() +{ + xputs(BC); + curx--; +} + +bell() +{ +#ifdef DGK + if (flags.silent) return; +#endif /* DGK /**/ + (void) putchar('\007'); /* curx does not change */ + (void) fflush(stdout); +} + +static short tmspc10[] = { /* from termcap */ + 0, 2000, 1333, 909, 743, 666, 500, 333, 166, 83, 55, 41, 20, 10, 5 +}; + +delay_output() { + /* delay 50 ms - could also use a 'nap'-system call */ + /* BUG: if the padding character is visible, as it is on the 5620 + then this looks terrible. */ +#ifdef MSDOS + /* simulate the delay with "cursor here" */ + register i; + for (i = 0; i < 3; i++) { + cmov(curx, cury); + (void) fflush(stdout); + } +#else + if(!flags.nonull) + tputs("50", 1, xputc); + + /* cbosgd!cbcephus!pds for SYS V R2 */ + /* is this terminfo, or what? */ + /* tputs("$<50>", 1, xputc); */ + + else if(ospeed > 0 || ospeed < SIZE(tmspc10)) if(CM) { + /* delay by sending cm(here) an appropriate number of times */ + register int cmlen = strlen(tgoto(CM, curx-1, cury-1)); + register int i = 500 + tmspc10[ospeed]/2; + + while(i > 0) { + cmov(curx, cury); + i -= cmlen*tmspc10[ospeed]; + } + } +#endif /* MSDOS /**/ +} + +cl_eos() /* free after Robert Viduya */ +{ /* must only be called with curx = 1 */ + + if(CD) + xputs(CD); + else { + register int cx = curx, cy = cury; + while(cury <= LI-2) { + cl_end(); + xputc('\n'); + curx = 1; + cury++; + } + cl_end(); + curs(cx, cy); + } +} diff --git a/src/timeout.c b/src/timeout.c @@ -0,0 +1,63 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* timeout.c - version 1.0.3 */ + +#include "hack.h" + +timeout(){ +register 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 & TIMEOUT)) { + 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); + if (!See_invisible) + pline("You are no longer invisible."); + break; + case WOUNDED_LEGS: + heal_legs(); + break; + } + } +} + +/* He is being petrified - dialogue by inmet!tower */ +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 */ +}; + +stoned_dialogue() +{ + register long i = (Stoned & TIMEOUT); + + if(i > 0 && i <= SIZE(stoned_texts)) + pline(stoned_texts[SIZE(stoned_texts) - i]); + if(i == 5) + Fast = 0; + if(i == 3) + nomul(-3); +} diff --git a/src/topl.c b/src/topl.c @@ -0,0 +1,192 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* topl.c - version 1.0.2 */ + +#include <stdio.h> +#include "hack.h" +extern char *eos(); +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 */ + +doredotopl(){ + 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){ + (void) strcpy(toplines, last_redone_topl->topl_text); + } + redotoplin(); + return(0); +} + +redotoplin() { + home(); + if(index(toplines, '\n')) cl_end(); + putstr(toplines); + cl_end(); + tlx = curx; + tly = cury; + flags.toplin = 1; + if(tly > 1) + more(); +} + +remember_topl() { +register struct topl *tl; +register 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 = 0; + tl = (struct topl *) + alloc((unsigned)(strlen(toplines) + sizeof(struct topl) + 1)); + tl->next_topl = old_toplines; + tl->topl_text = (char *)(tl + 1); + (void) strcpy(tl->topl_text, toplines); + old_toplines = tl; + while(cnt && tl){ + cnt--; + tl = tl->next_topl; + } + if(tl && tl->next_topl){ + free((char *) tl->next_topl); + tl->next_topl = 0; + } +} + +addtopl(s) char *s; { + curs(tlx,tly); + if(tlx + strlen(s) > CO) putsym('\n'); + putstr(s); + tlx = curx; + tly = cury; + flags.toplin = 1; +} + +xmore(s) +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; +} + +more(){ + xmore(""); +} + +cmore(s) +register char *s; +{ + xmore(s); +} + +clrlin(){ + if(flags.toplin) { + home(); + cl_end(); + if(tly > 1) docorner(1, tly-1); + remember_topl(); + } + flags.toplin = 0; +} + +/*VARARGS1*/ +pline(line,arg1,arg2,arg3,arg4,arg5,arg6) +register char *line,*arg1,*arg2,*arg3,*arg4,*arg5,*arg6; +{ + char pbuf[BUFSZ]; + register char *bp = pbuf, *tl; + register int n,n0; + + if(!line || !*line) return; + if(!index(line, '%')) (void) strcpy(pbuf,line); else + (void) sprintf(pbuf,line,arg1,arg2,arg3,arg4,arg5,arg6); + 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 + strlen(toplines) + 3 < CO-8 && /* leave room for --More-- */ + strncmp(bp, "You ", 4)) { + (void) strcat(toplines, " "); + (void) 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; + } + (void) 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]) (void) strcat(tl, "\n"); + } + redotoplin(); +} + +putsym(c) 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++; + } + (void) putchar(c); +} + +putstr(s) register char *s; { + while(*s) putsym(*s++); +} diff --git a/src/track.c b/src/track.c @@ -0,0 +1,38 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* track.c - version 1.0.2 */ + +#include "hack.h" + +#define UTSZ 50 + +coord utrack[UTSZ]; +int utcnt = 0; +int utpnt = 0; + +initrack(){ + utcnt = utpnt = 0; +} + +/* add to track */ +settrack(){ + if(utcnt < UTSZ) utcnt++; + if(utpnt == UTSZ) utpnt = 0; + utrack[utpnt].x = u.ux; + utrack[utpnt].y = u.uy; + utpnt++; +} + +coord * +gettrack(x,y) register x,y; { +register int i,cnt,dist; +coord tc; + cnt = utcnt; + for(i = utpnt-1; cnt--; i--){ + if(i == -1) i = UTSZ-1; + tc = utrack[i]; + dist = (x-tc.x)*(x-tc.x) + (y-tc.y)*(y-tc.y); + if(dist < 3) + return(dist ? &(utrack[i]) : 0); + } + return(0); +} diff --git a/src/trap.c b/src/trap.c @@ -0,0 +1,464 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* trap.c - version 1.0.3 */ + +#include <stdio.h> +#include "hack.h" + +extern struct monst *makemon(); + +char vowels[] = "aeiou"; + +char *traps[] = { + " bear trap", + "n arrow trap", + " dart trap", + " trapdoor", + " teleportation trap", + " pit", + " sleeping gas trap", + " piercer", + " mimic" +}; + +struct trap * +maketrap(x,y,typ) +register x,y,typ; +{ + register 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); +} + +dotrap(trap) register struct trap *trap; { + register 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 + (void) 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 { + register 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; + } + fflush(stdout); + + 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); + } + } +} + +mintrap(mtmp) register struct monst *mtmp; { + register struct trap *trap = t_at(mtmp->mx, mtmp->my); + register int wasintrap = mtmp->mtrapped; + + if(!trap) { + mtmp->mtrapped = 0; /* perhaps teleported? */ + } else if(wasintrap) { + if(!rn2(40)) mtmp->mtrapped = 0; + } else { + register int tt = trap->ttyp; +#ifdef DGK + /* A bug fix for dumb messages by ab@unido. + */ + int in_sight = cansee(mtmp->mx,mtmp->my) + && (!mtmp->minvis || See_invisible); +#else + int in_sight = cansee(mtmp->mx,mtmp->my); +#endif + extern char mlarge[]; + + 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(index(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(!index("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); +} + +selftouch(arg) 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"); + } +} + +float_up(){ + 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!"); +} + +float_down(){ + register struct trap *trap; + pline("You float gently to the ground."); + if(trap = t_at(u.ux,u.uy)) + switch(trap->ttyp) { + case PIERC: + break; + case TRAPDOOR: + if(!xdnstair || u.ustuck) break; + /* fall into next case */ + default: + dotrap(trap); + } + pickup(1); +} + +vtele() { +#include "mkroom.h" + register struct mkroom *croom; + for(croom = &rooms[0]; croom->hx >= 0; croom++) + if(croom->rtype == VAULT) { + register 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(); +} + +tele() { + extern coord getpos(); + coord cc; + register 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); +} + +teleds(nux, nuy) +register int nux,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(); + (void) inshop(); + pickup(1); + if(!Blind) read_engr_at(u.ux,u.uy); +} + +teleok(x,y) register int x,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) */ +} + +dotele() { + extern char pl_character[]; + + 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); +} + +placebc(attach) 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; + } + } +} + +unplacebc(){ + if(!carried(uball)){ + freeobj(uball); + unpobj(uball); + } + freeobj(uchain); + unpobj(uchain); +} + +level_tele() { +register 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 { +#ifdef DGK + /* 1 chance in 5 of random tport to HELL */ + newlevel = rn2(5) ? 5 + rn2(20) : 30; +#else + newlevel = 5 + rn2(20); /* 5 - 24 */ +#endif + 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 ..."); +#ifdef DGK + fflush(stdout); +#endif + 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 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 plummet to the ground and break your neck."); + dlevel = 0; + killer = "fall"; + done("died"); + } + + goto_level(newlevel, FALSE); /* calls done("escaped") if newlevel==0 */ +} + +drown() +{ + pline("You fall into a pool!"); + pline("You can't swim!"); + if(rn2(3) < u.uluck+2) { + /* most scrolls become unreadable */ + register 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 */ + (void) dotele(); + if(levl[u.ux][u.uy].typ != POOL) return; + } + pline("You drown ..."); + killer = "pool of water"; + done("drowned"); +} diff --git a/src/trap.h b/src/trap.h @@ -0,0 +1,27 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* trap.h - version 1.0.2 */ + +struct trap { + struct trap *ntrap; + xchar tx,ty; + unsigned ttyp:5; + unsigned tseen:1; + unsigned once:1; +}; + +extern struct trap *ftrap; +struct trap *t_at(); +#define newtrap() (struct trap *) 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/src/tty.c b/src/tty.c @@ -0,0 +1,204 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* 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 <stdio.h> +#include "hack.h" + +static char erase_char, kill_char; + +/* + * 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 + */ +gettty(){ + erase_char = '\b'; + kill_char = 21; /* cntl-U */ + flags.cbreak = TRUE; +#ifdef DGK + disable_ctrlP(); /* turn off ^P processing */ +#endif +} + +/* reset terminal to original state */ +settty(s) char *s; { + end_screen(); + if(s) printf(s); + (void) fflush(stdout); +#ifdef DGK + enable_ctrlP(); /* turn on ^P processing */ +#endif +} + + +/* fatal error */ +/*VARARGS1*/ +error(s,x,y) char *s; { + end_screen(); + putchar('\n'); + printf(s,x,y); + 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". + */ +getlin(bufp) +register char *bufp; +{ + register char *obufp = bufp; + register int c; + + flags.toplin = 2; /* nonempty, no --More-- required */ + for(;;) { + (void) 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(); + } +} + +getret() { + cgetret(""); +} + +cgetret(s) +register 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 */ + +xwaitforspace(s) +register char *s; /* chars allowed besides space or return */ +{ +register int c; + + morc = 0; + while((c = readchar()) != '\n') { + if(flags.cbreak) { + if(c == ' ') break; + if(s && index(s,c)) { + morc = c; + break; + } + bell(); + } + } +} + +static int last_multi; + +char * +parse() +{ + static char inline[COLNO]; + register foo; + + flags.move = 1; + if(!Invisible) curs_on_u(); else home(); + multi = 0; +#ifdef DGK + while((foo = readchar()) >= '0' && foo <= '9') { + multi = 10*multi+foo-'0'; + if (multi < 0 || multi > LARGEST_INT) + multi = LARGEST_INT; + if (multi > 9) { + remember_topl(); + home(); + cl_end(); + printf("Count: %d", multi); + } + last_multi = multi; + } +# ifdef REDO + if (foo == DOAGAIN || in_doagain) + multi = last_multi; + else { + savech(0); /* reset input queue */ + savech(foo); + } +# endif + +#else /* DGK */ + + while((foo = readchar()) >= '0' && foo <= '9') + multi = 10*multi+foo-'0'; + +#endif /* DGK */ + + if(multi) { + multi--; + save_cm = inline; + } + inline[0] = foo; + inline[1] = 0; + if(foo == 'g' || foo == 'G'){ + inline[1] = getchar(); + savech(inline[1]); + inline[2] = 0; + } + if(foo == 'm' || foo == 'M'){ + inline[1] = getchar(); + savech(inline[1]); + inline[2] = 0; + } + clrlin(); + return(inline); +} + +char +readchar() { + register int sym; + + (void) fflush(stdout); + sym = getchar(); + if(flags.toplin == 1) + flags.toplin = 2; + return((char) sym); +} diff --git a/src/u_init.c b/src/u_init.c @@ -0,0 +1,344 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* u_init.c - version 1.0.3 */ + +#include <stdio.h> +#include "hack.h" +#define Strcpy (void) strcpy +#define Strcat (void) strcat +#define UNDEF_TYP 0 +#define UNDEF_SPE '\177' +extern struct obj *addinv(); +extern char *eos(); +extern char plname[]; +extern boolean female; + +struct you zerou; +char pl_character[PL_CSIZ]; +char *(roles[]) = { /* must all have distinct first letter */ + /* roles[4] may be changed to -man */ + "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 + +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 } +}; + +u_init(){ +register int i; +char exper = 'y', pc; +extern char readchar(); + if(female) /* should have been set in HACKOPTIONS */ + roles[4] = "Cave-woman"; + for(i = 0; i < NR_OF_ROLES; i++) + rolesyms[i] = roles[i][0]; + rolesyms[i] = 0; + + if(pc = pl_character[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("\nShall I pick a character for you (yes, no, or quit) ? [ynq] "); + + while(!index("yYnNqQ", (exper = readchar()))) + bell(); + printf("%c\n", exper); /* echo */ + if (index("qQ", exper)) + exit(0); + if(index("Yy", 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 "); + else if(i < NR_OF_ROLES - 2) + printf(","); + else if(i == NR_OF_ROLES - 2) + printf(" or"); + } + printf("? [%s or q(quit)] ", rolesyms); + + while(pc = readchar()) { + if (pc == 'q' || pc == 'Q') + exit(0); + if('a' <= pc && pc <= 'z') pc += 'A'-'a'; + if((i = role_index(pc)) >= 0) { + printf("%c\n", pc); /* echo */ + (void) fflush(stdout); /* should be seen */ + break; + } + if(pc == '\n') + break; + bell(); + } + if(pc == '\n') + pc = 0; + +beginner: + if(!pc) { + i = rn2(NR_OF_ROLES); + pc = rolesyms[i]; + printf("\nThis game you will be a%s %s.\n", + exper ? "n experienced" : "", + roles[i]); + getret(); + /* give him some feedback in case mklev takes much time */ + (void) putchar('\n'); + (void) fflush(stdout); + } + if(exper) { + roles[i][0] = pc; + } + +got_suffix: + + (void) 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(); + 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)) { + register int d = rn2(7) - 2; /* biased variation */ + u.ustr += d; + u.ustrmax += d; + } + +#ifdef WIZARD + if(wizard) wiz_inv(); +#endif + /* make sure he can carry all he has - especially for T's */ + while(inv_weight() > 0 && u.ustr < 118) + u.ustr++, u.ustrmax++; +} + +ini_inv(trop) register struct trobj *trop; { +register struct obj *obj; +extern struct obj *mkobj(); + 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); + if(--trop->trquan) continue; /* make a similar object */ + trop++; + } +} + +#ifdef WIZARD +wiz_inv(){ +register struct trobj *trop = &Extra_objs[0]; +extern char *getenv(); +register char *ep = getenv("INVENT"); +register int type; + while(ep && *ep) { + type = atoi(ep); + ep = index(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 + +plnamesuffix() { +register char *p; + if(p = rindex(plname, '-')) { + *p = 0; + pl_character[0] = p[1]; + pl_character[1] = 0; + if(!plname[0]) { + askname(); + plnamesuffix(); + } + } +} + +role_index(pc) +char pc; +{ /* must be called only from u_init() */ + /* so that rolesyms[] is defined */ + register char *cp; + + if(cp = index(rolesyms, pc)) + return(cp - rolesyms); + return(-1); +} diff --git a/src/unix.c b/src/unix.c @@ -0,0 +1,89 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* unix.c - version 1.0.3 */ + +/* This file collects some Unix dependencies; pager.c contains some more */ + +/* + * The time is used for: + * - seed for rand() + * - 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 "hack.h" /* mainly for index() which depends on BSD */ + +#include <sys/types.h> /* for time_t */ +#include <time.h> + +extern time_t time(); + +setrandom() +{ + (void) srand((int) time ((time_t *) 0)); +} + +struct tm * +getlt() +{ + time_t date; + struct tm *localtime(); + + (void) time(&date); + return(localtime(&date)); +} + +getyear() +{ + return(1900 + getlt()->tm_year); +} + +char * +getdate() +{ + static char datestr[7]; + register struct tm *lt = getlt(); + + (void) sprintf(datestr, "%2d%2d%2d", + lt->tm_year, lt->tm_mon + 1, lt->tm_mday); + if(datestr[2] == ' ') datestr[2] = '0'; + if(datestr[4] == ' ') datestr[4] = '0'; + return(datestr); +} + +phase_of_the_moon() /* 0-7, with 0: new, 4: full */ +{ /* moon period: 29.5306 days */ + /* year: 365.2422 days */ + register struct tm *lt = getlt(); + register 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 ); +} + +night() +{ + register int hour = getlt()->tm_hour; + + return(hour < 6 || hour > 21); +} + +midnight() +{ + return(getlt()->tm_hour == 0); +} + +regularize(s) /* normalize file name - we don't like ..'s or /'s */ +register char *s; +{ + register char *lp; + + while((lp = index(s, '.')) || (lp = index(s, '/'))) + *lp = '_'; +} diff --git a/src/vault.c b/src/vault.c @@ -0,0 +1,248 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* vault.c - version 1.0.2 */ + +#include "hack.h" +#include "mkroom.h" + +extern struct monst *makemon(); +#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]; +}; + +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 +restfakecorr() +{ + register fcx,fcy,fcbeg; + register 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 = 0; +} + +static +goldincorridor() +{ + register 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); +} + +setgd(){ +register struct monst *mtmp; + for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) if(mtmp->isgd){ + guard = mtmp; + gdlevel = dlevel; + return; + } + guard = 0; +} + +invault(){ +register 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]; + register 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) { + register 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 = 0; + 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 = 0; + 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; + } +} + +gd_move(){ +register int x,y,dx,dy,gx,gy,nx,ny,typ; +register struct fakecorridor *fcp; +register 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) { + register 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); +} + +gddead(){ + guard = 0; +} + +replgd(mtmp,mtmp2) +register struct monst *mtmp, *mtmp2; +{ + if(mtmp == guard) + guard = mtmp2; +} diff --git a/src/version.c b/src/version.c @@ -0,0 +1,17 @@ +/* version.c - version 1.0.3 */ + +#include "hack.h" + +doversion() +{ + pline("PC HACK %d.%d - Oct 26, 1986", vmajor, vminor); + return (0); +} + +doMSCversion() +{ + pline("PC HACK %d.%d is the MSDOS(tm) version of UNIX(tm) HACK 1.03.", + vmajor, vminor); + pline("PC implementation in Microsoft(tm) C by D.Kneller, Berkeley, CA"); + return (0); +} diff --git a/src/wield.c b/src/wield.c @@ -0,0 +1,99 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* wield.c - version 1.0.3 */ + +#include "hack.h" +extern struct obj zeroobj; + +setuwep(obj) register struct obj *obj; { + setworn(obj, W_WEP); +} + +dowield() +{ + register struct obj *wep; + register 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((struct obj *) 0); + 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); +} + +corrode_weapon(){ + 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--; + } +} + +chwepon(otmp,amount) +register struct obj *otmp; +register amount; +{ +register char *color = (amount < 0) ? "black" : "green"; +register char *time; + 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; + time = (amount*amount == 1) ? "moment" : "while"; + pline("Your %s %s for a %s.", + aobjnam(uwep, "glow"), color, time); + uwep->spe += amount; + if(amount > 0) uwep->cursed = 0; + return(1); +} diff --git a/src/wizard.c b/src/wizard.c @@ -0,0 +1,339 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* wizard.c - version 1.0.3 */ + +/* wizard code - inspired by rogue code from Merlyn Leroy (digi-g!brian) */ + +#include "hack.h" +extern struct permonst pm_wizard; +extern struct monst *makemon(); + +#ifdef DGK +#define WIZSHOT 2 /* one chance in WIZSHOT that wizard will try magic */ +#else +#define WIZSHOT 6 /* one chance in WIZSHOT that wizard will try magic */ +#endif +#define BOLT_LIM 8 /* from this distance D and 1 will try to hit you */ + +char wizapp[] = "@DNPTUVXcemntx"; + +#ifdef DGK +#define URETREATING(x,y) (movedist(u.ux,u.uy,x,y) > movedist(u.ux0,u.uy0,x,y)) +extern char mlarge[]; + +movedist(x0, x1, y0, y1) +{ + register int absdx, absdy; + + absdx = abs(x1 - x0); + absdy = abs(y1 - y0); + + return (absdx + absdy - min(absdx, absdy)); +} +#endif + +/* If he has found the Amulet, make the wizard appear after some time */ +amulet(){ + register struct obj *otmp; + register 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; + } +} + +wiz_hit(mtmp) +register 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)) { + register 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)) + ; + } + else + inrange(mtmp); /* try magic */ + return(0); +} + +#ifdef DGK +/* Check if a monster is carrying a particular item. + */ +struct obj * +m_carrying(mtmp, type) +struct monst *mtmp; +int type; +{ + register struct obj *otmp; + + for(otmp = mtmp->minvent; otmp; otmp = otmp->nobj) + if(otmp->otyp == type) + return(otmp); + return((struct obj *) 0); +} + +/* Remove an item from the monster's inventory. + */ +m_useup(mon, obj) +struct monst *mon; +struct obj *obj; +{ + struct obj *otmp, *prev; + + prev = ((struct obj *) 0); + for (otmp = mon->minvent; otmp; otmp = otmp->nobj) { + if (otmp == obj) { + if (prev) + prev->nobj = obj->nobj; + else + mon->minvent = obj->nobj; + free((char *) obj); + break; + } + prev = otmp; + } +} + +m_throw(x, y, dx, dy, range, obj) +register int x,y,dx,dy,range; /* direction and range */ +register struct obj *obj; +{ + register struct monst *mtmp; + struct objclass *oclass = &objects[obj->otyp]; + char sym = obj->olet; + int damage; + extern char *exclam(); + + bhitpos.x = x; + bhitpos.y = y; + + if(sym) tmp_at(-1, sym); /* open call */ + while(range-- > 0) { + bhitpos.x += dx; + bhitpos.y += dy; + if(mtmp = m_at(bhitpos.x,bhitpos.y)) { + damage = index(mlarge, mtmp->data->mlet) + ? oclass->wldam + : oclass->wsdam; + hit(oclass->oc_name, mtmp, exclam(damage)); + mtmp->mhp -= damage; + if(mtmp->mhp < 1) { + pline("%s is killed!", Monnam(mtmp)); + mondied(mtmp); + } + range = 0; + } + if (bhitpos.x == u.ux && bhitpos.y == u.uy) { + if (multi) + nomul(0); + if (!thitu(8, rnd(oclass->wldam), oclass->oc_name)) { + mksobj_at(obj->otyp, u.ux, u.uy); + fobj->quan = 1; + stackobj(fobj); + } + range = 0; + } + tmp_at(bhitpos.x, bhitpos.y); + } + tmp_at(-1, -1); +} +#endif + +/* Return 1 if it's OK for the monster to move as well as (throw, + * zap, etc). + */ +inrange(mtmp) +register struct monst *mtmp; +{ + register schar tx,ty; +#ifdef DGK + struct obj *otmp; + register xchar x, y; +#endif + + /* do nothing if cancelled (but make '1' say something) */ + if(mtmp->data->mlet != '1' && mtmp->mcan) + return 1; + + /* 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 1; + tx = u.ux - mtmp->mx; + ty = u.uy - mtmp->my; +#ifdef DGK + if ((!tx || !ty || abs(tx) == abs(ty)) /* straight line or diagonal */ + && movedist(tx, 0, ty, 0) < BOLT_LIM) { + /* Check if there are any dead squares between. If so, + * it won't be possible to shoot. + */ + for (x = mtmp->mx, y = mtmp->my; x != u.ux || y != u.uy; + x += sgn(tx), y += sgn(ty)) + if (!ACCESSIBLE(levl[x][y].typ)) + return 1; + + switch(mtmp->data->mlet) { + case 'K': + case 'C': + /* If you're coming toward the monster, the monster + * should try to soften you up with arrows. If you're + * going away, you are probably hurt or running. Give + * chase, but if you're getting too far away, throw. + */ + x = mtmp->mx; + y = mtmp->my; + otmp = (mtmp->data->mlet == 'K') ? m_carrying(mtmp, DART) + : m_carrying(mtmp, CROSSBOW_BOLT); + if (otmp && (!URETREATING(x,y) + || !rn2(BOLT_LIM - movedist(x, u.ux, y, u.uy)))) { + m_throw(mtmp->mx, mtmp->my, sgn(tx), sgn(ty), + BOLT_LIM,otmp); + if (!--otmp->quan) + m_useup(mtmp, otmp); + return 0; + } + break; +#else + if((!tx && abs(ty) < BOLT_LIM) || (!ty && abs(tx) < BOLT_LIM) + || (abs(tx) == abs(ty) && abs(tx) < BOLT_LIM)){ + switch(mtmp->data->mlet) { +#endif + 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)); +#ifdef DGK + /* bug fix by ab@unido + */ + nomul(-(rn2(3) + 3)); +#else + nomul(rn2(3) + 3); +#endif + 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) */ + (void) makemon((struct permonst *)0, 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); + } + return 1; +} + +aggravate() +{ + register struct monst *mtmp; + + for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) { + mtmp->msleep = 0; + if(mtmp->mfroz && !rn2(5)) + mtmp->mfroz = 0; + } +} + +clonewiz(mtmp) +register struct monst *mtmp; +{ + register struct monst *mtmp2; + + if(mtmp2 = makemon(PM_WIZARD, mtmp->mx, mtmp->my)) { + flags.no_of_wizards = 2; + unpmon(mtmp2); + mtmp2->mappearance = wizapp[rn2(sizeof(wizapp)-1)]; + pmon(mtmp); + } +} diff --git a/src/worm.c b/src/worm.c @@ -0,0 +1,183 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* worm.c - version 1.0.2 */ + +#include "hack.h" +#ifndef NOWORM +#include "wseg.h" + +struct wseg *wsegs[32]; /* linked list, tail first */ +struct wseg *wheads[32]; +long wgrowtime[32]; + +getwn(mtmp) struct monst *mtmp; { +register 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 */ +initworm(mtmp) struct monst *mtmp; { +register struct wseg *wtmp; +register tmp = mtmp->wormno; + if(!tmp) return; + wheads[tmp] = wsegs[tmp] = wtmp = newseg(); + wgrowtime[tmp] = 0; + wtmp->wx = mtmp->mx; + wtmp->wy = mtmp->my; +/* wtmp->wdispl = 0; */ + wtmp->nseg = 0; +} + +worm_move(mtmp) struct monst *mtmp; { +register struct wseg *wtmp, *whd; +register tmp = mtmp->wormno; + wtmp = newseg(); + wtmp->wx = mtmp->mx; + wtmp->wy = mtmp->my; + wtmp->nseg = 0; +/* wtmp->wdispl = 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); +} + +worm_nomove(mtmp) register struct monst *mtmp; { +register tmp; +register struct wseg *wtmp; + tmp = mtmp->wormno; + wtmp = wsegs[tmp]; + if(wtmp == wheads[tmp]) return; + if(wtmp == 0 || wtmp->nseg == 0) panic("worm_nomove?"); + wsegs[tmp] = wtmp->nseg; + remseg(wtmp); + mtmp->mhp -= 3; /* mhpmax not changed ! */ +} + +wormdead(mtmp) register struct monst *mtmp; { +register tmp = mtmp->wormno; +register struct wseg *wtmp, *wtmp2; + if(!tmp) return; + mtmp->wormno = 0; + for(wtmp = wsegs[tmp]; wtmp; wtmp = wtmp2){ + wtmp2 = wtmp->nseg; + remseg(wtmp); + } + wsegs[tmp] = 0; +} + +wormhit(mtmp) register struct monst *mtmp; { +register tmp = mtmp->wormno; +register struct wseg *wtmp; + if(!tmp) return; /* worm without tail */ + for(wtmp = wsegs[tmp]; wtmp; wtmp = wtmp->nseg) + (void) hitu(mtmp,1); +} + +wormsee(tmp) register unsigned tmp; { +register 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; + } +} + +pwseg(wtmp) register struct wseg *wtmp; { + if(!wtmp->wdispl){ + atl(wtmp->wx, wtmp->wy, '~'); + wtmp->wdispl = 1; + } +} + +cutworm(mtmp,x,y,weptyp) +register struct monst *mtmp; +register xchar x,y; +register uchar weptyp; /* uwep->otyp or 0 */ +{ + register struct wseg *wtmp, *wtmp2; + register struct monst *mtmp2; + register tmp,tmp2; + if(mtmp->mx == x && mtmp->my == y) return; /* hit headon */ + + /* 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"); +} + +remseg(wtmp) register struct wseg *wtmp; { + if(wtmp->wdispl) + newsym(wtmp->wx, wtmp->wy); + free((char *) wtmp); +} +#endif /* NOWORM /**/ diff --git a/src/worn.c b/src/worn.c @@ -0,0 +1,65 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* worn.c - version 1.0.2 */ + +#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 } +}; + +setworn(obj, mask) +register struct obj *obj; +long mask; +{ + register struct worn *wp; + register 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 */ +setnotworn(obj) register struct obj *obj; { + register struct worn *wp; + + for(wp = worn; wp->w_mask; wp++) + if(obj == *(wp->w_obj)) { + *(wp->w_obj) = 0; + obj->owornmask &= ~wp->w_mask; + } + if(uarm2 && !uarm) { + uarm = uarm2; + uarm2 = 0; + uarm->owornmask ^= (W_ARM | W_ARM2); + } +} diff --git a/src/wseg.h b/src/wseg.h @@ -0,0 +1,13 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* wseg.h - version 1.0.2 */ + +#ifndef NOWORM +/* worm structure */ +struct wseg { + struct wseg *nseg; + xchar wx,wy; + unsigned wdispl:1; +}; + +#define newseg() (struct wseg *) alloc(sizeof(struct wseg)) +#endif /* NOWORM /**/ diff --git a/src/zap.c b/src/zap.c @@ -0,0 +1,657 @@ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* zap.c - version 1.0.3 */ + +#include "hack.h" + +extern struct obj *mkobj_at(); +extern struct monst *makemon(), *mkmon_at(), youmonst; +struct monst *bhit(); +char *exclam(); + +char *fl[]= { + "magic missile", + "bolt of fire", + "sleep ray", + "bolt of cold", + "death ray" +}; + +/* Routines for IMMEDIATE wands. */ +/* bhitm: monster mtmp was hit by the effect of wand otmp */ +bhitm(mtmp, otmp) +register struct monst *mtmp; +register struct obj *otmp; +{ + wakeup(mtmp); + switch(otmp->otyp) { + case WAN_STRIKING: + if(u.uswallow || rnd(20) < 10+mtmp->data->ac) { + register 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(index(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 + default: + impossible("What an interesting wand (%u)", otmp->otyp); + } +} + +bhito(obj, otmp) /* object obj was hit by the effect of wand otmp */ +register struct obj *obj, *otmp; /* returns TRUE if sth was done */ +{ + register int res = TRUE; +#ifdef DGK + struct obj *otmp2; +#endif + + if(obj == uball || obj == uchain) + res = FALSE; + else + switch(otmp->otyp) { + case WAN_POLYMORPH: + /* preserve symbol and quantity, but turn rocks into gems */ +#ifdef DGK + otmp2 = mkobj_at((obj->otyp == ROCK + || obj->otyp == ENORMOUS_ROCK) ? GEM_SYM : obj->olet, + obj->ox, obj->oy); + otmp2->quan = obj->quan; + /* keep special fields (including charges on wands) */ + otmp2->spe = min(obj->spe, otmp2->spe); + otmp2->cursed = otmp->cursed; + /* update the weight */ + otmp2->owt = weight(otmp2); +#else + mkobj_at((obj->otyp == ROCK || obj->otyp == ENORMOUS_ROCK) + ? GEM_SYM : obj->olet, + obj->ox, obj->oy) -> quan = obj->quan; +#endif + 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 + res = FALSE; + break; + default: + impossible("What an interesting wand (%u)", otmp->otyp); + } + return(res); +} + +dozap() +{ + register 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) { + register struct obj *otmp = o_at(u.ux, u.uy); + if(otmp) + (void) bhito(otmp, obj); + } + } else + (void) 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: + { register int cnt = 1; + if(!rn2(23)) cnt += rn2(7) + 1; + while(cnt--) + (void) makemon((struct permonst *) 0, u.ux, u.uy); + } + break; + case WAN_WISHING: + { char buf[BUFSZ]; + register struct obj *otmp; + extern struct obj *readobjnam(), *addinv(); + 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. + */ + { register struct rm *room; + register int digdepth; + if(u.uswallow) { + register 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); +} + +char * +exclam(force) +register 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) ? "." : "!" ); +} + +hit(str,mtmp,force) +register char *str; +register struct monst *mtmp; +register char *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); +} + +miss(str,mtmp) +register char *str; +register 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() */ + +struct monst * +bhit(ddx,ddy,range,sym,fhitm,fhito,obj) +register int ddx,ddy,range; /* direction and range */ +char sym; /* symbol displayed on path */ +int (*fhitm)(), (*fhito)(); /* fns called when mon/obj hit */ +struct obj *obj; /* 2nd arg to fhitm/fhito */ +{ + register struct monst *mtmp; + register struct obj *otmp; + register int typ; + + bhitpos.x = u.ux; + bhitpos.y = u.uy; + + if(sym) tmp_at(-1, sym); /* open call */ + 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(dx,dy) { + register int i, ct; + register struct monst *mtmp; + char sym = ')'; + extern schar xdir[], ydir[]; + + 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)){ + 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 */ + (void) 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); +} + +char +dirlet(dx,dy) register dx,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 */ +buzz(type,sx,sy,dx,dy) +register int type; +register xchar sx,sy; +register int dx,dy; +{ + int abstype = abs(type); + register char *fltxt = (type == -1) ? "blaze of fire" : fl[abstype]; + struct rm *lev; + xchar range; + struct monst *mon; + + if(u.uswallow) { + register 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) { + register 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) { + register 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); +} + +zhit(mon,type) /* returns damage to mon */ +register struct monst *mon; +register type; +{ + register int tmp = 0; + + switch(type) { + case 0: /* magic missile */ + tmp = d(2,6); + break; + case -1: /* Dragon blazing fire */ + case 1: /* fire */ + if(index("Dg", mon->data->mlet)) break; + tmp = d(6,6); + if(index("YF", mon->data->mlet)) tmp += 7; + break; + case 2: /* sleep*/ + mon->mfroz = 1; + break; + case 3: /* cold */ + if(index("YFgf", mon->data->mlet)) break; + tmp = d(6,6); + if(mon->data->mlet == 'D') tmp += 7; + break; + case 4: /* death*/ + if(index(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)) +revive(obj) +register struct obj *obj; +{ + register struct monst *mtmp; + + 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 */ +} + +rloco(obj) +register struct obj *obj; +{ + register 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); +} + +fracture_rock(obj) /* fractured by pick-axe or wand of striking */ +register struct obj *obj; /* no texts here! */ +{ + /* unpobj(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); +} + +burn_scrolls() +{ + register struct obj *obj, *obj2; + register 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/termcap/fgetlr.c b/termcap/fgetlr.c @@ -0,0 +1,109 @@ +/************************************************************************ + * * + * Copyright (c) 1982, Fred Fish * + * All Rights Reserved * + * * + * This software and/or documentation is released for public * + * distribution for personal, non-commercial use only. * + * Limited rights to use, modify, and redistribute are hereby * + * granted for non-commercial purposes, provided that all * + * copyright notices remain intact and all changes are clearly * + * documented. The author makes no warranty of any kind with * + * respect to this product and explicitly disclaims any implied * + * warranties of merchantability or fitness for any particular * + * purpose. * + * * + ************************************************************************ + */ + + +/* + * LIBRARY FUNCTION + * + * fgetlr get logical record from a file + * + * KEY WORDS + * + * fgetlr + * string functions + * + * SYNOPSIS + * + * char *fgetlr(bp,bpsize,fp) + * char *bp; + * int bpsize; + * FILE *fp; + * + * DESCRIPTION + * + * Reads the next logical record from stream "fp" into buffer "bp" + * until next unescaped newline, "bpsize" minus one characters + * have been read, end of file, or read error. + * The last character read is followed by a NULL. + * + * A logical record may span several physical records by having + * each newline escaped with the standard C escape character + * (backslash). + * + * This is particularly useful for things like the termcap + * file, where a single entry is too long for one physical + * line, yet needs to be treated as a single record. + * + * Returns its first argument unless an end of file or read + * error occurs prior to any characters being read. + * + * BUGS + * + * The only way to know if read was terminated due to buffer size + * limitation is to test for a newline before the terminating + * null. + * + */ + +#include <stdio.h> + +/* + * PSEUDO CODE + * + * Begin fgetlr + * If read fails then + * Return NULL. + * Else + * Find out how many characters were read. + * Initialize pointer to terminating null. + * If last char read was newline then + * If newline was escaped then + * Replace backslash with the newline. + * Replace newline with null. + * Read and append more. + * End if + * End if + * Return buffer pointer. + * End if + * End fgetlr + * + */ + +char *fgetlr(bp,bpsize,fp) +char *bp; +int bpsize; +FILE *fp; +{ + int numch; + char *cp; + + if (fgets(bp,bpsize,fp) == NULL) { + return(NULL); + } else { + numch = strlen(bp); + cp = &bp[numch]; + if (*--cp == '\n') { + if (numch > 1 && *--cp == '\\') { + *cp++ = '\n'; + *cp = NULL; + fgetlr(cp,bpsize-numch+1,fp); + } + } + return(bp); + } +} diff --git a/termcap/isdigit.c b/termcap/isdigit.c @@ -0,0 +1,54 @@ +/************************************************************************ + * * + * Copyright (c) 1982, Fred Fish * + * All Rights Reserved * + * * + * This software and/or documentation is released for public * + * distribution for personal, non-commercial use only. * + * Limited rights to use, modify, and redistribute are hereby * + * granted for non-commercial purposes, provided that all * + * copyright notices remain intact and all changes are clearly * + * documented. The author makes no warranty of any kind with * + * respect to this product and explicitly disclaims any implied * + * warranties of merchantability or fitness for any particular * + * purpose. * + * * + ************************************************************************ + */ + + +/* + * LIBRARY FUNCTION + * + * isdigit test character for numeric property + * + * SYNOPSIS + * + * int isdigit(ch) + * char ch; + * + * DESCRIPTION + * + * Returns TRUE or FALSE depending upon whether the specified + * character is a numeric character or not. + * + * BUGS + * + * May fail on machines in which native character set is not ASCII. + * + */ + +#include <stdio.h> + +#define TRUE 1 +#define FALSE 0 + +int isdigit(ch) +char ch; +{ + if (ch > '9' || ch < '0') { + return(FALSE); + } else { + return(TRUE); + } +} diff --git a/termcap/makefile b/termcap/makefile @@ -0,0 +1,60 @@ +# +# FILE +# +# Makefile build termcap library +# +# KEY WORDS +# +# libraries +# test functions +# +# SYNOPSIS +# +# make compile the library sources +# make tests compile sources for tests +# +# DESCRIPTION +# +# Standard make file for building the termcap library and tests. +# +# AUTHOR +# +# Fred Fish +# + +CC = msc +MODEL = L +CFLAGS = -A${MODEL} -DDGK + +TSOURCES = testtcp.c +LSOURCES = tgetent.c tgetflag.c tgetnum.c tgetstr.c tgoto.c tputs.c \ + isdigit.c fgetlr.c + + +TOBJECTS = testtcp.obj +LOBJECTS = tgetent.obj tgetflag.obj tgetnum.obj tgetstr.obj tgoto.obj \ + tputs.obj isdigit.obj fgetlr.obj + +termlib.lib : $(LOBJECTS) + lib termlib.lib -+ $(LOBJECTS); + +install: termlib.lib + mv termlib.lib \lib\${MODEL}termlib.lib + +all: library tests + +tests : testtcp + +testtcp : testtcp.obj + link testtcp.obj, testtcp,, \lib\termcap +# $(CC) -o testtcp testtcp.obj -ltermcap + +testtcp.obj : testtcp.c + $(CC) -c $(CFLAGS) testtcp.c + +# +# Clean up the directory. +# + +clean: + rm *.obj diff --git a/termcap/termcap b/termcap/termcap @@ -0,0 +1,26 @@ +# +# Monochrome IBMPC. +# This is a termcap for the NANSI.SYS device driver. +# It is the same as the ANSI termcap, except NANSI supports +# line insert (al) and delete (dl) while ANSI does not. +# +ibmpc-mono:\ + :co#80:\ + :li#24:\ + :cl=\E[2J:\ + :bs:\ + :ho=\E[H:\ + :cm=\E[%i%2;%2H:\ + :up=\E[A:\ + :xd=\E[B:\ + :nd=\E[C:\ + :bc=\E[D:\ + :ce=\E[K:\ + :ti=\E[m:\ + :te=\E[m:\ + :so=\E[1m:\ + :se=\E[m:\ + :us=\E[4m:\ + :ue=\E[m:\ + :al=\E[L:\ + :dl=\E[M: diff --git a/termcap/testtcp.c b/termcap/testtcp.c @@ -0,0 +1,348 @@ +/************************************************************************ + * * + * Copyright (c) 1982, Fred Fish * + * All Rights Reserved * + * * + * This software and/or documentation is released for public * + * distribution for personal, non-commercial use only. * + * Limited rights to use, modify, and redistribute are hereby * + * granted for non-commercial purposes, provided that all * + * copyright notices remain intact and all changes are clearly * + * documented. The author makes no warranty of any kind with * + * respect to this product and explicitly disclaims any implied * + * warranties of merchantability or fitness for any particular * + * purpose. * + * * + ************************************************************************ + */ + + +/* + * TEST PROGRAM + * + * testtcp test termcap functions + * + * KEY WORDS + * + * test routines + * termcap test + * + * SYNOPSIS + * + * termcap [-efns] terminal [capability [capability ...]] + * + * -e => expand string capability given by -s + * -f => determine boolean capabilities for terminal + * -n => determine numeric capabilities for terminal + * -s => determine string capabilities for terminal + * + * terminal => terminal name as given in termcap file + * capability => a boolean, numeric, or string capability + * + * NOTE: All capabilities must be of same type, as + * given by [-fns]. + * + * If terminal is only argument then entire entry is + * printed. + * + * DESCRIPTION + * + * Provides way to test termcap functions. Can find + * and print an entire termcap terminal entry, or various + * capabilities from the entry. + * + * AUTHOR + * + * Fred Fish + * + */ + +#include <stdio.h> + +#define TRUE 1 +#define FALSE 0 +#define NO_FILE -1 /* Returned if can't open file */ +#define NO_ENTRY 0 /* Returned if can't find entry */ +#define SUCCESS 1 /* Returned if entry found ok */ +#define TRUNCATED 2 /* Returned if entry found but trunc */ +#define BUFFER_SIZE 1024 + +int eflag = FALSE; +int fflag = FALSE; +int nflag = FALSE; +int sflag = FALSE; + +int got_terminal = FALSE; +int got_capability = FALSE; + + +/* + * FUNCTION + * + * main termcap test entry point + * + * KEY WORDS + * + * main + * + * SYNOPSIS + * + * main(argc,argv) + * int argc; + * char *argv[]; + * + * DESCRIPTION + * + * This is where the termcap test starts executing. All argument list + * switches are processed first, then all the specified + * capability identification strings are processed. + * + */ + +/* + * PSEUDO CODE + * + * Begin main + * Process command line options. + * For each argument list field + * If field was not erased during option processing + * If terminal name field not yet processed then + * Process an assumed terminal name field. + * Set terminal name processed flag. + * Else + * Process a capability field. + * Set capability field processed flag. + * End if + * End if + * End for + * If no capabilities processed then + * Simply dump buffer. + * End if + * End main + * + */ + +main(argc, argv) +int argc; +char *argv[]; +{ + char *argp; + int argnum; + char buffer[BUFFER_SIZE]; + + options(argc,argv); + for (argnum = 1; argnum < argc; argnum++) { + if ((argp = argv[argnum]) != NULL) { + if (!got_terminal) { + terminal(buffer,argp); + got_terminal = TRUE; + } else { + capability(argp); + got_capability = TRUE; + } + } + } + if (got_terminal && !got_capability) { + printf("%s",buffer); + } +} + +/* + * FUNCTION + * + * options process command line options + * + * SYNOPSIS + * + * options(argc,argv) + * int argc; + * char *argv[]; + * + * DESCRIPTION + * + * Scans argument list, processing each switch as it is + * found. The pointer to each switch string is then + * replaced with a NULL to effectively erase the switch + * argument. + * + */ + +/* + * PSEUDO CODE + * + * Begin options + * For each argument in the argument list + * Get pointer to first char of argument. + * If the argument is a switch then + * Replace argument pointer with NULL. + * Look at next argument character. + * While there is another argument character + * Switch on the argument character + * Case "EXPAND": + * Set expand (e) flag. + * Break out of switch. + * Case "BOOLEAN": + * Set boolean (f) flag. + * Break out of switch. + * Case "NUMERIC": + * Set numeric flag. + * Break out of switch. + * Case "STRING": + * Set string flag. + * Break out of switch. + * Default: + * Abort with usage message. + * End switch + * End while + * End if + * End for + * End options + * + */ + +options(argc, argv) +int argc; +char *argv[]; +{ + int i; + char c; /* 1st char of current command-line argument */ + char *cp; /* current argument pointer */ + + for (i=1; i<argc; i++) { + cp = argv[i]; + if (*cp == '-') { + argv[i] = NULL; + cp++; + while (c = *cp++) { + switch (c) { + case 'e': + eflag = TRUE; + break; + case 'f': + fflag = TRUE; + break; + case 'n': + nflag = TRUE; + break; + case 's': + sflag = TRUE; + break; + default: + usage(); + } + } + } + } +} + +/* + * FUNCTION + * + * usage give usage message and abort + * + * KEY WORDS + * + * usage + * help processing + * abort locations + * + * SYNOPSIS + * + * usage() + * + * DESCRIPTION + * + * Usage is typically called when a problem has been + * detected in the argument list. + * It prints a usage message and exits. + * + */ + +/* + * PSEUDO CODE + * + * Begin usage + * Print usage message. + * Exit. + * End usage + * + */ + +usage() +{ + printf("Usage: termcap [-fns] terminal [capability [capability ... ]]\n"); + exit(); +} + + +terminal(buffer,name) +char *buffer; +char *name; +{ + int status; + + status = tgetent(buffer,name); + switch (status) { + case NO_FILE: + fprintf(stderr,"Can't find a termcap data base file.\n"); + exit(); + case NO_ENTRY: + fprintf(stderr,"Can't find entry \"%s\"\n",name); + exit(); + case TRUNCATED: + fprintf(stderr,"Warning --- entry \"%s\" too long\n",name); + break; + case SUCCESS: + break; + default: + fprintf(stderr,"? tgetent returned illegal status %d\n",status); + exit(); + } +} + +capability(id) +char *id; +{ + int value; + char buffer[256]; + char *area; + char *ep, *tgoto(); + + if (fflag) { + value = tgetflag(id); + if (value) { + printf("%s TRUE\n",id); + } else { + printf("%s FALSE\n",id); + } + } else if (nflag) { + value = tgetnum(id); + printf("%s = %o octal %d decimal\n",id,value,value); + } else if (sflag) { + area = buffer; + tgetstr(id,&area); + if (eflag) { + ep = tgoto(buffer,75,23); + } + doprint(id,buffer); + if (eflag) { + doprint(id,ep); + } + } +} + +doprint(id,cp) +char *id; +char *cp; +{ + printf("%s = ",id); + for ( ; *cp != NULL; cp++) { + if (*cp < 040) { + printf("^%c ",*cp |= 0100); + } else { + printf("%c ",*cp); + } + } + printf("\n"); +} + diff --git a/termcap/tgetent.c b/termcap/tgetent.c @@ -0,0 +1,320 @@ +/************************************************************************ + * * + * Copyright (c) 1982, Fred Fish * + * All Rights Reserved * + * * + * This software and/or documentation is released for public * + * distribution for personal, non-commercial use only. * + * Limited rights to use, modify, and redistribute are hereby * + * granted for non-commercial purposes, provided that all * + * copyright notices remain intact and all changes are clearly * + * documented. The author makes no warranty of any kind with * + * respect to this product and explicitly disclaims any implied * + * warranties of merchantability or fitness for any particular * + * purpose. * + * * + ************************************************************************ + */ + + +/* + * LIBRARY FUNCTION + * + * tgetent load buffer with entry for specified terminal + * + * KEY WORDS + * + * termcap functions + * utility routines + * + * SYNOPSIS + * + * int tgetent(bp,name) + * char *bp; + * char *name; + * + * DESCRIPTION + * + * Extracts the entry for terminal <name> from the termcap file + * and places it in the character buffer <bp>. It is currently + * assumed that bp is at least 1024 characters. If the entry in + * the termcap file is larger than 1023 characters the excess + * characters will be discarded and appropriate status will + * be returned. + * + * Also note that since bp is used by other termcap + * routines, the storage associated with the termcap entry + * cannot be freed until all termcap calls are completed. + * + * Tgetent can be directed to look in a file other than + * the default (/etc/termcap) by defining an environment + * variable called TERMCAP to be the pathname of the desired + * termcap file. This is useful for debugging new entries. + * NOTE: the pathname MUST begin with a '/' character. + * + * Also, if the string assigned to TERMCAP does not begin with + * a '/' and if the environment variable TERM matches <name> then + * the string assigned to TERMCAP is copied to buffer <bp> + * instead of reading a termcap file. + * + * RETURNS + * + * -1 if the termcap file cannot be opened + * 0 if no entry in termcap file matches <name> + * 1 if extraction is successful with no errors + * 2 if extraction is successful but entry truncated + * + * SEE ALSO + * + * tgetnum extract numeric type capability + * tgetflag test boolean type capability + * tgetstr get string value of capability + * + * AUTHOR + * + * Fred Fish + * + */ + +#include <stdio.h> + +#define TRUE 1 +#define FALSE 0 +#define BUFSIZE 1024 /* Assumed size of external buffer */ + +#define NO_FILE -1 /* Returned if can't open file */ +#define NO_ENTRY 0 /* Returned if can't find entry */ +#define SUCCESS 1 /* Returned if entry found ok */ +#define TRUNCATED 2 /* Returned if entry found but trunc */ + +# ifdef DGK +# define DEFAULT_ROOT "termcap" /* name without path component */ + FILE *fopenp(); +# endif +# define DEFAULT_FILE "/etc/termcap" /* default termcap filename */ + +char *_tcpbuf; /* Place to remember buffer pointer */ + +/* + * PSEUDO CODE + * + * Begin tgetent + * Erase any previous buffer contents. + * Remember the buffer pointer. + * If termcap file is not found then + * If buffer was filled anyway then + * Return SUCCESS. + * Else + * Return NO_FILE. + * End if + * Else + * While records left to process + * If this is entry is what we want then + * Close the termcap file. + * If entry was truncated then + * Return TRUNCATED status + * Else + * Return SUCCESS status. + * End if + * End if + * End while + * Return NO_ENTRY status. + * End if + * End tgetent + * + */ + +int tgetent(bp,name) +char *bp; /* Pointer to buffer (1024 char min) */ +char *name; /* Pointer to terminal entry to find */ +{ + FILE *fp, *find_file(); + + *bp = NULL; + _tcpbuf = bp; + if ((fp = find_file(bp)) == NULL) { + if (*bp != NULL) { + return(SUCCESS); + } else { + return(NO_FILE); + } + } else { + while (fgetlr(bp,BUFSIZE,fp)) { + if (gotcha(bp,name)) { + fclose(fp); + if (bp[strlen(bp)-1] != '\n') { + return(TRUNCATED); + } else { + return(SUCCESS); + } + } + } + return(NO_ENTRY); + } +} + +/* + * INTERNAL FUNCTION + * + * find_file find the termcap file and open it if possible + * + * KEY WORDS + * + * internal functions + * find_file + * + * SYNOPSIS + * + * static FILE *find_file(bp) + * char *bp; + * + * DESCRIPTION + * + * Attempts to locate and open the termcap file. Also handles + * using the environment TERMCAP string as the actual buffer + * (that's why bp has to be an input parameter). + * + * If TERMCAP is defined an begins with a '/' character then + * it is taken to be the pathname of the termcap file and + * an attempt is made to open it. If this fails then + * the default termcap file is used instead. + * + * If TERMCAP is defined but does not begin with a '/' then + * it is assumed to be the actual buffer contents provided + * that <name> matches the environment variable TERM. + * + * BUGS + * + * There is currently no way to be sure which termcap + * file was opened since the default will always be + * tried. + * + */ + +/* + * PSEUDO CODE + * + * Begin find_file + * If there is a TERMCAP environment string then + * If the string is not null then + * If the string is a pathname then + * If that file is opened successfully then + * Return its pointer. + * End if + * Else + * If there is a TERM environment string then + * If TERM matches <name> then + * Copy TERMCAP string to buffer. + * Return NULL for no file. + * End if + * End if + * End if + * End if + * End if + * Open default termcap file and return results. + * End find_file + * + */ + +static FILE *find_file(bp) +char *bp; +{ + FILE *fp, *fopen(); + char *cp, *ncp, *getenv(); + + if ((cp = getenv("TERMCAP")) != NULL) { + if (*cp != NULL) { + if (*cp == '/' || *cp == '\\') { + if ((fp = fopen(cp,"r")) != NULL) { + return(fp); + } + } else { + if ((ncp = getenv("TERM")) != NULL) { + if (strcmp(cp,ncp) == 0) { + strcpy(bp,cp); + return((FILE *)NULL); + } + } + } + } + } +# ifdef DGK + /* Try current directory, then /etc/termcap, then along the path + */ + if (fp = fopen(DEFAULT_ROOT, "r")) + return fp; + else if (fp = fopen(DEFAULT_FILE, "r")) + return fp; + else + return fopenp(DEFAULT_ROOT, "r", NULL); +# else + return(fopen(DEFAULT_FILE,"r")); +# endif +} + +/* + * INTERNAL FUNCTION + * + * gotcha test to see if entry is for specified terminal + * + * SYNOPSIS + * + * gotcha(bp,name) + * char *bp; + * char *name; + * + * DESCRIPTION + * + * Tests to see if the entry in buffer bp matches the terminal + * specified by name. Returns TRUE if match is detected, FALSE + * otherwise. + * + */ + +/* + * PSEUDO CODE + * + * Begin gotcha + * If buffer character is comment character then + * Return FALSE since remainder is comment + * Else + * Initialize name scan pointer. + * Compare name and buffer until end or mismatch. + * If valid terminators for both name and buffer strings + * Return TRUE since a match was found. + * Else + * Find next non-name character in buffer. + * If not an alternate name separater character + * Return FALSE since no more names to check. + * Else + * Test next name and return results. + * End if + * End if + * End if + * End gotcha + * + */ + +gotcha(bp,name) +char *bp; +char *name; +{ + char *np; + + if (*bp == '#') { + return(FALSE); + } else { + np = name; + while (*np == *bp && *np != NULL) {np++; bp++;} + if (*np == NULL && (*bp == NULL || *bp == '|' || *bp == ':')) { + return(TRUE); + } else { + while (*bp != NULL && *bp != ':' && *bp != '|') {bp++;} + if (*bp != '|') { + return(FALSE); + } else { + return(gotcha(++bp,name)); + } + } + } +} diff --git a/termcap/tgetflag.c b/termcap/tgetflag.c @@ -0,0 +1,89 @@ +/************************************************************************ + * * + * Copyright (c) 1982, Fred Fish * + * All Rights Reserved * + * * + * This software and/or documentation is released for public * + * distribution for personal, non-commercial use only. * + * Limited rights to use, modify, and redistribute are hereby * + * granted for non-commercial purposes, provided that all * + * copyright notices remain intact and all changes are clearly * + * documented. The author makes no warranty of any kind with * + * respect to this product and explicitly disclaims any implied * + * warranties of merchantability or fitness for any particular * + * purpose. * + * * + ************************************************************************ + */ + + +/* + * LIBRARY FUNCTION + * + * tgetflag extract boolean termcap capability + * + * KEY WORDS + * + * termcap + * + * SYNOPSIS + * + * tgetflag(id) + * char *id; + * + * DESCRIPTION + * + * Returns TRUE if specified id is present in terminal + * entry, FALSE otherwise. + * + */ + +#include <stdio.h> + +#define TRUE 1 +#define FALSE 0 +# ifdef MSDOS +# define index strchr +# endif + +extern char *_tcpbuf; /* Termcap entry buffer pointer */ + +/* + * PSEUDO CODE + * + * Begin tgetflag + * Initialize pointer to the termcap entry buffer. + * While there is a field to process + * Skip over the field separator character. + * If this is the entry we want then + * If entry is identifier only then + * Return TRUE + * Else + * Return FALSE + * End if + * End if + * End while + * Return FALSE as default. + * End tgetflag + * + */ + +tgetflag(id) +char *id; +{ + char *bp; + extern char *index(); + + bp = _tcpbuf; + while ((bp = index(bp,':')) != NULL) { + bp++; + if (*bp++ == id[0] && *bp != NULL && *bp++ == id[1]) { + if (*bp == NULL || *bp++ == ':') { + return(TRUE); + } else { + return(FALSE); + } + } + } + return(FALSE); +} diff --git a/termcap/tgetnum.c b/termcap/tgetnum.c @@ -0,0 +1,108 @@ +/************************************************************************ + * * + * Copyright (c) 1982, Fred Fish * + * All Rights Reserved * + * * + * This software and/or documentation is released for public * + * distribution for personal, non-commercial use only. * + * Limited rights to use, modify, and redistribute are hereby * + * granted for non-commercial purposes, provided that all * + * copyright notices remain intact and all changes are clearly * + * documented. The author makes no warranty of any kind with * + * respect to this product and explicitly disclaims any implied * + * warranties of merchantability or fitness for any particular * + * purpose. * + * * + ************************************************************************ + */ + + +/* + * LIBRARY FUNCTION + * + * tgetnum extract numeric option from termcap entry + * + * KEY WORDS + * + * termcap + * ce functions + * + * SYNOPSIS + * + * tgetnum(id) + * char *id; + * + * DESCRIPTION + * + * Returns numeric value of capability <id>, or -1 if <id> + * is not found. Knows about octal numbers, which + * begin with 0. + * + */ + +#include <stdio.h> +# ifdef MSDOS +# define index strchr +# endif + +extern char *_tcpbuf; /* Termcap entry buffer pointer */ + +/* + * PSEUDO CODE + * + * Begin tgetnum + * Initialize pointer to the termcap entry buffer. + * While there is a field to process + * Skip over the field separator character. + * If this is the entry we want then + * If the entry is not a numeric then + * Return failure value. + * Else + * Initialize value to zero. + * If number begins with zero then + * Set accumulation base to 8. + * Else + * Set accumulation base to 10. + * End if + * While there is a numeric character + * Accumulate the value. + * End while + * Return value. + * End if + * End if + * End while + * Return failure value. + * End tgetnum + * + */ + +tgetnum(id) +char *id; +{ + int value, base; + char *bp; + extern char *index(); + + bp = _tcpbuf; + while ((bp = index(bp,':')) != NULL) { + bp++; + if (*bp++ == id[0] && *bp != NULL && *bp++ == id[1]) { + if (*bp != NULL && *bp++ != '#') { + return(-1); + } else { + value = 0; + if (*bp == '0') { + base = 8; + } else { + base = 10; + } + while (isdigit(*bp)) { + value *= base; + value += (*bp++ - '0'); + } + return(value); + } + } + } + return(-1); +} diff --git a/termcap/tgetstr.c b/termcap/tgetstr.c @@ -0,0 +1,281 @@ +/************************************************************************ + * * + * Copyright (c) 1982, Fred Fish * + * All Rights Reserved * + * * + * This software and/or documentation is released for public * + * distribution for personal, non-commercial use only. * + * Limited rights to use, modify, and redistribute are hereby * + * granted for non-commercial purposes, provided that all * + * copyright notices remain intact and all changes are clearly * + * documented. The author makes no warranty of any kind with * + * respect to this product and explicitly disclaims any implied * + * warranties of merchantability or fitness for any particular * + * purpose. * + * * + ************************************************************************ + */ + + +/* + * LIBRARY FUNCTION + * + * tgetstr extract string capability from termcap entry + * + * KEY WORDS + * + * termcap + * + * SYNOPSIS + * + * char *tgetstr(id,area) + * char *id; + * char **area; + * + * DESCRIPTION + * + * Gets the string capability for <id>, placing it in + * the buffer at *area, and advancing *area to point + * to next available storage. + * + * For example, if the following capabilities are + * in the termcap file: + * + * ZZ=zzzz + * YY=yyyyyy + * WW=www + * + * then successive calls using YY, ZZ, and WW will + * build the following buffer: + * + * yyyyyy0zzzz0www0 + * + * The first call will return a pointer to yyyyyy, the + * second will return a pointer to zzzz and the third + * will return a pointer to www. Note that each + * string is null terminated, as are all C strings. + * + * Characters preceded by the carot character (\136) + * are mapped into the corresponding control character. + * For example, the two character sequence ^A becomes + * a single control-A (\001) character. + * + * The escape character is the normal C backslash and + * the normal C escape sequences are recognized, along + * with a special sequence for the ASCII escape character + * (\033). The recognized sequences are: + * + * \E => '\033' (ASCII escape character) + * \b => '\010' (ASCII backspace character) + * \f => '\014' (ASCII form feed character) + * \n => '\012' (ASCII newline/linefeed char) + * \r => '\015' (ASCII carriage return char) + * \t => '\011' (ASCII tab character) + * \ddd => '\ddd' (arbitrary ASCII digit) + * \x => 'x' (ordinary ASCII character) + * + */ + +#include <stdio.h> +# ifdef MSDOS +# define index strchr +# endif + +extern char *_tcpbuf; /* Termcap entry buffer pointer */ + +/* + * PSEUDO CODE + * + * Begin tgetstr + * Initialize pointer to the termcap entry buffer. + * While there is a field to process + * Skip over the field separator character. + * If this is the entry we want then + * If the entry is not a string then + * Return NULL. + * Else + * Transfer string and rtn pointer. + * End if + * End if + * End while + * Return NULL + * End tgetstr + * + */ + +char *tgetstr(id,area) +char *id; +char **area; +{ + char *bp; + extern char *index(); + char *decode(); + + bp = _tcpbuf; + while ((bp = index(bp,':')) != NULL) { + bp++; + if (*bp++ == id[0] && *bp != NULL && *bp++ == id[1]) { + if (*bp != NULL && *bp++ != '=') { + return(NULL); + } else { + return(decode(bp,area)); + } + } + } + return(NULL); +} + +/* + * INTERNAL FUNCTION + * + * decode transfer string capability, decoding escapes + * + * SYNOPSIS + * + * static char *decode(bp,area) + * char *bp; + * char **area; + * + * DESCRIPTION + * + * Transfers the string capability, up to the next ':' + * character, or null, to the buffer pointed to by + * the pointer in *area. Note that the initial + * value of *area and *area is updated to point + * to the next available location after the null + * terminating the transfered string. + * + * BUGS + * + * There is no overflow checking done on the destination + * buffer, so it better be large enough to hold + * all expected strings. + * + */ + +/* + * PSEUDO CODE + * + * Begin decode + * Initialize the transfer pointer. + * While there is an input character left to process + * Switch on input character + * Case ESCAPE: + * Decode and xfer the escaped sequence. + * Break + * Case CONTROLIFY: + * Controlify and xfer the next character. + * Advance the buffer pointer. + * Break + * Default: + * Xfer a normal character. + * End switch + * End while + * Null terminate the output string. + * Remember where the output string starts. + * Update the output buffer pointer. + * Return pointer to the output string. + * End decode + * + */ + +static char *decode(bp,area) +char *bp; +char **area; +{ + char *cp, *bgn; + char *do_esc(); + + cp = *area; + while (*bp != NULL && *bp != ':') { + switch(*bp) { + case '\\': + bp = do_esc(cp++,++bp); + break; + case '^': + *cp++ = *++bp & 037; + bp++; + break; + default: + *cp++ = *bp++; + break; + } + } + *cp++ = NULL; + bgn = *area; + *area = cp; + return(bgn); +} + +/* + * INTERNAL FUNCTION + * + * do_esc process an escaped sequence + * + * SYNOPSIS + * + * char *do_esc(out,in); + * char *out; + * char *in; + * + * DESCRIPTION + * + * Processes an escape sequence pointed to by + * in, transfering it to location pointed to + * by out, and updating the pointer to in. + * + */ + +/* + * PSEUDO CODE + * + * Begin do_esc + * If the first character is not a NULL then + * If is a digit then + * Set value to zero. + * For up to 3 digits + * Accumulate the sum. + * End for + * Transfer the sum. + * Else if character is in remap list then + * Transfer the remapped character. + * Advance the input pointer once. + * Else + * Simply transfer the character. + * End if + * End if + * Return updated input pointer. + * End do_esc + * + */ + +static char *maplist = { + "E\033b\bf\fn\nr\rt\t" +}; + +char *do_esc(out,in) +char *out; +char *in; +{ + int count; + char ch; + char *cp; + extern int isdigit(); + + if (*in != NULL) { + if (isdigit(*in)) { + ch = 0; + for (count = 0; count < 3 && isdigit(*in); in++) { + ch <<= 3; + ch |= (*in - '0'); + } + *out++ = ch; + } else if ((cp = index(maplist,*in)) != NULL) { + *out++ = *++cp; + in++; + } else { + *out++ = *in++; + } + } + return(in); +} diff --git a/termcap/tgoto.c b/termcap/tgoto.c @@ -0,0 +1,247 @@ +/************************************************************************ + * * + * Copyright (c) 1982, Fred Fish * + * All Rights Reserved * + * * + * This software and/or documentation is released for public * + * distribution for personal, non-commercial use only. * + * Limited rights to use, modify, and redistribute are hereby * + * granted for non-commercial purposes, provided that all * + * copyright notices remain intact and all changes are clearly * + * documented. The author makes no warranty of any kind with * + * respect to this product and explicitly disclaims any implied * + * warranties of merchantability or fitness for any particular * + * purpose. * + * * + ************************************************************************ + */ + + +/* + * LIBRARY FUNCTION + * + * tgoto expand cursor addressing string from cm capability + * + * KEY WORDS + * + * termcap + * + * SYNOPSIS + * + * char *tgoto(cm,destcol,destline) + * char *cm; + * int destcol; + * int destline; + * + * DESCRIPTION + * + * Returns cursor addressing string, decoded from the cm + * capability string, to move cursor to column destcol on + * line destline. + * + * The following sequences uses one input argument, either + * line or column, and place the appropriate substitution + * in the output string: + * + * %d substitute decimal value (in ASCII) + * %2 like %d but forces field width to 2 + * %3 like %d but forces field width to 3 + * %. like %c + * %+x like %c but adds ASCII value of x + * + * The following sequences cause processing modifications + * but do not "use up" one of the arguments. If they + * act on an argument they act on the next one to + * be converted. + * + * %>xy if next value to be converted is + * greater than value of ASCII char x + * then add value of ASCII char y. + * %r reverse substitution of line + * and column (line is substituted + * first by default). + * %i causes input values destcol and + * destline to be incremented. + * %% gives single % character in output. + * + * BUGS + * + * Does not implement some of the more arcane sequences for + * radically weird terminals (specifically %n, %B, & %D). + * If you have one of these you deserve whatever happens. + * + */ + +/* + * Miscellaneous stuff + */ + +#include <stdio.h> + +#define MAXARGS 2 + +static char *in; /* Internal copy of input string pointer */ +static char *out; /* Pointer to output array */ +static int args[MAXARGS]; /* Maximum number of args to convert */ +static int pcount; /* Count of args processed */ +static char output[64]; /* Converted string */ + + +/* + * PSEUDO CODE + * + * Begin tgoto + * If no string to process then + * Return pointer to error string. + * Else + * Initialize pointer to input string. + * Initialize pointer to result string. + * First arg is line number by default. + * Second arg is col number by default. + * No arguments processed yet. + * While there is another character to process + * If character is a not a % character then + * Simply copy to output. + * Else + * Process the control sequence. + * End if + * End while + * Return pointer to static output string. + * End if + * End tgoto + * + */ + +char *tgoto(cm,destcol,destline) +char *cm; +int destcol; +int destline; +{ + if (cm == NULL) { + return("OOPS"); + } else { + in = cm; + out = output; + args[0] = destline; + args[1] = destcol; + pcount = 0; + while (*in != NULL) { + if (*in != '%') { + *out++ = *in++; + } else { + process(); + } + } + return(output); + } +} + +/* + * INTERNAL FUNCTION + * + * process process the conversion/command sequence + * + * SYNOPSIS + * + * static process() + * + * DESCRIPTION + * + * Processes the sequence beginning with the % character. + * Directly manipulates the input string pointer, the + * output string pointer, and the arguments. Leaves + * the input string pointer pointing to the next character + * to be processed, and the output string pointer pointing + * to the next output location. If conversion of + * one of the numeric arguments occurs, then the pcount + * is incremented. + * + */ + +/* + * PSEUDO CODE + * + * Begin process + * Skip over the % character. + * Switch on next character after % + * Case 'd': + * Process %d type conversion (variable width). + * Reinitialize output pointer. + * Break; + * Case '2': + * Process %d type conversion (width 2). + * Reinitialize output pointer. + * Break; + * Case '3': + * Process %d type conversion (width 3). + * Reinitialize output pointer. + * Break; + * Case '.' + * Process %c type conversion. + * Break; + * Case '+': + * Process %c type conversion with offset. + * Break; + * Case '>': + * Process argument modification. + * Break; + * Case 'r': + * Process argument reversal. + * Break; + * Case 'i': + * Increment argument values. + * Break; + * Case '%': + * Copy to output, incrementing pointers. + * Break; + * End switch + * End process + * + */ + + +static process() +{ + int temp; + + in++; + switch(*in++) { + case 'd': + sprintf(out,"%d",args[pcount++]); + out = &output[strlen(output)]; + break; + case '2': + sprintf(out,"%02d",args[pcount++]); + out = &output[strlen(output)]; + break; + case '3': + sprintf(out,"%03d",args[pcount++]); + out = &output[strlen(output)]; + break; + case '.': + *out++ = args[pcount++]; + break; + case '+': + *out++ = args[pcount++] + *in++; + break; + case '>': + if (args[pcount] > *in++) { + args[pcount] += *in++; + } else { + in++; + } + break; + case 'r': + temp = args[pcount]; + args[pcount] = args[pcount+1]; + args[pcount+1] = temp; + break; + case 'i': + args[pcount]++; + args[pcount+1]++; + break; + case '%': + *out++ = '%'; + break; + } +} diff --git a/termcap/tputs.c b/termcap/tputs.c @@ -0,0 +1,218 @@ +/************************************************************************ + * * + * Copyright (c) 1982, Fred Fish * + * All Rights Reserved * + * * + * This software and/or documentation is released for public * + * distribution for personal, non-commercial use only. * + * Limited rights to use, modify, and redistribute are hereby * + * granted for non-commercial purposes, provided that all * + * copyright notices remain intact and all changes are clearly * + * documented. The author makes no warranty of any kind with * + * respect to this product and explicitly disclaims any implied * + * warranties of merchantability or fitness for any particular * + * purpose. * + * * + ************************************************************************ + */ + + +/* + * LIBRARY FUNCTION + * + * tputs output string with appropriate padding + * + * KEY WORDS + * + * termcap + * + * SYNOPSIS + * + * tputs(cp,affcnt,outc) + * char *cp; + * int affcnt; + * int (*outc)(); + * + * DESCRIPTION + * + * Outputs string pointed to by cp, using function outc, and + * following it with the appropriate number of padding characters. + * Affcnt contains the number of lines affected, which is used + * as a multiplier for the specified per line pad time. If + * per line pad count is not applicable, affcnt should be 1, + * NOT zero. + * + * The format of the string pointed to by cp is: + * + * [pad time][*]<string to send> + * + * where: pad time => time to delay in milliseconds + * * => specifies that time is per line + * + * The pad character is assumed to reside in the external + * variable "PC". Also, the external variable "ospeed" + * should contain the output speed of the terminal as + * encoded in /usr/include/sgtty.h (B0-B9600). + * + * BUGS + * + * Digit conversion is based on native character set + * being ASCII. + * + */ + +/* + * Miscellaneous stuff + */ + +#include <stdio.h> + +# ifndef MSDOS +extern char PC; /* Pad character to use */ +extern char ospeed; /* Encoding of output speed */ + +static int times[] = { + 0, /* Tenths of ms per char 0 baud */ + 2000, /* Tenths of ms per char 50 baud */ + 1333, /* Tenths of ms per char 75 baud */ + 909, /* Tenths of ms per char 110 baud */ + 743, /* Tenths of ms per char 134 baud */ + 666, /* Tenths of ms per char 150 baud */ + 500, /* Tenths of ms per char 200 baud */ + 333, /* Tenths of ms per char 300 baud */ + 166, /* Tenths of ms per char 600 baud */ + 83, /* Tenths of ms per char 1200 baud */ + 55, /* Tenths of ms per char 1800 baud */ + 41, /* Tenths of ms per char 2400 baud */ + 20, /* Tenths of ms per char 4800 baud */ + 10 /* Tenths of ms per char 9600 baud */ +}; +# endif + + +/* + * PSEUDO CODE + * + * Begin tgoto + * If string pointer is invalid then + * Return without doing anything. + * Else + * For each pad digit (if any) + * Do decimal left shift. + * Accumulate the lower digit. + * End for + * Adjust scale to tenths of milliseconds + * If there is a fractional field + * Skip the decimal point. + * If there is a valid tenths digit + * Accumulate the tenths. + * End if + * Discard remaining digits. + * End if + * If per line is specified then + * Adjust the pad time. + * Discard the per line flag char. + * End if + * While there are any characters left + * Send them out via output function. + * End while + * Transmit any padding required. + * End if + * End tgoto + * + */ + +tputs(cp,affcnt,outc) +char *cp; +int affcnt; +int (*outc)(); +{ + int ptime; /* Pad time in tenths of milliseconds */ + extern int isdigit(); + + if (cp == NULL || *cp == NULL) { + return; + } else { + for (ptime = 0; isdigit(*cp); cp++) { + ptime *= 10; + ptime += (*cp - '0'); + } + ptime *= 10; + if (*cp == '.') { + cp++; + if (isdigit(*cp)) { + ptime += (*cp++ - '0'); + } + while (isdigit(*cp)) {cp++;} + } + if (*cp == '*') { + ptime *= affcnt; + cp++; + } + while (*cp != NULL) { + (*outc)(*cp++); + } +# ifndef MSDOS + do_padding(ptime,outc); +# endif + } +} + +# ifndef MSDOS +/* + * FUNCTION + * + * do_padding transmit any pad characters required + * + * SYNOPSIS + * + * static do_padding(ptime,outc) + * int ptime; + * int (*outc)(); + * + * DESCRIPTION + * + * Does any padding required as specified by ptime (in tenths + * of milliseconds), the output speed given in the external + * variable ospeed, and the pad character given in the + * external variable PC. + * + */ + +/* + * PSEUDO CODE + * + * Begin do_padding + * If there is a non-zero pad time then + * If the external speed is in range then + * Look up the delay per pad character. + * Round pad time up by half a character. + * Compute number of characters to send. + * For each pad character to send + * Transmit the pad character. + * End for + * End if + * End if + * End do_padding + * + */ + +static do_padding(ptime,outc) +int ptime; +int (*outc)(); +{ + register int nchars; + register int tpc; + + if (ptime != 0) { + if (ospeed >= 0 && ospeed <= (sizeof(times)/ sizeof(int))) { + tpc = times[ospeed]; + ptime += (tpc / 2); + nchars = ptime / tpc; + for ( ; nchars > 0; --nchars) { + (*outc)(PC); + } + } + } +} +# endif