aboutsummaryrefslogtreecommitdiff
path: root/larn
diff options
context:
space:
mode:
authorB. Watson <yalhcru@gmail.com>2015-05-07 16:32:32 -0400
committerB. Watson <yalhcru@gmail.com>2015-05-07 16:32:32 -0400
commit013ac7742311556022304e8b30ca170d48b3a016 (patch)
tree53faa33e75991363f1a6dcc7edc83a66b70e6995 /larn
downloadbsd-games-extra-013ac7742311556022304e8b30ca170d48b3a016.tar.gz
initial commit
Diffstat (limited to 'larn')
-rw-r--r--larn/COPYRIGHT8
-rw-r--r--larn/Fixed.Bugs218
-rw-r--r--larn/Makefile83
-rw-r--r--larn/OWNER3
-rw-r--r--larn/README150
-rw-r--r--larn/action.c305
-rw-r--r--larn/bill.c163
-rw-r--r--larn/config.c50
-rw-r--r--larn/create.c606
-rw-r--r--larn/data.c664
-rw-r--r--larn/datfiles/larn.help140
-rw-r--r--larn/datfiles/larnmaze288
-rw-r--r--larn/datfiles/larnopts12
-rw-r--r--larn/diag.c411
-rw-r--r--larn/display.c637
-rw-r--r--larn/extern.h226
-rw-r--r--larn/fortune.c94
-rw-r--r--larn/global.c861
-rw-r--r--larn/header.h444
-rw-r--r--larn/help.c129
-rw-r--r--larn/io.c991
-rw-r--r--larn/larn.6157
-rw-r--r--larn/main.c1341
-rw-r--r--larn/monster.c1911
-rw-r--r--larn/moreobj.c296
-rw-r--r--larn/movem.c447
-rw-r--r--larn/nap.c23
-rw-r--r--larn/object.c1336
-rw-r--r--larn/pathnames.h38
-rw-r--r--larn/regen.c187
-rw-r--r--larn/savelev.c67
-rw-r--r--larn/scores.c855
-rw-r--r--larn/signal.c138
-rw-r--r--larn/store.c910
-rw-r--r--larn/tok.c235
35 files changed, 14424 insertions, 0 deletions
diff --git a/larn/COPYRIGHT b/larn/COPYRIGHT
new file mode 100644
index 0000000..2f66c0f
--- /dev/null
+++ b/larn/COPYRIGHT
@@ -0,0 +1,8 @@
+$NetBSD: COPYRIGHT,v 1.2 1995/03/23 08:33:02 cgd Exp $
+
+This entire subtree is copyright by Noah Morgan.
+The following copyright notice applies to all files found here. None of
+these files contain AT&T proprietary source code.
+_____________________________________________________________________________
+
+/* Larn is copyrighted 1986 by Noah Morgan. */
diff --git a/larn/Fixed.Bugs b/larn/Fixed.Bugs
new file mode 100644
index 0000000..e1bc278
--- /dev/null
+++ b/larn/Fixed.Bugs
@@ -0,0 +1,218 @@
+$NetBSD: Fixed.Bugs,v 1.2 1995/03/23 08:33:03 cgd Exp $
+
+This is a list of the fixes/enhancements made to larn V11.0 in Version 12.0.
+(Version numbers consist of 2 parts: ver.subver. When the save file format
+changes, ver must be bumped. This is why the next release of Larn is 12.0
+and not 11.1. This is used in the savefile routines to check for out-of-date
+save files). This list was mainly meant to be a record of what changed,
+for my own sanity. It's included for your benefit (Warning: SPOILER!):
+
+0. lprintf() in fileio.c (now called io.c) has been changed to use varargs
+ so that its variable number of arguments usage is now portable. Pyramids
+ primarily had this problem.
+
+1. Panic handler was added to signal.c. This routine catches fatal errors
+ like segmentation faults, bus errors, illegal instructions, etc., and
+ trys to performs a savegame() before dumping core. This helps prevent
+ the loss of a good game due to a game malfunction. Also, the name of the
+ signal received is printed, instead of just its number.
+
+2. The version number of the program is now selectable from the Makefile.
+ see the symbols VER and SUBVER.
+
+3. When at an altar, pray and donate 3000000000 gp. and ye used to receive
+ a whopping amount of gold due to a wraparound problem with the signed
+ ints. This has been fixed by using unsigned longs when asking for money
+ amounts.
+
+4. It was possible that when compiled with work hours checking, checkpointing
+ enabled, and having "play-day-play" in the .larnopts file a segmentation
+ fault would occur at its first attempt to do a checkpoint. This was due
+ to an improperly declared savefilename array in tok.c. This has been fixed.
+
+5. on level H, casting a missile weapon (mle cld ssp bal lit) off the edge of
+ the level would mess up the display, as it didn't know when to stop. This
+ is needless to say, fixed. Absolute bounds are now in effect for missile
+ type spells, see godirect() in monster.c.
+
+6. The create monster routine will now create monsters in random positions
+ around the player. Before, the 1st one would always be created to the
+ upper left.
+
+7. If you vpr or lit at a throne, it would summon a gnome king that you
+ would have to deal with. However, as each throne has only one king with it,
+ successive vpr's should not create more gnome kings. Presently, successive
+ vpr's will create more kings. This has been fixed.
+
+8. The mechanism to manage spheres of annihilation has been reworked to provide
+ a cleaner design and to eliminate some possible problems.
+
+9. The spell gen (genocide monsters) has been implemented.
+
+10. When dropping a ring of strength and having been weakened to STR=3 the
+ player might end up with a negative strength. Strength is now stored
+ in 2 variables, real strength, and strength bonuses. Only real strength
+ can now be weakened down to a minimum of 3, so unless you have a ring of
+ strength -3 or less, strengths below 3 should not occur.
+
+11. larn -h will now print out a list of all available command line options.
+
+12. larn -o<optsfile> now lets you specify a .larnopts file on the command
+ line. This was necessary as part of the solution to number 14 below.
+
+13. The "savefile:" statement has been aded to the .larnopts format to allow
+ specifying the savefilename (full path) for the savegame operation.
+ This too was needed as part of # 14 below.
+
+14. A player id facility has been added to larn. The complaint was that
+ the game used the userid to order the scoreboard, thus only one scoreboard
+ entry was allowed for each userid. If the compile time symbol UIDSCORE
+ is defined at compilation time (see Makefile), this will still be true.
+ However, if this define is omitted, the game will create and manage a
+ file called ".playerids" where names are taken from the specified
+ .larnopts file (now a command line option) and assigned a unique playerid.
+ playerid's will now be used to govern scoreboard entry posting. This
+ feature makes it easy for one person to have many characters, each
+ appearing on the scoreboard. Be kind to your fellow players!
+ The philosophy of one score per player gives more players the opportunity
+ to bask in glory for all to see!
+
+15. It is no longer required that the player be WIZID to create the scoreboard
+ or to examine the logfile. Anyone with the correct wizard's password can
+ now use these command line options (password is only needed to create/clear
+ the scoreboard). If you want to prevent players from zeroing the
+ scoreboard, change the wizard's password. (in config.c) By the way, wizards
+ may be alot of fun, but they are prevented from being placed on any
+ scoreboard. (for clarification)
+
+16. Monsters now have intelligence, that is some of them. This determines if
+ the monster moves using the previously stupid movement method, or by using
+ the new IMM (intelligent monster movement) algorithm. With IMM, monsters
+ will move around corners, avoid pits, traps, etc. With increasing levels
+ of difficulty, more monsters will be using IMM. Beware of IMM when
+ aggravated! Those little beasties can really find you!
+
+17. Added the scroll of life protection.
+
+18. Larn now consults the file ".holiday" to check for holidays if the TIMECHECK
+ option (no playing during working hours) is enabled. Before, larn knew
+ nothing about holidays. It should now let people play if it is a holiday.
+ The format for a .holiday entry is: "mmm dd yyyy comments . . .".
+
+19. In nap() and napms() it is possible that with nap(0) or napms(0) there
+ would be an infinite loop and the game would hang. The case of nap(0)
+ is now looked for.
+
+20. The granularity of gold piles has been increased. iarg[] has been changed
+ from char's to short's, so instead of 255 x 10^n granularity we now have
+ 32767 x 10^n granularity. This also means more than 255000 gp can be
+ dropped in one place. Not realistic, but it prevents a worthless
+ annoyance. Who said games were supposed to be realistic?
+
+21. Termcap capability has been added to larn. If the symbol VT100 is defined
+ in the makefile, the game will be compiled to use only VT100 compatible
+ terminals (Much more efficient). If the symbol VT100 is omitted, the game
+ will be compiled to use the termcap entry for whatever terminal you are
+ using. This involves an extra layer of output interpretation, as every
+ byte sent to the terminal must be inspected for control tokens.
+ Only 3 termcap entries need be found for the game to be functional:
+ CM (cursor movement), CE (clear to end of line), and CL (clear screen).
+ For a better display, the following are optional: AL (insert line), DL
+ (delete line), SO (Standout begin), SE (Standout end), and CD (clear to end
+ of screen). The .larn.help file was left as is, with VT100 escape
+ sequences in it. If the termcap version of larn reads it, it is translated
+ for the desired terminal type. The .mail60* files have been removed, and
+ their text is now included in bill.c so it can be used with any terminal.
+ Note: If compiled for termcap, and using a VT100, the display will act
+ a little different. This is because the VT100 does not have insert line/
+ delete line codes, and the scrolling region must be simulated with vertical
+ wraparound instead of scrolling. Thanks goes to Michiel Huisjes for the
+ original termcap patch.
+
+22. When playing as wizard, if you go down stairs on 10 or V3, or up stairs
+ on H, 1, or V1, etc. you would be placed in a phantom zone where the display
+ was really weird ([-1] subscripting), and would eventually lead to a
+ segmentation fault. Stairs and volcano shafts now check for the level
+ they are being used on.
+
+23. In response to some sites having only unsigned chars (flame the
+ manufacturer), the chars that were used to store positive and negative
+ numbers have been changed to shorts. This includes diroffx[], diroffy[],
+ iarg[][][], ivenarg[], and some others. I believe the changes are correct,
+ but I have none of these machines to try it out on. (Volunteers?)
+
+24. The function fullhit(n) in monster.c was supposed to return the damage
+ done by n full hits on a monster. It only returned the damage for ONE hit,
+ thus severely limiting the usefulness of the web and sle spells.
+
+25. Someone said that they were getting segmentation faults when they were
+ reading scrolls as the wizard. I couldn't find the problem, which may
+ have had something to do with the signed char problem mentioned above.
+ However, I've added a check in read_scroll() and quaff_potion() to trap
+ any scroll or potion types that are not in the game.
+
+26. "vt125" has been added to the acceptable terminal list
+ (checked only if compiled with -DVT100).
+
+27. In savegame() and restoregame(), there was a 6 hardwired into the i/o
+ statements which assumed the size of struct cel was 6. On some machines
+ this caused the rightmost part of each level to not be saved in a savefile.
+ These 6's have been replaced with sizeof(struct cel), and should now be
+ portable.
+
+28. The option "no-beep" has been added to the .larnopts file. When specified,
+ beeping is inhibited at the terminal.
+
+29. When becoming wizard, no longer to you wear the ring of protection, and
+ null scrolls and potions are no longer created.
+
+30. Many spelling errors have been fixed, both in player messages, and in the
+ code itself. A thanks goes to Mars Gralia who sent me a detailed list of
+ the mistakes.
+
+31. When a player wins a game, if getlogin() fails, a segmentation fault will
+ result, because the NULL returned from getlogin() is used as a pointer.
+ This call has been replaced (now using loginname already determined).
+ Also, the mail creation upon winning has been rewritten, mainly to allow
+ termcapping of the text.
+
+32. The Larn Revenue Service will now always appear on level H. Before, it
+ was only created if the player had outstanding taxes. In that multiple
+ save files per player are now more possible, this was seen as incorrect.
+
+33. Input buffer flushing is now in effect. If the input char queue exceeds
+ 5 bytes, the excess is discarded. Also, if the player hits or gets hit
+ all input bytes are flushed (within 1). This relieves the situation
+ where many moves have been typed ahead of the display and the player keeps
+ getting hit while the queue of moves is processed.
+
+34. When a savefile has been altered, a warning message is displayed to the
+ effect that you've cheated, and you will not be placed on the normal
+ scoreboard. If you then save the game, and start 'er up again, memory
+ of the cheating was lost. This has been fixed, by letting the scoreboard
+ routines consult the cheating flag. Also, the I node number of the
+ savefile is written into the savefile, so cp'ing, etc., will avail the
+ cheater not. If high security is needed, the game should be run suid.
+ This suid mode has not been made the default because most installations
+ do not want to install it that way.
+
+35. The sources have been run through lint, and most of lint's complaints have
+ been taken care of. An attempt was made to adjust the code for 16 bit int
+ machines. Many casts to long have been put in. I don't know if it will
+ run on a 16 bitter, but it should be closer to that end.
+
+36. When larn starts up, if it can't find the scoreboard, it will now make a
+ blank one instead of complaining that there is no scoreboard. It is not
+ necessary to do "larn -c" to initially create the scoreboard.
+
+37. When listing out the logfile (larn -l), the error message "error reading
+ from input file" has been fixed. Also, the date & time of a player's
+ demise is now included in the logfile.
+
+38. When casting web or sle into a mirror, the game will no longer bash the
+ player. Instead, the player will either fall asleep or get stuck in his
+ web.
+
+39. Items like cookies, books, chests, swords of slashing, and Bessmann's
+ flailing hammer can now be sold at the trading post.
+
diff --git a/larn/Makefile b/larn/Makefile
new file mode 100644
index 0000000..f5d6747
--- /dev/null
+++ b/larn/Makefile
@@ -0,0 +1,83 @@
+# $NetBSD: Makefile,v 1.21 2011/08/16 11:19:41 christos Exp $
+# @(#)Makefile 5.12 (Berkeley) 5/30/93
+
+# EXTRA
+# Incorporates code to gather additional performance statistics
+#
+# TERMIO
+# Use sysv termio
+# TERMIOS
+# Use posix termios
+# BSD
+# Use BSD specific features (mostly timer and signal stuff)
+# BSD4.1
+# Use BSD4.1 to avoid some 4.2 dependencies (must be used with
+# BSD above; do not mix with SYSV)
+# HIDEBYLINK
+# If defined, the program attempts to hide from ps
+# DOCHECKPOINTS
+# If not defined, checkpoint files are periodically written by the
+# larn process (no forking) if enabled in the .larnopts description
+# file. Checkpointing is handy on an unreliable system, but takes
+# CPU. Inclusion of DOCHECKPOINTS will cause fork()ing to perform the
+# checkpoints (again if enabled in the .larnopts file). This usually
+# avoids pauses in larn while the checkpointing is being done (on
+# large machines).
+# VER
+# This is the version of the software, example: 12
+# SUBVER
+# This is the revision of the software, example: 1
+# FLUSHNO=#
+# Set the input queue excess flushing threshold (default 5)
+# NOVARARGS
+# Define for systems that don't have varargs (a default varargs will
+# be used).
+# MACRORND
+# Define to use macro version of rnd() and rund() (fast and big)
+# UIDSCORE
+# Define to use user id's to manage scoreboard. Leaving this out will
+# cause player id's from the file ".playerids" to be used instead.
+# (.playerids is created upon demand). Only one entry per id # is
+# allowed in each scoreboard (winning & non-winning).
+# VT100
+# Compile for using vt100 family of terminals. Omission of this
+# define will cause larn to use termcap, but it will be MUCH slower
+# due to an extra layer of output interpretation. Also, only VT100
+# mode allows 2 different standout modes, inverse video, and bold video.
+# And only in VT100 mode is the scrolling region of the terminal used
+# (much nicer than insert/delete line sequences to simulate it, if
+# VT100 is omitted).
+# NONAP
+# This causes napms() to return immediately instead of delaying n
+# milliseconds. This define may be needed on some systems if the nap
+# stuff does not work correctly (possible hang). nap() is primarilly
+# used to delay for effect when casting missile type spells.
+# NOLOG
+# Turn off logging.
+
+.include <bsd.own.mk>
+
+PROG= larn
+MAN= larn.6
+# 20150211 bkw: add -DVT100
+CPPFLAGS+=-DSYSV -DVER=12 -DSUBVER=0 -DNONAP -DUIDSCORE -DTERMIOS -DVT100
+#CPPFLAGS+=-DBSD -DVER=12 -DSUBVER=0 -DNONAP -DUIDSCORE -DTERMIOS -DVT100
+SRCS= main.c object.c create.c tok.c display.c global.c data.c io.c \
+ monster.c store.c diag.c help.c config.c nap.c bill.c scores.c \
+ signal.c action.c moreobj.c movem.c regen.c fortune.c savelev.c
+DPADD= ${LIBTERMINFO}
+# 20150209 bkw: get rid of -lterminfo, add -lcurses -lbsd
+LDADD=-lcurses -lbsd
+HIDEGAME=hidegame
+SETGIDGAME=yes
+
+.if ${MKSHARE} != "no"
+DAT=larnmaze larnopts larn.help
+FILES=${DAT:S@^@${.CURDIR}/datfiles/@g}
+FILESDIR=/usr/share/games/larn
+.endif
+
+COPTS.display.c += -Wno-format-nonliteral
+COPTS.monster.c += -Wno-format-nonliteral
+
+.include <bsd.prog.mk>
diff --git a/larn/OWNER b/larn/OWNER
new file mode 100644
index 0000000..06aaf47
--- /dev/null
+++ b/larn/OWNER
@@ -0,0 +1,3 @@
+ Noah Morgan
+ panda!condor!noah
+ GenRad Inc. Bolton, MA
diff --git a/larn/README b/larn/README
new file mode 100644
index 0000000..5abeadb
--- /dev/null
+++ b/larn/README
@@ -0,0 +1,150 @@
+$NetBSD: README,v 1.2 1995/03/23 08:33:07 cgd Exp $
+
+Larn is a dungeon type game program. Larn is a adventure/action game similar
+in concept to rogue or hack, but with a much different feel.
+Try it, you'll like it!
+
+You will have to edit the Makefile to reflect your configuration. Define
+LARNHOME as the place where the larn auxiliary files will reside, and
+BINDIR as the place where the larn executable should be placed. Type
+"make" to compile, or "make all" to compile and install ("make install"
+does just the install).
+
+Here's a list of what is in each of the various source files:
+
+Fixed.Bugs this is a list of the things that were changed
+ since ver 11.0
+Makefile makefile script to compile the program
+Make.lint makefile script to run larn sources through lint
+README this is what you are now reading
+bill.c code for the letters of praise if player wins
+config.c data definitions for the installation dependent data --
+ savefilenames, where the scorefiles are, etc.
+create.c code to create the dungeon and all objects
+data.c data definitions for the game -- no code here
+diag.c code to produce diagnostic data for wizards, & savegame stuff
+display.c code to update the display on the screen
+fortune.c code for the fortune cookies
+global.c code for globally used functions that are specific to larn
+header.h constant and structure definitions
+help.c code for the help screens in the game of larn
+.holidays data file which lists upcoming holidays
+io.c code to handle file and terminal i/o
+.larn.help.uue larn help file (UUENCODED)
+.larnmaze data file for pre-made mazes
+.larnopts a sample .larnopts option data file
+.lfortune data file which contains the hints
+main.c code for the main command control and parsing
+monster.c code to handle attack and defense modes with monsters
+moreobj.c code for the fountains, altars, thrones
+movem.c code to move the monsters around the dungeon
+nap.c code to sleep for less than a second
+object.c code to handle objects in the dungeon
+regen.c code to regenerate the player and advance game time
+savelev.c code to get/put a level from level storage into working
+ level memory
+scores.c code to process and manage the scoreboard
+signal.c code to handle UNIX signals that are trapped
+store.c code for the larn thrift shoppe, bank, trading post, lrs
+tok.c code for the input front end and options file processing
+
+To find out how to play the game, run it and type in a '?' to get the help
+screens. By the way, the wizards password is "pvnert(x)" and to become wizard
+type in an underscore, you are then prompted for the password. Wizards are
+non-scoring characters that get enlightenment, everlasting expanded
+awareness, and one of every object in the game. They help the author to debug
+the game.
+
+Note regarding the wizard id: If you are using userid's, then WIZID must be
+set to the userid of the person who can become wizard. If you are using
+player id's, WIZID must be set to the playerid (edit file .playerids if needed)
+of the player who can become wizard.
+
+You may want to clear out the scoreboard. The command "larn -c" will make a
+new scoreboard. It will prompt you for the wizards password.
+
+BUGS & FIXES:
+
+James McNamara has volunteered to maintain the latest sources, and provide
+latest bug fixes to anyone who asks. Both James and I will field requests for
+sources, for those who ask.
+
+ ___ Prince of Gems (alias Noah Morgan)
+ /. \ USENET: panda!condor!noah
+ \ / at GenRad Inc. Bolton MA
+ \ /
+ v
+
+Below is some additional info about the installation of larn:
+
+Install: Notes on the game LARN installation.
+Larn is copyrighted 1986 by Noah Morgan.
+This file (below) originally by James D. McNamara, last update 7/27/86 by nm
+
+THIS DISTRIBUTION:
+
+ You should receive six (6) shar files, which are:
+
+ larn.part-1
+ larn.part-2
+ larn.part-3
+ larn.part-4
+ larn.part-5
+ larn.part-6
+
+I. Use /bin/sh (or your system equivalent) to "unravel" shar files
+ larn.part-1, ..., larn.part-6. I suggest you do this directly
+ into $LARNHOME (See Section III.). Notable files:
+
+ README - The author's how-to.
+ MANIFEST - Files you should have.
+
+III. Edit a copy of "Makefile" and leave the edited version in $LARNHOME.
+
+All the "configuration" options are tidily near the top of the "Makefile."
+Here are the ones you probably will want to edit:
+
+LARNHOME I specified (literally) the directory, with path from root,
+ where "larn" will reside. This included where I put the *.c files,
+ it is where the *.o files ended up, as well as all data and *.h files.
+ i suspect the *.c and intallation-documentation files can be moved off,
+ but the data and bits must all remain here for execution.
+
+BINDIR I specified (literally) the directory, with path from root,
+ where the executable "larn" will reside. The "Makefile" will dump
+ the "a.out", named "larn", in this directory. My BINDIR was not
+ my LARNHOME, so $BINDIR/larn was the ONLY file dumed here. You'll
+ probably have to chmod it for public execute, etc.
+
+
+OPTIONS This is how *I* specified them... they are documented in-line:
+ OPTIONS = -DWIZZARD -DWIZID=157 -DEXTRA -DBSD -DSAVEINHOME
+
+IV. Compile the bugger. Read "README" before you do. You have a couple
+ of options here:
+
+ make - will not install, suspect good for updates.
+ make all - compile (and) intall
+ make install - just install
+
+ I did "make" and then "make install" -- seems to work "ok", but
+ "make all" probably safer, if I had known. Note that "Makefile"
+ is the default file for "make."
+
+V. Execute and have fun. If wizard code "ok", larn -c will refresh the
+ scoreboard. Play and win (or get killed) to put somebody on the
+ scoreboard.
+
+VI. BUGS and FIXES.
+
+ Please forward any bug-fixes in these regards to me (or Noah), so I may
+ compile a fix-list for other installers. Thanks.
+
+Regards,
+===============================================================================
+James D. McNamara CSNET: jim@bu-cs
+ ARPANET: jim%bu-cs@csnet-relay
+ UUCP: ...harvard!bu-cs!jim
+ BITNET: jim%bu-cs%csnet-relay.arpa@wiscvm
+===============================================================================
+
diff --git a/larn/action.c b/larn/action.c
new file mode 100644
index 0000000..6fe1a88
--- /dev/null
+++ b/larn/action.c
@@ -0,0 +1,305 @@
+/* $NetBSD: action.c,v 1.1 2008/02/19 06:05:26 dholland Exp $ */
+
+/*
+ * action.c Larn is copyrighted 1986 by Noah Morgan.
+ *
+ * Routines in this file:
+ *
+ * ...
+ */
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: action.c,v 1.1 2008/02/19 06:05:26 dholland Exp $");
+#endif /* not lint */
+#include <stdlib.h>
+#include <unistd.h>
+#include "header.h"
+#include "extern.h"
+
+static void ohear(void);
+
+/*
+ * act_remove_gems
+ *
+ * Remove gems from a throne.
+ *
+ * arg is zero if there is a gnome king associated with the throne.
+ *
+ * Assumes that cursors() has been called previously, and that a check
+ * has been made that the throne actually has gems.
+ */
+void
+act_remove_gems(int arg)
+{
+ int i, k;
+
+ k = rnd(101);
+ if (k < 25) {
+ for (i = 0; i < rnd(4); i++)
+ creategem(); /* gems pop off the
+ * throne */
+ item[playerx][playery] = ODEADTHRONE;
+ know[playerx][playery] = 0;
+ } else if (k < 40 && arg == 0) {
+ createmonster(GNOMEKING);
+ item[playerx][playery] = OTHRONE2;
+ know[playerx][playery] = 0;
+ } else
+ lprcat("\nnothing happens");
+}
+
+/*
+ * act_sit_throne
+ *
+ * Sit on a throne.
+ *
+ * arg is zero if there is a gnome king associated with the throne
+ *
+ * Assumes that cursors() has been called previously.
+ */
+void
+act_sit_throne(int arg)
+{
+ int k;
+
+ k = rnd(101);
+ if (k < 30 && arg == 0) {
+ createmonster(GNOMEKING);
+ item[playerx][playery] = OTHRONE2;
+ know[playerx][playery] = 0;
+ } else if (k < 35) {
+ lprcat("\nZaaaappp! You've been teleported!\n");
+ beep();
+ oteleport(0);
+ } else
+ lprcat("\nnothing happens");
+}
+
+/*
+ * Code to perform the action of drinking at a fountain. Assumes that
+ * cursors() has already been called, and that a check has been made
+ * that the player is actually standing at a live fountain.
+ */
+void
+act_drink_fountain(void)
+{
+ int x;
+
+ if (rnd(1501) < 2) {
+ lprcat("\nOops! You seem to have caught the dreadful sleep!");
+ beep();
+ lflush();
+ sleep(3);
+ died(280);
+ return;
+ }
+ x = rnd(100);
+ if (x < 7) {
+ c[HALFDAM] += 200 + rnd(200);
+ lprcat("\nYou feel a sickness coming on");
+ } else if (x < 13)
+ quaffpotion(23); /* see invisible */
+ else if (x < 45)
+ lprcat("\nnothing seems to have happened");
+ else if (rnd(3) != 2)
+ fntchange(1); /* change char levels upward */
+ else
+ fntchange(-1); /* change char levels
+ * downward */
+ if (rnd(12) < 3) {
+ lprcat("\nThe fountains bubbling slowly quiets");
+ item[playerx][playery] = ODEADFOUNTAIN; /* dead fountain */
+ know[playerx][playery] = 0;
+ }
+}
+
+/*
+ * Code to perform the action of washing at a fountain. Assumes that
+ * cursors() has already been called and that a check has been made
+ * that the player is actually standing at a live fountain.
+ */
+void
+act_wash_fountain(void)
+{
+ int x;
+
+ if (rnd(100) < 11) {
+ x = rnd((level << 2) + 2);
+ lprintf("\nOh no! The water was foul! You suffer %ld hit points!", (long) x);
+ lastnum = 273;
+ losehp(x);
+ bottomline();
+ cursors();
+ } else if (rnd(100) < 29)
+ lprcat("\nYou got the dirt off!");
+ else if (rnd(100) < 31)
+ lprcat("\nThis water seems to be hard water! The dirt didn't come off!");
+ else if (rnd(100) < 34)
+ createmonster(WATERLORD); /* make water lord */
+ else
+ lprcat("\nnothing seems to have happened");
+}
+
+/*
+ * Perform the actions associated with altar desecration.
+ */
+void
+act_desecrate_altar(void)
+{
+ if (rnd(100) < 60) {
+ createmonster(makemonst(level + 2) + 8);
+ c[AGGRAVATE] += 2500;
+ } else if (rnd(101) < 30) {
+ lprcat("\nThe altar crumbles into a pile of dust before your eyes");
+ forget(); /* remember to destroy
+ * the altar */
+ } else
+ lprcat("\nnothing happens");
+}
+
+/*
+ * Perform the actions associated with praying at an altar and giving
+ * a donation.
+ */
+void
+act_donation_pray(void)
+{
+ long amt;
+
+ lprcat("\n\n");
+ cursor(1, 24);
+ cltoeoln();
+ cursor(1, 23);
+ cltoeoln();
+ lprcat("how much do you donate? ");
+ amt = readnum((long) c[GOLD]);
+ if (amt < 0 || c[GOLD] < amt) {
+ lprcat("\nYou don't have that much!");
+ return;
+ }
+ c[GOLD] -= amt;
+ if (amt < c[GOLD] / 10 || amt < rnd(50)) {
+ createmonster(makemonst(level + 1));
+ c[AGGRAVATE] += 200;
+ } else if (rnd(101) > 50) {
+ ohear();
+ return;
+ } else if (rnd(43) == 5) {
+ if (c[WEAR])
+ lprcat("\nYou feel your armor vibrate for a moment");
+ enchantarmor();
+ return;
+ } else if (rnd(43) == 8) {
+ if (c[WIELD])
+ lprcat("\nYou feel your weapon vibrate for a moment");
+ enchweapon();
+ return;
+ } else
+ lprcat("\nThank You.");
+ bottomline();
+}
+
+/*
+ * Performs the actions associated with 'just praying' at the altar. Called
+ * when the user responds 'just pray' when in prompt mode, or enters 0 to
+ * the money prompt when praying.
+ *
+ * Assumes cursors(), and that any leading \n have been printed.
+ */
+void
+act_just_pray(void)
+{
+ if (rnd(100) < 75)
+ lprcat("\nnothing happens");
+ else if (rnd(13) < 4)
+ ohear();
+ else if (rnd(43) == 10) {
+ if (c[WEAR])
+ lprcat("\nYou feel your armor vibrate for a moment");
+ enchantarmor();
+ return;
+ } else if (rnd(43) == 10) {
+ if (c[WIELD])
+ lprcat("\nYou feel your weapon vibrate for a moment");
+ enchweapon();
+ return;
+ } else
+ createmonster(makemonst(level + 1));
+}
+
+/*
+ * Function to cast a +3 protection on the player
+ */
+static void
+ohear(void)
+{
+ lprcat("\nYou have been heard!");
+ if (c[ALTPRO] == 0)
+ c[MOREDEFENSES] += 3;
+ c[ALTPRO] += 500; /* protection field */
+ bottomline();
+}
+
+/*
+ * Performs the act of ignoring an altar.
+ *
+ * Assumptions: cursors() has been called.
+ */
+void
+act_ignore_altar(void)
+{
+ if (rnd(100) < 30) {
+ createmonster(makemonst(level + 1));
+ c[AGGRAVATE] += rnd(450);
+ } else
+ lprcat("\nnothing happens");
+}
+
+/*
+ * Performs the act of opening a chest.
+ *
+ * Parameters: x,y location of the chest to open.
+ * Assumptions: cursors() has been called previously
+ */
+void
+act_open_chest(int x, int y)
+{
+ int i, k;
+
+ k = rnd(101);
+ if (k < 40) {
+ lprcat("\nThe chest explodes as you open it");
+ beep();
+ i = rnd(10);
+ lastnum = 281; /* in case he dies */
+ lprintf("\nYou suffer %ld hit points damage!", (long) i);
+ checkloss(i);
+ switch (rnd(10)) { /* see if he gets a
+ * curse */
+ case 1:
+ c[ITCHING] += rnd(1000) + 100;
+ lprcat("\nYou feel an irritation spread over your skin!");
+ beep();
+ break;
+
+ case 2:
+ c[CLUMSINESS] += rnd(1600) + 200;
+ lprcat("\nYou begin to lose hand to eye coordination!");
+ beep();
+ break;
+
+ case 3:
+ c[HALFDAM] += rnd(1600) + 200;
+ beep();
+ lprcat("\nA sickness engulfs you!");
+ break;
+ };
+ item[x][y] = know[x][y] = 0;
+ if (rnd(100) < 69)
+ creategem(); /* gems from the chest */
+ dropgold(rnd(110 * iarg[x][y] + 200));
+ for (i = 0; i < rnd(4); i++)
+ something(iarg[x][y] + 2);
+ } else
+ lprcat("\nnothing happens");
+}
diff --git a/larn/bill.c b/larn/bill.c
new file mode 100644
index 0000000..961c424
--- /dev/null
+++ b/larn/bill.c
@@ -0,0 +1,163 @@
+/* $NetBSD: bill.c,v 1.11 2011/08/29 20:30:37 joerg Exp $ */
+
+/*-
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)bill.c 5.2 (Berkeley) 5/28/91";
+#else
+__RCSID("$NetBSD: bill.c,v 1.11 2011/08/29 20:30:37 joerg Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/file.h>
+#include <sys/wait.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <paths.h>
+#include "header.h"
+#include "extern.h"
+
+/* bill.c Larn is copyrighted 1986 by Noah Morgan. */
+
+static const char *mail[] = {
+ "From: the LRS (Larn Revenue Service)\n",
+ "~s undeclared income\n",
+ "\n We have heard you survived the caverns of Larn. Let me be the",
+ "\nfirst to congratulate you on your success. It was quite a feat.",
+ "\nIt was also very profitable for you...",
+ "\n\n The Dungeon Master has informed us that you brought",
+ "1",
+ "\ncounty of Larn is in dire need of funds, we have spared no time",
+ "2",
+ "\nof this notice, and is due within 5 days. Failure to pay will",
+ "\nmean penalties. Once again, congratulations, We look forward",
+ "\nto your future successful expeditions.\n",
+ NULL,
+ "From: His Majesty King Wilfred of Larndom\n",
+ "~s a noble deed\n",
+ "\n I have heard of your magnificent feat, and I, King Wilfred,",
+ "\nforthwith declare today to be a national holiday. Furthermore,",
+ "\nhence three days, ye be invited to the castle to receive the",
+ "\nhonour of Knight of the realm. Upon thy name shall it be written...",
+ "\n\nBravery and courage be yours.",
+ "\n\nMay you live in happiness forevermore...\n",
+ NULL,
+ "From: Count Endelford\n",
+ "~s You Bastard!\n",
+ "\n I have heard (from sources) of your journey. Congratulations!",
+ "\nYou Bastard! With several attempts I have yet to endure the",
+ " caves,\nand you, a nobody, makes the journey! From this time",
+ " onward, bewarned\nupon our meeting you shall pay the price!\n",
+ NULL,
+ "From: Mainair, Duke of Larnty\n",
+ "~s High Praise\n",
+ "\n With certainty, a hero I declare to be amongst us! A nod of",
+ "\nfavour I send to thee. Me thinks Count Endelford this day of",
+ "\nright breath'eth fire as of dragon of whom ye are slayer. I",
+ "\nyearn to behold his anger and jealously. Should ye choose to",
+ "\nunleash some of thy wealth upon those who be unfortunate, I,",
+ "\nDuke Mainair, shall equal thy gift also.\n",
+ NULL,
+ "From: St. Mary's Children's Home\n",
+ "~s these poor children\n",
+ "\n News of your great conquests has spread to all of Larndom.",
+ "\nMight I have a moment of a great adventurers's time? We here at",
+ "\nSt. Mary's Children's Home are very poor, and many children are",
+ "\nstarving. Disease is widespread and very often fatal without",
+ "\ngood food. Could you possibly find it in your heart to help us",
+ "\nin our plight? Whatever you could give will help much.",
+ "\n(your gift is tax deductible)\n",
+ NULL,
+ "From: The National Cancer Society of Larn\n",
+ "~s hope\n",
+ "\nCongratulations on your successful expedition. We are sure much",
+ "\ncourage and determination were needed on your quest. There are",
+ "\nmany though, that could never hope to undertake such a journey",
+ "\ndue to an enfeebling disease -- cancer. We at the National",
+ "\nCancer Society of Larn wish to appeal to your philanthropy in",
+ "\norder to save many good people -- possibly even yourself a few",
+ "\nyears from now. Much work needs to be done in researching this",
+ "\ndreaded disease, and you can help today. Could you please see it",
+ "\nin your heart to give generously? Your continued good health",
+ "\ncan be your everlasting reward.\n",
+ NULL
+};
+
+/*
+ * function to mail the letters to the player if a winner
+ */
+
+void
+mailbill(void)
+{
+ int i;
+ char fname[32];
+ char buf[128];
+ const char **cp;
+ int fd;
+
+ wait(0);
+ if (fork() == 0) {
+ resetscroll();
+ cp = mail;
+ snprintf(fname, sizeof(fname), "%slarnmail.XXXXXX", _PATH_TMP);
+ for (i = 0; i < 6; i++) {
+ if ((fd = mkstemp(fname)) == -1)
+ exit(0);
+ while (*cp != NULL) {
+ if (*cp[0] == '1') {
+ snprintf(buf, sizeof(buf),
+ "\n%ld gold pieces back with you from your journey. As the",
+ (long) c[GOLD]);
+ write(fd, buf, strlen(buf));
+ } else if (*cp[0] == '2') {
+ snprintf(buf, sizeof(buf),
+ "\nin preparing your tax bill. You owe %ld gold pieces as",
+ (long) c[GOLD] * TAXRATE);
+ write(fd, buf, strlen(buf));
+ } else
+ write(fd, *cp, strlen(*cp));
+ cp++;
+ }
+ cp++;
+
+ close(fd);
+ snprintf(buf, sizeof(buf),
+ "mail -I %s < %s > /dev/null", loginname, fname);
+ system(buf);
+ unlink(fname);
+ }
+ }
+ exit(0);
+}
diff --git a/larn/config.c b/larn/config.c
new file mode 100644
index 0000000..47aa03a
--- /dev/null
+++ b/larn/config.c
@@ -0,0 +1,50 @@
+/* $NetBSD: config.c,v 1.6 2008/01/28 05:38:53 dholland Exp $ */
+
+/*
+ * config.c -- This defines the installation dependent variables.
+ * Some strings are modified later. ANSI C would
+ * allow compile time string concatenation, we must
+ * do runtime concatenation, in main.
+ *
+ * Larn is copyrighted 1986 by Noah Morgan.
+ */
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: config.c,v 1.6 2008/01/28 05:38:53 dholland Exp $");
+#endif /* not lint */
+
+#include "header.h"
+#include "pathnames.h"
+
+/*
+ * All these strings will be appended to in main() to be complete filenames
+ */
+
+/* the game save filename */
+char savefilename[1024];
+
+/* the logging file */
+char logfile[] = _PATH_LOG;
+
+/* the help text file */
+char helpfile[] = _PATH_HELP;
+
+/* the score file */
+char scorefile[] = _PATH_SCORE;
+
+/* the maze data file */
+char larnlevels[] = _PATH_LEVELS;
+
+/* the .larnopts filename */
+char optsfile[1024] = "/.larnopts";
+
+/* the player id datafile name */
+char playerids[] = _PATH_PLAYERIDS;
+
+char diagfile[] = "Diagfile"; /* the diagnostic filename */
+char ckpfile[] = "Larn12.0.ckp"; /* the checkpoint filename */
+const char *password = "pvnert(x)"; /* the wizards password <=32 */
+char psname[PSNAMESIZE] = "larn"; /* the process name */
+
+#define WIZID 1
+int wisid = 0; /* the user id of the only person who can be wizard */
diff --git a/larn/create.c b/larn/create.c
new file mode 100644
index 0000000..440b918
--- /dev/null
+++ b/larn/create.c
@@ -0,0 +1,606 @@
+/* $NetBSD: create.c,v 1.12 2012/06/19 05:30:43 dholland Exp $ */
+
+/* create.c Larn is copyrighted 1986 by Noah Morgan. */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: create.c,v 1.12 2012/06/19 05:30:43 dholland Exp $");
+#endif /* not lint */
+
+#include "header.h"
+#include "extern.h"
+#include <unistd.h>
+
+static void makemaze(int);
+static int cannedlevel(int);
+static void treasureroom(int);
+static void troom(int, int, int, int, int, int);
+static void makeobject(int);
+static void fillmroom(int, int, int);
+static void froom(int, int, int);
+static void fillroom(int, int);
+static void sethp(int);
+static void checkgen(void);
+
+/*
+ makeplayer()
+
+ subroutine to create the player and the players attributes
+ this is called at the beginning of a game and at no other time
+ */
+void
+makeplayer(void)
+{
+ int i;
+ scbr();
+ clear();
+ c[HPMAX] = c[HP] = 10; /* start player off with 15 hit points */
+ c[LEVEL] = 1; /* player starts at level one */
+ c[SPELLMAX] = c[SPELLS] = 1; /* total # spells starts off as 3 */
+ c[REGENCOUNTER] = 16;
+ c[ECOUNTER] = 96; /* start regeneration correctly */
+ c[SHIELD] = c[WEAR] = c[WIELD] = -1;
+ for (i = 0; i < 26; i++)
+ iven[i] = 0;
+ spelknow[0] = spelknow[1] = 1; /* he knows protection, magic missile */
+ if (c[HARDGAME] <= 0) {
+ iven[0] = OLEATHER;
+ iven[1] = ODAGGER;
+ ivenarg[1] = ivenarg[0] = c[WEAR] = 0;
+ c[WIELD] = 1;
+ }
+ playerx = rnd(MAXX - 2);
+ playery = rnd(MAXY - 2);
+ oldx = 0;
+ oldy = 25;
+ gltime = 0; /* time clock starts at zero */
+ cbak[SPELLS] = -50;
+ for (i = 0; i < 6; i++)
+ c[i] = 12; /* make the attributes, ie str, int, etc. */
+ recalc();
+}
+
+
+/*
+ newcavelevel(level)
+ int level;
+
+ function to enter a new level. This routine must be called anytime the
+ player changes levels. If that level is unknown it will be created.
+ A new set of monsters will be created for a new level, and existing
+ levels will get a few more monsters.
+ Note that it is here we remove genocided monsters from the present level.
+ */
+void
+newcavelevel(int x)
+{
+ int i, j;
+ if (beenhere[level])
+ savelevel(); /* put the level back into storage */
+ level = x; /* get the new level and put in working
+ * storage */
+ if (beenhere[x]) {
+ getlevel();
+ sethp(0);
+ checkgen();
+ return;
+ }
+
+ /* fill in new level */
+ for (i = 0; i < MAXY; i++)
+ for (j = 0; j < MAXX; j++)
+ know[j][i] = mitem[j][i] = 0;
+ makemaze(x);
+ makeobject(x);
+ beenhere[x] = 1;
+ sethp(1);
+ checkgen(); /* wipe out any genocided monsters */
+
+#if WIZID
+ if (wizard || x == 0)
+#else
+ if (x == 0)
+#endif
+ for (j = 0; j < MAXY; j++)
+ for (i = 0; i < MAXX; i++)
+ know[i][j] = 1;
+}
+
+/*
+ makemaze(level)
+ int level;
+
+ subroutine to make the caverns for a given level. only walls are made.
+ */
+static int mx, mxl, mxh, my, myl, myh, tmp2;
+
+static void
+makemaze(int k)
+{
+ int i, j, tmp;
+ int z;
+ if (k > 1 && (rnd(17) <= 4 || k == MAXLEVEL - 1 || k == MAXLEVEL + MAXVLEVEL - 1)) {
+ if (cannedlevel(k))
+ return; /* read maze from data file */
+ }
+ if (k == 0)
+ tmp = 0;
+ else
+ tmp = OWALL;
+ for (i = 0; i < MAXY; i++)
+ for (j = 0; j < MAXX; j++)
+ item[j][i] = tmp;
+ if (k == 0)
+ return;
+ eat(1, 1);
+ if (k == 1)
+ item[33][MAXY - 1] = 0; /* exit from dungeon */
+
+ /* now for open spaces -- not on level 10 */
+ if (k != MAXLEVEL - 1) {
+ tmp2 = rnd(3) + 3;
+ for (tmp = 0; tmp < tmp2; tmp++) {
+ my = rnd(11) + 2;
+ myl = my - rnd(2);
+ myh = my + rnd(2);
+ if (k < MAXLEVEL) {
+ mx = rnd(44) + 5;
+ mxl = mx - rnd(4);
+ mxh = mx + rnd(12) + 3;
+ z = 0;
+ } else {
+ mx = rnd(60) + 3;
+ mxl = mx - rnd(2);
+ mxh = mx + rnd(2);
+ z = makemonst(k);
+ }
+ for (i = mxl; i < mxh; i++)
+ for (j = myl; j < myh; j++) {
+ item[i][j] = 0;
+ if ((mitem[i][j] = z))
+ hitp[i][j] = monster[z].hitpoints;
+ }
+ }
+ }
+ if (k != MAXLEVEL - 1) {
+ my = rnd(MAXY - 2);
+ for (i = 1; i < MAXX - 1; i++)
+ item[i][my] = 0;
+ }
+ if (k > 1)
+ treasureroom(k);
+}
+
+/*
+ function to eat away a filled in maze
+ */
+void
+eat(int xx, int yy)
+{
+ int dir, try;
+ dir = rnd(4);
+ try = 2;
+ while (try) {
+ switch (dir) {
+ case 1:
+ if (xx <= 2)
+ break; /* west */
+ if ((item[xx - 1][yy] != OWALL) || (item[xx - 2][yy] != OWALL))
+ break;
+ item[xx - 1][yy] = item[xx - 2][yy] = 0;
+ eat(xx - 2, yy);
+ break;
+
+ case 2:
+ if (xx >= MAXX - 3)
+ break; /* east */
+ if ((item[xx + 1][yy] != OWALL) || (item[xx + 2][yy] != OWALL))
+ break;
+ item[xx + 1][yy] = item[xx + 2][yy] = 0;
+ eat(xx + 2, yy);
+ break;
+
+ case 3:
+ if (yy <= 2)
+ break; /* south */
+ if ((item[xx][yy - 1] != OWALL) || (item[xx][yy - 2] != OWALL))
+ break;
+ item[xx][yy - 1] = item[xx][yy - 2] = 0;
+ eat(xx, yy - 2);
+ break;
+
+ case 4:
+ if (yy >= MAXY - 3)
+ break; /* north */
+ if ((item[xx][yy + 1] != OWALL) || (item[xx][yy + 2] != OWALL))
+ break;
+ item[xx][yy + 1] = item[xx][yy + 2] = 0;
+ eat(xx, yy + 2);
+ break;
+ };
+ if (++dir > 4) {
+ dir = 1;
+ --try;
+ }
+ }
+}
+
+/*
+ * function to read in a maze from a data file
+ *
+ * Format of maze data file: 1st character = # of mazes in file (ascii digit)
+ * For each maze: 18 lines (1st 17 used) 67 characters per line
+ *
+ * Special characters in maze data file:
+ *
+ * # wall D door . random monster
+ * ~ eye of larn ! cure dianthroritis
+ * - random object
+ */
+static int
+cannedlevel(int k)
+{
+ char *row;
+ int i, j;
+ int it, arg, mit, marg;
+ if (lopen(larnlevels) < 0) {
+ write(1, "Can't open the maze data file\n", 30);
+ died(-282);
+ return (0);
+ }
+ i = lgetc();
+ if (i <= '0') {
+ died(-282);
+ return (0);
+ }
+ for (i = 18 * rund(i - '0'); i > 0; i--)
+ lgetl(); /* advance to desired maze */
+ for (i = 0; i < MAXY; i++) {
+ row = lgetl();
+ for (j = 0; j < MAXX; j++) {
+ it = mit = arg = marg = 0;
+ switch (*row++) {
+ case '#':
+ it = OWALL;
+ break;
+ case 'D':
+ it = OCLOSEDDOOR;
+ arg = rnd(30);
+ break;
+ case '~':
+ if (k != MAXLEVEL - 1)
+ break;
+ it = OLARNEYE;
+ mit = rund(8) + DEMONLORD;
+ marg = monster[mit].hitpoints;
+ break;
+ case '!':
+ if (k != MAXLEVEL + MAXVLEVEL - 1)
+ break;
+ it = OPOTION;
+ arg = 21;
+ mit = DEMONLORD + 7;
+ marg = monster[mit].hitpoints;
+ break;
+ case '.':
+ if (k < MAXLEVEL)
+ break;
+ mit = makemonst(k + 1);
+ marg = monster[mit].hitpoints;
+ break;
+ case '-':
+ it = newobject(k + 1, &arg);
+ break;
+ };
+ item[j][i] = it;
+ iarg[j][i] = arg;
+ mitem[j][i] = mit;
+ hitp[j][i] = marg;
+
+#if WIZID
+ know[j][i] = (wizard) ? 1 : 0;
+#else
+ know[j][i] = 0;
+#endif
+ }
+ }
+ lrclose();
+ return (1);
+}
+
+/*
+ function to make a treasure room on a level
+ level 10's treasure room has the eye in it and demon lords
+ level V3 has potion of cure dianthroritis and demon prince
+ */
+static void
+treasureroom(int lv)
+{
+ int tx, ty, xsize, ysize;
+
+ for (tx = 1 + rnd(10); tx < MAXX - 10; tx += 10)
+ if ((lv == MAXLEVEL - 1) || (lv == MAXLEVEL + MAXVLEVEL - 1) || rnd(13) == 2) {
+ xsize = rnd(6) + 3;
+ ysize = rnd(3) + 3;
+ ty = rnd(MAXY - 9) + 1; /* upper left corner of room */
+ if (lv == MAXLEVEL - 1 || lv == MAXLEVEL + MAXVLEVEL - 1)
+ troom(lv, xsize, ysize, tx = tx + rnd(MAXX - 24), ty, rnd(3) + 6);
+ else
+ troom(lv, xsize, ysize, tx, ty, rnd(9));
+ }
+}
+
+/*
+ * subroutine to create a treasure room of any size at a given location
+ * room is filled with objects and monsters
+ * the coordinate given is that of the upper left corner of the room
+ */
+static void
+troom(int lv, int xsize, int ysize, int tx, int ty, int glyph)
+{
+ int i, j;
+ int tp1, tp2;
+ for (j = ty - 1; j <= ty + ysize; j++)
+ for (i = tx - 1; i <= tx + xsize; i++) /* clear out space for
+ * room */
+ item[i][j] = 0;
+ for (j = ty; j < ty + ysize; j++)
+ for (i = tx; i < tx + xsize; i++) { /* now put in the walls */
+ item[i][j] = OWALL;
+ mitem[i][j] = 0;
+ }
+ for (j = ty + 1; j < ty + ysize - 1; j++)
+ for (i = tx + 1; i < tx + xsize - 1; i++) /* now clear out
+ * interior */
+ item[i][j] = 0;
+
+ switch (rnd(2)) { /* locate the door on the treasure room */
+ case 1:
+ item[i = tx + rund(xsize)][j = ty + (ysize - 1) * rund(2)] = OCLOSEDDOOR;
+ iarg[i][j] = glyph; /* on horizontal walls */
+ break;
+ case 2:
+ item[i = tx + (xsize - 1) * rund(2)][j = ty + rund(ysize)] = OCLOSEDDOOR;
+ iarg[i][j] = glyph; /* on vertical walls */
+ break;
+ };
+
+ tp1 = playerx;
+ tp2 = playery;
+ playery = ty + (ysize >> 1);
+ if (c[HARDGAME] < 2)
+ for (playerx = tx + 1; playerx <= tx + xsize - 2; playerx += 2)
+ for (i = 0, j = rnd(6); i <= j; i++) {
+ something(lv + 2);
+ createmonster(makemonst(lv + 1));
+ }
+ else
+ for (playerx = tx + 1; playerx <= tx + xsize - 2; playerx += 2)
+ for (i = 0, j = rnd(4); i <= j; i++) {
+ something(lv + 2);
+ createmonster(makemonst(lv + 3));
+ }
+
+ playerx = tp1;
+ playery = tp2;
+}
+
+
+/*
+ ***********
+ MAKE_OBJECT
+ ***********
+ subroutine to create the objects in the maze for the given level
+ */
+static void
+makeobject(int j)
+{
+ int i;
+ if (j == 0) {
+ fillroom(OENTRANCE, 0); /* entrance to dungeon */
+ fillroom(ODNDSTORE, 0); /* the DND STORE */
+ fillroom(OSCHOOL, 0); /* college of Larn */
+ fillroom(OBANK, 0); /* 1st national bank of larn */
+ fillroom(OVOLDOWN, 0); /* volcano shaft to temple */
+ fillroom(OHOME, 0); /* the players home & family */
+ fillroom(OTRADEPOST, 0); /* the trading post */
+ fillroom(OLRS, 0); /* the larn revenue service */
+ return;
+ }
+ if (j == MAXLEVEL)
+ fillroom(OVOLUP, 0); /* volcano shaft up from the temple */
+
+ /* make the fixed objects in the maze STAIRS */
+ if ((j > 0) && (j != MAXLEVEL - 1) && (j != MAXLEVEL + MAXVLEVEL - 1))
+ fillroom(OSTAIRSDOWN, 0);
+ if ((j > 1) && (j != MAXLEVEL))
+ fillroom(OSTAIRSUP, 0);
+
+ /* make the random objects in the maze */
+
+ fillmroom(rund(3), OBOOK, j);
+ fillmroom(rund(3), OALTAR, 0);
+ fillmroom(rund(3), OSTATUE, 0);
+ fillmroom(rund(3), OPIT, 0);
+ fillmroom(rund(3), OFOUNTAIN, 0);
+ fillmroom(rnd(3) - 2, OIVTELETRAP, 0);
+ fillmroom(rund(2), OTHRONE, 0);
+ fillmroom(rund(2), OMIRROR, 0);
+ fillmroom(rund(2), OTRAPARROWIV, 0);
+ fillmroom(rnd(3) - 2, OIVDARTRAP, 0);
+ fillmroom(rund(3), OCOOKIE, 0);
+ if (j == 1)
+ fillmroom(1, OCHEST, j);
+ else
+ fillmroom(rund(2), OCHEST, j);
+ if ((j != MAXLEVEL - 1) && (j != MAXLEVEL + MAXVLEVEL - 1))
+ fillmroom(rund(2), OIVTRAPDOOR, 0);
+ if (j <= 10) {
+ fillmroom((rund(2)), ODIAMOND, rnd(10 * j + 1) + 10);
+ fillmroom(rund(2), ORUBY, rnd(6 * j + 1) + 6);
+ fillmroom(rund(2), OEMERALD, rnd(4 * j + 1) + 4);
+ fillmroom(rund(2), OSAPPHIRE, rnd(3 * j + 1) + 2);
+ }
+ for (i = 0; i < rnd(4) + 3; i++)
+ fillroom(OPOTION, newpotion()); /* make a POTION */
+ for (i = 0; i < rnd(5) + 3; i++)
+ fillroom(OSCROLL, newscroll()); /* make a SCROLL */
+ for (i = 0; i < rnd(12) + 11; i++)
+ fillroom(OGOLDPILE, 12 * rnd(j + 1) + (j << 3) + 10); /* make GOLD */
+ if (j == 5)
+ fillroom(OBANK2, 0); /* branch office of the bank */
+ froom(2, ORING, 0); /* a ring mail */
+ froom(1, OSTUDLEATHER, 0); /* a studded leather */
+ froom(3, OSPLINT, 0); /* a splint mail */
+ froom(5, OSHIELD, rund(3)); /* a shield */
+ froom(2, OBATTLEAXE, rund(3)); /* a battle axe */
+ froom(5, OLONGSWORD, rund(3)); /* a long sword */
+ froom(5, OFLAIL, rund(3)); /* a flail */
+ froom(4, OREGENRING, rund(3)); /* ring of regeneration */
+ froom(1, OPROTRING, rund(3)); /* ring of protection */
+ froom(2, OSTRRING, 4); /* ring of strength + 4 */
+ froom(7, OSPEAR, rnd(5)); /* a spear */
+ froom(3, OORBOFDRAGON, 0); /* orb of dragon slaying */
+ froom(4, OSPIRITSCARAB, 0); /* scarab of negate spirit */
+ froom(4, OCUBEofUNDEAD, 0); /* cube of undead control */
+ froom(2, ORINGOFEXTRA, 0); /* ring of extra regen */
+ froom(3, ONOTHEFT, 0); /* device of antitheft */
+ froom(2, OSWORDofSLASHING, 0); /* sword of slashing */
+ if (c[BESSMANN] == 0) {
+ froom(4, OHAMMER, 0); /* Bessman's flailing hammer */
+ c[BESSMANN] = 1;
+ }
+ if (c[HARDGAME] < 3 || (rnd(4) == 3)) {
+ if (j > 3) {
+ froom(3, OSWORD, 3); /* sunsword + 3 */
+ froom(5, O2SWORD, rnd(4)); /* a two handed sword */
+ froom(3, OBELT, 4); /* belt of striking */
+ froom(3, OENERGYRING, 3); /* energy ring */
+ froom(4, OPLATE, 5); /* platemail + 5 */
+ }
+ }
+}
+
+/*
+ subroutine to fill in a number of objects of the same kind
+ */
+
+static void
+fillmroom(int n, int what_i, int arg)
+{
+ int i;
+ char what;
+
+ /* truncate to char width (just in case it matters) */
+ what = (char)what_i;
+ for (i = 0; i < n; i++)
+ fillroom(what, arg);
+}
+
+static void
+froom(int n, int theitem, int arg)
+{
+ if (rnd(151) < n)
+ fillroom(theitem, arg);
+}
+
+/*
+ subroutine to put an object into an empty room
+ * uses a random walk
+ */
+static void
+fillroom(int what_i, int arg)
+{
+ int x, y;
+ char what;
+
+ /* truncate to char width (just in case it matters) */
+ what = (char)what_i;
+
+#ifdef EXTRA
+ c[FILLROOM]++;
+#endif
+
+ x = rnd(MAXX - 2);
+ y = rnd(MAXY - 2);
+ while (item[x][y]) {
+
+#ifdef EXTRA
+ c[RANDOMWALK]++;/* count up these random walks */
+#endif
+
+ x += rnd(3) - 2;
+ y += rnd(3) - 2;
+ if (x > MAXX - 2)
+ x = 1;
+ if (x < 1)
+ x = MAXX - 2;
+ if (y > MAXY - 2)
+ y = 1;
+ if (y < 1)
+ y = MAXY - 2;
+ }
+ item[x][y] = what;
+ iarg[x][y] = arg;
+}
+
+/*
+ subroutine to put monsters into an empty room without walls or other
+ monsters
+ */
+int
+fillmonst(int what)
+{
+ int x, y, trys;
+ for (trys = 5; trys > 0; --trys) { /* max # of creation attempts */
+ x = rnd(MAXX - 2);
+ y = rnd(MAXY - 2);
+ if ((item[x][y] == 0) && (mitem[x][y] == 0) && ((playerx != x) || (playery != y))) {
+ mitem[x][y] = what;
+ know[x][y] = 0;
+ hitp[x][y] = monster[what].hitpoints;
+ return (0);
+ }
+ }
+ return (-1); /* creation failure */
+}
+
+/*
+ creates an entire set of monsters for a level
+ must be done when entering a new level
+ if sethp(1) then wipe out old monsters else leave them there
+ */
+static void
+sethp(int flg)
+{
+ int i, j;
+ if (flg)
+ for (i = 0; i < MAXY; i++)
+ for (j = 0; j < MAXX; j++)
+ stealth[j][i] = 0;
+ if (level == 0) {
+ c[TELEFLAG] = 0;
+ return;
+ } /* if teleported and found level 1 then know
+ * level we are on */
+ if (flg)
+ j = rnd(12) + 2 + (level >> 1);
+ else
+ j = (level >> 1) + 1;
+ for (i = 0; i < j; i++)
+ fillmonst(makemonst(level));
+ positionplayer();
+}
+
+/*
+ * Function to destroy all genocided monsters on the present level
+ */
+static void
+checkgen(void)
+{
+ int x, y;
+ for (y = 0; y < MAXY; y++)
+ for (x = 0; x < MAXX; x++)
+ if (monster[mitem[x][y]].genocided)
+ mitem[x][y] = 0; /* no more monster */
+}
diff --git a/larn/data.c b/larn/data.c
new file mode 100644
index 0000000..4f35994
--- /dev/null
+++ b/larn/data.c
@@ -0,0 +1,664 @@
+/* $NetBSD: data.c,v 1.13 2008/02/03 20:11:04 dholland Exp $ */
+
+/*-
+ * Copyright (c) 1988 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)data.c 5.3 (Berkeley) 5/13/91";
+#else
+__RCSID("$NetBSD: data.c,v 1.13 2008/02/03 20:11:04 dholland Exp $");
+#endif
+#endif /* not lint */
+
+/* data.c Larn is copyrighted 1986 by Noah Morgan. */
+/* #define NODEFS */
+#include "header.h"
+#include "extern.h"
+
+/*
+ class[c[LEVEL]-1] gives the correct name of the players experience level
+ */
+static char aa1[] = " mighty evil master";
+static char aa2[] = "apprentice demi-god";
+static char aa3[] = " minor demi-god ";
+static char aa4[] = " major demi-god ";
+static char aa5[] = " minor deity ";
+static char aa6[] = " major deity ";
+static char aa7[] = " novice guardian ";
+static char aa8[] = "apprentice guardian";
+static char aa9[] = " The Creator ";
+const char *class[] =
+{
+ " novice explorer ", "apprentice explorer", " practiced explorer", /* -3 */
+ " expert explorer ", " novice adventurer", " adventurer ", /* -6 */
+ "apprentice conjurer", " conjurer ", " master conjurer ", /* -9 */
+ " apprentice mage ", " mage ", " experienced mage ", /* -12 */
+ " master mage ", " apprentice warlord", " novice warlord ", /* -15 */
+ " expert warlord ", " master warlord ", " apprentice gorgon ", /* -18 */
+ " gorgon ", " practiced gorgon ", " master gorgon ", /* -21 */
+ " demi-gorgon ", " evil master ", " great evil master ", /* -24 */
+ aa1, aa1, aa1, /* -27 */
+ aa1, aa1, aa1, /* -30 */
+ aa1, aa1, aa1, /* -33 */
+ aa1, aa1, aa1, /* -36 */
+ aa1, aa1, aa1, /* -39 */
+ aa2, aa2, aa2, /* -42 */
+ aa2, aa2, aa2, /* -45 */
+ aa2, aa2, aa2, /* -48 */
+ aa3, aa3, aa3, /* -51 */
+ aa3, aa3, aa3, /* -54 */
+ aa3, aa3, aa3, /* -57 */
+ aa4, aa4, aa4, /* -60 */
+ aa4, aa4, aa4, /* -63 */
+ aa4, aa4, aa4, /* -66 */
+ aa5, aa5, aa5, /* -69 */
+ aa5, aa5, aa5, /* -72 */
+ aa5, aa5, aa5, /* -75 */
+ aa6, aa6, aa6, /* -78 */
+ aa6, aa6, aa6, /* -81 */
+ aa6, aa6, aa6, /* -84 */
+ aa7, aa7, aa7, /* -87 */
+ aa8, aa8, aa8, /* -90 */
+ aa8, aa8, aa8, /* -93 */
+ " earth guardian ", " air guardian ", " fire guardian ", /* -96 */
+ " water guardian ", " time guardian ", " ethereal guardian ", /* -99 */
+ aa9, aa9, aa9, /* -102 */
+};
+
+/*
+ table of experience needed to be a certain level of player
+ skill[c[LEVEL]] is the experience required to attain the next level
+ */
+#define MEG 1000000
+long skill[] = {
+ 0, 10, 20, 40, 80, 160, 320, 640, 1280, 2560, 5120, /* 1-11 */
+ 10240, 20480, 40960, 100000, 200000, 400000, 700000, 1 * MEG, /* 12-19 */
+ 2 * MEG, 3 * MEG, 4 * MEG, 5 * MEG, 6 * MEG, 8 * MEG, 10 * MEG, /* 20-26 */
+ 12 * MEG, 14 * MEG, 16 * MEG, 18 * MEG, 20 * MEG, 22 * MEG, 24 * MEG, 26 * MEG, 28 * MEG, /* 27-35 */
+ 30 * MEG, 32 * MEG, 34 * MEG, 36 * MEG, 38 * MEG, 40 * MEG, 42 * MEG, 44 * MEG, 46 * MEG, /* 36-44 */
+ 48 * MEG, 50 * MEG, 52 * MEG, 54 * MEG, 56 * MEG, 58 * MEG, 60 * MEG, 62 * MEG, 64 * MEG, /* 45-53 */
+ 66 * MEG, 68 * MEG, 70 * MEG, 72 * MEG, 74 * MEG, 76 * MEG, 78 * MEG, 80 * MEG, 82 * MEG, /* 54-62 */
+ 84 * MEG, 86 * MEG, 88 * MEG, 90 * MEG, 92 * MEG, 94 * MEG, 96 * MEG, 98 * MEG, 100 * MEG, /* 63-71 */
+ 105 * MEG, 110 * MEG, 115 * MEG, 120 * MEG, 125 * MEG, 130 * MEG, 135 * MEG, 140 * MEG, /* 72-79 */
+ 145 * MEG, 150 * MEG, 155 * MEG, 160 * MEG, 165 * MEG, 170 * MEG, 175 * MEG, 180 * MEG, /* 80-87 */
+ 185 * MEG, 190 * MEG, 195 * MEG, 200 * MEG, 210 * MEG, 220 * MEG, 230 * MEG, 240 * MEG, /* 88-95 */
+ 250 * MEG, 260 * MEG, 270 * MEG, 280 * MEG, 290 * MEG, 300 * MEG /* 96-101 */
+};
+#undef MEG
+
+u_char *lpbuf, *lpnt, *inbuffer, *lpend; /* input/output pointers
+ * to the buffers */
+struct cel *cell; /* pointer to the dungeon storage */
+short hitp[MAXX][MAXY]; /* monster hp on level */
+short iarg[MAXX][MAXY]; /* arg for the item array */
+u_char item[MAXX][MAXY]; /* objects in maze if any */
+u_char know[MAXX][MAXY]; /* 1 or 0 if here before */
+u_char mitem[MAXX][MAXY]; /* monster item array */
+u_char moved[MAXX][MAXY]; /* monster movement flags */
+u_char stealth[MAXX][MAXY]; /* 0=sleeping 1=awake monst */
+u_char iven[26]; /* inventory for player */
+short ivenarg[26]; /* inventory for player */
+char lastmonst[40]; /* this has the name of the current monster */
+u_char beenhere[MAXLEVEL + MAXVLEVEL] = {0}; /* 1 if have been on
+ * this level */
+char VERSION = VER; /* this is the present version # of the
+ * program */
+char SUBVERSION = SUBVER;
+u_char nosignal = 0; /* set to 1 to disable the signals from doing
+ * anything */
+u_char predostuff = 0; /* 2 means that the trap handling routines
+ * must do a showplayer() after a trap. 0
+ * means don't showplayer() 0 - we are in
+ * create player screen 1 - we are in welcome
+ * screen 2 - we are in the normal game */
+char loginname[20]; /* players login name */
+char logname[LOGNAMESIZE]; /* players name storage for scoring */
+u_char sex = 1; /* default is a man 0=woman */
+u_char boldon = 1; /* 1=bold objects 0=inverse objects */
+u_char ckpflag = 0; /* 1 if want checkpointing of game, 0
+ * otherwise */
+u_char cheat = 0; /* 1 if the player has fudged save file */
+short level = 0; /* cavelevel player is on = c[CAVELEVEL] */
+u_char wizard = 0; /* the wizard mode flag */
+short lastnum = 0; /* the number of the monster last hitting
+ * player */
+short hitflag = 0; /* flag for if player has been hit when
+ * running */
+short hit2flag = 0; /* flag for if player has been hit when
+ * running */
+short hit3flag = 0; /* flag for if player has been hit flush
+ * input */
+short playerx, playery; /* the room on the present level of
+ * the player */
+short lastpx, lastpy; /* 0 --- MAXX-1 or 0 --- MAXY-1 */
+short oldx, oldy;
+short lasthx = 0, lasthy = 0; /* location of monster last hit by
+ * player */
+short nobeep = 0; /* true if program is not to beep */
+unsigned long randx = 33601; /* the random number seed */
+time_t initialtime = 0;/* time playing began */
+long gltime = 0; /* the clock for the game */
+long outstanding_taxes = 0; /* present tax bill from score file */
+long c[100], cbak[100]; /* the character description arrays */
+int enable_scroll = 0; /* constant for enabled/disabled
+ * scrolling regn */
+char aborted[] = " aborted";
+struct sphere *spheres = 0; /* pointer to linked list for spheres of
+ * annihilation */
+const char *levelname[] =
+{" H", " 1", " 2", " 3", " 4", " 5", " 6", " 7", " 8", " 9", "10", "V1", "V2", "V3"};
+
+char objnamelist[] = " ATOP%^F&^+M=%^$$f*OD#~][[)))(((||||||||{?!BC}o:@.<<<<EVV))([[]]](^ [H*** ^^ S tsTLc............................................";
+char monstnamelist[] = " BGHJKOScjtAELNQRZabhiCTYdegmvzFWflorXV pqsyUkMwDDPxnDDuD ...............................................................";
+const char *objectname[] =
+{0, "a holy altar", "a handsome jewel encrusted throne", "the orb", "a pit",
+ "a staircase leading upwards", "an elevator going up", "a bubbling fountain",
+ "a great marble statue", "a teleport trap", "the college of Larn",
+ "a mirror", "the DND store", "a staircase going down", "an elevator going down",
+ "the bank of Larn", "the 5th branch of the Bank of Larn",
+ "a dead fountain", "gold", "an open door", "a closed door",
+ "a wall", "The Eye of Larn", "plate mail", "chain mail", "leather armor",
+ "a sword of slashing", "Bessman's flailing hammer", "a sunsword",
+ "a two handed sword", "a spear", "a dagger",
+ "ring of extra regeneration", "a ring of regeneration", "a ring of protection",
+ "an energy ring", "a ring of dexterity", "a ring of strength",
+ "a ring of cleverness", "a ring of increase damage", "a belt of striking",
+ "a magic scroll", "a magic potion", "a book", "a chest",
+ "an amulet of invisibility", "an orb of dragon slaying",
+ "a scarab of negate spirit", "a cube of undead control",
+ "device of theft prevention", "a brilliant diamond", "a ruby",
+ "an enchanting emerald", "a sparkling sapphire", "the dungeon entrance",
+ "a volcanic shaft leaning downward", "the base of a volcanic shaft",
+ "a battle axe", "a longsword", "a flail", "ring mail", "studded leather armor",
+ "splint mail", "plate armor", "stainless plate armor", "a lance of death",
+ "an arrow trap", "an arrow trap", "a shield", "your home",
+ "gold", "gold", "gold", "a dart trap",
+ "a dart trap", "a trapdoor", "a trapdoor", "the local trading post",
+ "a teleport trap", "a massive throne",
+ "a sphere of annihilation", "a handsome jewel encrusted throne",
+ "the Larn Revenue Service", "a fortune cookie", "", "", "", "", "", "",
+ "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""
+};
+
+
+
+/*
+ * for the monster data
+ *
+ * array to do rnd() to create monsters <= a given level
+ */
+u_char monstlevel[] = {5, 11, 17, 22, 27, 33, 39, 42, 46, 50, 53, 56, 59};
+
+struct monst monster[] = {
+ /*
+ * NAME LV AC DAM ATT DEF GEN
+ * INT GOLD HP EXP
+ * -----------------------------------------------------------------
+ */
+ {"", 0, 0, 0, 0, 0, 0, 3, 0, 0, 0},
+ {"bat", 1, 0, 1, 0, 0, 0, 3, 0, 1, 1},
+ {"gnome", 1, 10, 1, 0, 0, 0, 8, 30, 2, 2},
+ {"hobgoblin", 1, 14, 2, 0, 0, 0, 5, 25, 3, 2},
+ {"jackal", 1, 17, 1, 0, 0, 0, 4, 0, 1, 1},
+ {"kobold", 1, 20, 1, 0, 0, 0, 7, 10, 1, 1},
+
+ {"orc", 2, 12, 1, 0, 0, 0, 9, 40, 4, 2},
+ {"snake", 2, 15, 1, 0, 0, 0, 3, 0, 3, 1},
+ {"giant centipede", 2, 14, 0, 4, 0, 0, 3, 0, 1, 2},
+ {"jaculi", 2, 20, 1, 0, 0, 0, 3, 0, 2, 1},
+ {"troglodyte", 2, 10, 2, 0, 0, 0, 5, 80, 4, 3},
+ {"giant ant", 2, 8, 1, 4, 0, 0, 4, 0, 5, 5},
+
+ /*
+ * NAME LV AC DAM ATT DEF GEN
+ * INT GOLD HP EXP
+ * -----------------------------------------------------------------
+ */
+
+ {"floating eye", 3, 8, 1, 0, 0, 0, 3, 0, 5, 2},
+ {"leprechaun", 3, 3, 0, 8, 0, 0, 3, 1500, 13, 45},
+ {"nymph", 3, 3, 0, 14, 0, 0, 9, 0, 18, 45},
+ {"quasit", 3, 5, 3, 0, 0, 0, 3, 0, 10, 15},
+ {"rust monster", 3, 4, 0, 1, 0, 0, 3, 0, 18, 25},
+ {"zombie", 3, 12, 2, 0, 0, 0, 3, 0, 6, 7},
+
+ {"assassin bug", 4, 9, 3, 0, 0, 0, 3, 0, 20, 15},
+ {"bugbear", 4, 5, 4, 15, 0, 0, 5, 40, 20, 35},
+ {"hell hound", 4, 5, 2, 2, 0, 0, 6, 0, 16, 35},
+ {"ice lizard", 4, 11, 2, 10, 0, 0, 6, 50, 16, 25},
+ {"centaur", 4, 6, 4, 0, 0, 0, 10, 40, 24, 45},
+
+ /*
+ * NAME LV AC DAM ATT DEF GEN
+ * INT GOLD HP EXP
+ * -----------------------------------------------------------------
+ */
+
+ {"troll", 5, 4, 5, 0, 0, 0, 9, 80, 50, 300},
+ {"yeti", 5, 6, 4, 0, 0, 0, 5, 50, 35, 100},
+ {"white dragon", 5, 2, 4, 5, 0, 0, 16, 500, 55, 1000},
+ {"elf", 5, 8, 1, 0, 0, 0, 15, 50, 22, 35},
+ {"gelatinous cube", 5, 9, 1, 0, 0, 0, 3, 0, 22, 45},
+
+ {"metamorph", 6, 7, 3, 0, 0, 0, 3, 0, 30, 40},
+ {"vortex", 6, 4, 3, 0, 0, 0, 3, 0, 30, 55},
+ {"ziller", 6, 15, 3, 0, 0, 0, 3, 0, 30, 35},
+ {"violet fungi", 6, 12, 3, 0, 0, 0, 3, 0, 38, 100},
+ {"wraith", 6, 3, 1, 6, 0, 0, 3, 0, 30, 325},
+ {"forvalaka", 6, 2, 5, 0, 0, 0, 7, 0, 50, 280},
+
+ /*
+ * NAME LV AC DAM ATT DEF GEN
+ * INT GOLD HP EXP
+ * -----------------------------------------------------------------
+ */
+
+ {"lama nobe", 7, 7, 3, 0, 0, 0, 6, 0, 35, 80},
+ {"osequip", 7, 4, 3, 16, 0, 0, 4, 0, 35, 100},
+ {"rothe", 7, 15, 5, 0, 0, 0, 3, 100, 50, 250},
+ {"xorn", 7, 0, 6, 0, 0, 0, 13, 0, 60, 300},
+ {"vampire", 7, 3, 4, 6, 0, 0, 17, 0, 50, 1000},
+ {"invisible stalker", 7, 3, 6, 0, 0, 0, 5, 0, 50, 350},
+
+ {"poltergeist", 8, 1, 4, 0, 0, 0, 3, 0, 50, 450},
+ {"disenchantress", 8, 3, 0, 9, 0, 0, 3, 0, 50, 500},
+ {"shambling mound", 8, 2, 5, 0, 0, 0, 6, 0, 45, 400},
+ {"yellow mold", 8, 12, 4, 0, 0, 0, 3, 0, 35, 250},
+ {"umber hulk", 8, 3, 7, 11, 0, 0, 14, 0, 65, 600},
+
+ /*
+ * NAME LV AC DAM ATT DEF GEN
+ * INT GOLD HP EXP
+ * -----------------------------------------------------------------
+ */
+
+ {"gnome king", 9, -1, 10, 0, 0, 0, 18, 2000, 100, 3000},
+ {"mimic", 9, 5, 6, 0, 0, 0, 8, 0, 55, 99},
+ {"water lord", 9, -10, 15, 7, 0, 0, 20, 0, 150, 15000},
+ {"bronze dragon", 9, 2, 9, 3, 0, 0, 16, 300, 80, 4000},
+ {"green dragon", 9, 3, 8, 10, 0, 0, 15, 200, 70, 2500},
+ {"purple worm", 9, -1, 11, 0, 0, 0, 3, 100, 120, 15000},
+ {"xvart", 9, -2, 12, 0, 0, 0, 13, 0, 90, 1000},
+
+ {"spirit naga", 10, -20, 12, 12, 0, 0, 23, 0, 95, 20000},
+ {"silver dragon", 10, -1, 12, 3, 0, 0, 20, 700, 100, 10000},
+ {"platinum dragon", 10, -5, 15, 13, 0, 0, 22, 1000, 130, 24000},
+ {"green urchin", 10, -3, 12, 0, 0, 0, 3, 0, 85, 5000},
+ {"red dragon", 10, -2, 13, 3, 0, 0, 19, 800, 110, 14000},
+
+ {"type I demon lord", 12, -30, 18, 0, 0, 0, 20, 0, 140, 50000},
+ {"type II demon lord", 13, -30, 18, 0, 0, 0, 21, 0, 160, 75000},
+ {"type III demon lord", 14, -30, 18, 0, 0, 0, 22, 0, 180, 100000},
+ {"type IV demon lord", 15, -35, 20, 0, 0, 0, 23, 0, 200, 125000},
+ {"type V demon lord", 16, -40, 22, 0, 0, 0, 24, 0, 220, 150000},
+ {"type VI demon lord", 17, -45, 24, 0, 0, 0, 25, 0, 240, 175000},
+ {"type VII demon lord", 18, -70, 27, 6, 0, 0, 26, 0, 260, 200000},
+ {"demon prince", 25, -127, 30, 6, 0, 0, 28, 0, 345, 300000}
+
+ /*
+ * NAME LV AC DAM ATT DEF
+ * GEN INT GOLD HP EXP
+ * -------------------------------------------------------------------
+ * --
+ */
+};
+
+/* name array for scrolls */
+
+const char *scrollname[] = {"", "", "", "", "", "", "", "", "", "", "", "", "", "",
+"", "", "", "", "", "", "", "", "", "", "", "", "", ""};
+
+const char *scrollhide[] = {
+ " enchant armor",
+ " enchant weapon",
+ " enlightenment",
+ " blank paper",
+ " create monster",
+ " create artifact",
+ " aggravate monsters",
+ " time warp",
+ " teleportation",
+ " expanded awareness",
+ " haste monsters",
+ " monster healing",
+ " spirit protection",
+ " undead protection",
+ " stealth",
+ " magic mapping",
+ " hold monsters",
+ " gem perfection",
+ " spell extension",
+ " identify",
+ " remove curse",
+ " annihilation",
+ " pulverization",
+ " life protection",
+ " ",
+ " ",
+ " ",
+ " "
+};
+
+const char *potionname[] = {"", "", "", "", "", "", "", "", "", "", "", "", "", "",
+ "", "", "", "", "", "", "", "", "", "", "", "", "", "",
+"", "", "", "", "", "", ""};
+
+/* name array for magic potions */
+const char *potionhide[] = {
+ " sleep",
+ " healing",
+ " raise level",
+ " increase ability",
+ " wisdom",
+ " strength",
+ " raise charisma",
+ " dizziness",
+ " learning",
+ " gold detection",
+ " monster detection",
+ " forgetfulness",
+ " water",
+ " blindness",
+ " confusion",
+ " heroism",
+ " sturdiness",
+ " giant strength",
+ " fire resistance",
+ " treasure finding",
+ " instant healing",
+ " cure dianthroritis",
+ " poison",
+ " see invisible",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
+};
+
+
+
+/*
+ spell data
+ */
+u_char spelknow[SPNUM] = {0};
+u_char splev[] = {1, 4, 9, 14, 18, 22, 26, 29, 32, 35, 37, 37, 37, 37, 37};
+
+const char *spelcode[] = {
+ "pro", "mle", "dex", "sle", "chm", "ssp",
+ "web", "str", "enl", "hel", "cbl", "cre", "pha", "inv",
+ "bal", "cld", "ply", "can", "has", "ckl", "vpr",
+ "dry", "lit", "drl", "glo", "flo", "fgr",
+ "sca", "hld", "stp", "tel", "mfi", /* 31 */
+ "sph", "gen", "sum", "wtw", "alt", "per"
+};
+
+const char *spelname[] = {
+ "protection", "magic missile", "dexterity",
+ "sleep", "charm monster", "sonic spear",
+
+ "web", "strength", "enlightenment",
+ "healing", "cure blindness", "create monster",
+ "phantasmal forces", "invisibility",
+
+ "fireball", "cold", "polymorph",
+ "cancellation", "haste self", "cloud kill",
+ "vaporize rock",
+
+ "dehydration", "lightning", "drain life",
+ "invulnerability", "flood", "finger of death",
+
+ "scare monster", "hold monster", "time stop",
+ "teleport away", "magic fire",
+
+ "sphere of annihilation", "genocide", "summon demon",
+ "walk through walls", "alter reality", "permanence",
+ ""
+};
+
+const char *speldescript[] = {
+ /* 1 */
+ "generates a +2 protection field",
+ "creates and hurls a magic missile equivalent to a +1 magic arrow",
+ "adds +2 to the caster's dexterity",
+ "causes some monsters to go to sleep",
+ "some monsters may be awed at your magnificence",
+ "causes your hands to emit a screeching sound toward what they point",
+ /* 7 */
+ "causes strands of sticky thread to entangle an enemy",
+ "adds +2 to the caster's strength for a short term",
+ "the caster becomes aware of things in the vicinity",
+ "restores some hp to the caster",
+ "restores sight to one so unfortunate as to be blinded",
+ "creates a monster near the caster appropriate for the location",
+ "creates illusions, and if believed, monsters die",
+ "the caster becomes invisible",
+ /* 15 */
+ "makes a ball of fire that burns on what it hits",
+ "sends forth a cone of cold which freezes what it touches",
+ "you can find out what this does for yourself",
+ "negates the ability of a monster to use its special abilities",
+ "speeds up the caster's movements",
+ "creates a fog of poisonous gas which kills all that is within it",
+ "this changes rock to air",
+ /* 22 */
+ "dries up water in the immediate vicinity",
+ "your finger will emit a lightning bolt when this spell is cast",
+ "subtracts hit points from both you and a monster",
+ "this globe helps to protect the player from physical attack",
+ "this creates an avalanche of H2O to flood the immediate chamber",
+ "this is a holy spell and calls upon your god to back you up",
+ /* 28 */
+ "terrifies the monster so that hopefully it won't hit the magic user",
+ "the monster is frozen in its tracks if this is successful",
+ "all movement in the caverns ceases for a limited duration",
+ "moves a particular monster around in the dungeon (hopefully away from you)",
+ "this causes a curtain of fire to appear all around you",
+ /* 33 */
+ "anything caught in this sphere is instantly killed. Warning -- dangerous",
+ "eliminates a species of monster from the game -- use sparingly",
+ "summons a demon who hopefully helps you out",
+ "allows the player to walk through walls for a short period of time",
+ "god only knows what this will do",
+ "makes a character spell permanent, i. e. protection, strength, etc.",
+ ""
+};
+
+char spelweird[MAXMONST + 8][SPNUM] = {
+ /*
+ * p m d s c s w s e h c c p i b c p c h c v d l d g f f
+ * s h s t m s g s w a p
+ */
+ /*
+ * r l e l h s e t n e b r h n a l l a a k p r i r l l g
+ * c l t e f p e u t l e
+ */
+ /*
+ * o e x e m p b r l l l e a v l d y n s l r y t l o o r
+ * a d p l i h n m w t r
+ */
+
+
+ /* bat */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* gnome */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* hobgoblin */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* jackal */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* kobold */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+
+ /* orc */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* snake */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* giant centipede */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* jaculi */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* troglodyte */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+
+ /* giant ant */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* floating eye */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* leprechaun */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* nymph */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* quasit */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+
+ /* rust monster */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* zombie */ {0, 0, 0, 8, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 4, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* assassin bug */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* bugbear */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* hell hound */ {0, 6, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+
+ /* ice lizard */ {0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* centaur */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* troll */ {0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* yeti */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* white dragon */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 15, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+
+ /* elf */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* gelatinous cube */ {0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* metamorph */ {0, 13, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 4, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* vortex */ {0, 13, 0, 0, 0, 10, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 4, 0, 0, 0, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* ziller */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+
+ /* violet fungi */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* wraith */ {0, 0, 0, 8, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 4, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* forvalaka */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* lama nobe */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* osequip */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+
+ /* rothe */ {0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* xorn */ {0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* vampire */ {0, 0, 0, 8, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* invisible staker */ {0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* poltergeist */ {0, 13, 0, 8, 0, 4, 1, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 4, 0, 0, 0, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+
+ /* disenchantress */ {0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* shambling mound */ {0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* yellow mold */ {0, 0, 0, 8, 0, 0, 1, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* umber hulk */ {0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* gnome king */ {0, 7, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+
+ /* mimic */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* water lord */ {0, 13, 0, 8, 3, 4, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 4, 0, 0, 0, 0, 0, 16, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* bronze dragon */ {0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* green dragon */ {0, 7, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* purple worm */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+
+ /* xvart */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* spirit naga */ {0, 13, 0, 8, 3, 4, 1, 0, 0, 0, 0, 0, 0, 5, 0, 4, 9, 0, 0, 4, 0, 4, 0, 0, 0, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* silver dragon */ {0, 6, 0, 9, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* platinum dragon */ {0, 7, 0, 9, 0, 0, 11, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* green urchin */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* red dragon */ {0, 6, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+
+ /*
+ * p m d s c s w s e h c c p i b c p c h c v d l d g f f
+ * s h s t m s g s w a p
+ */
+ /*
+ * r l e l h s e t n e b r h n a l l a a k p r i r l l g
+ * c l t e f p e u t l e
+ */
+ /*
+ * o e x e m p b r l l l e a v l d y n s l r y t l o o r
+ * a d p l i h n m w t r
+ */
+
+ /* demon lord */ {0, 7, 0, 4, 3, 0, 1, 0, 0, 0, 0, 0, 14, 5, 0, 0, 4, 0, 0, 4, 0, 4, 0, 0, 0, 4, 4, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0},
+ /* demon lord */ {0, 7, 0, 4, 3, 0, 1, 0, 0, 0, 0, 0, 14, 5, 0, 0, 4, 0, 0, 4, 0, 4, 0, 0, 0, 4, 4, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0},
+ /* demon lord */ {0, 7, 0, 4, 3, 0, 1, 0, 0, 0, 0, 0, 14, 5, 0, 0, 4, 0, 0, 4, 0, 4, 0, 0, 0, 4, 4, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0},
+ /* demon lord */ {0, 7, 0, 4, 3, 0, 1, 0, 0, 0, 0, 0, 14, 5, 0, 0, 4, 0, 0, 4, 0, 4, 0, 0, 0, 4, 4, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0},
+ /* demon lord */ {0, 7, 0, 4, 3, 0, 1, 0, 0, 0, 0, 0, 14, 5, 0, 0, 4, 0, 0, 4, 0, 4, 0, 0, 0, 4, 4, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0},
+ /* demon lord */ {0, 7, 0, 4, 3, 0, 1, 0, 0, 0, 0, 0, 14, 5, 0, 0, 4, 0, 0, 4, 0, 4, 0, 0, 0, 4, 4, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0},
+ /* demon lord */ {0, 7, 0, 4, 3, 0, 1, 0, 0, 0, 0, 0, 14, 5, 0, 0, 4, 0, 0, 4, 0, 4, 0, 0, 0, 4, 4, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0},
+ /* demon prince */ {0, 7, 0, 4, 3, 9, 1, 0, 0, 0, 0, 0, 14, 5, 0, 0, 4, 0, 0, 4, 0, 4, 0, 0, 0, 4, 4, 4, 0, 0, 0, 4, 9, 0, 0, 0, 0, 0}
+
+};
+
+const char *spelmes[] = {"",
+ /* 1 */ "the web had no effect on the %s",
+ /* 2 */ "the %s changed shape to avoid the web",
+ /* 3 */ "the %s isn't afraid of you",
+ /* 4 */ "the %s isn't affected",
+ /* 5 */ "the %s can see you with his infravision",
+ /* 6 */ "the %s vaporizes your missile",
+ /* 7 */ "your missile bounces off the %s",
+ /* 8 */ "the %s doesn't sleep",
+ /* 9 */ "the %s resists",
+ /* 10 */ "the %s can't hear the noise",
+ /* 11 */ "the %s's tail cuts it free of the web",
+ /* 12 */ "the %s burns through the web",
+ /* 13 */ "your missiles pass right through the %s",
+ /* 14 */ "the %s sees through your illusions",
+ /* 15 */ "the %s loves the cold!",
+ /* 16 */ "the %s loves the water!"
+};
+
+/*
+ * function to create scroll numbers with appropriate probability of
+ * occurrence
+ *
+ * 0 - armor 1 - weapon 2 - enlightenment 3 - paper
+ * 4 - create monster 5 - create item 6 - aggravate 7 - time warp
+ * 8 - teleportation 9 - expanded awareness 10 - haste monst
+ * 11 - heal monster 12 - spirit protection 13 - undead protection
+ * 14 - stealth 15 - magic mapping 16 - hold monster
+ * 17 - gem perfection 18 - spell extension 19 - identify
+ * 20 - remove curse 21 - annihilation 22 - pulverization
+ * 23 - life protection
+ */
+u_char scprob[] = {0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3,
+ 3, 3, 3, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9,
+ 9, 9, 10, 10, 10, 10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 13, 14, 14,
+ 15, 15, 16, 16, 16, 17, 17, 18, 18, 19, 19, 19, 20, 20, 20, 20, 21, 22,
+22, 22, 23};
+
+/*
+ * function to return a potion number created with appropriate probability
+ * of occurrence
+ *
+ * 0 - sleep 1 - healing 2 - raise level
+ * 3 - increase ability 4 - gain wisdom 5 - gain strength
+ * 6 - charismatic character 7 - dizziness 8 - learning
+ * 9 - gold detection 10 - monster detection 11 - forgetfulness
+ * 12 - water 13 - blindness 14 - confusion
+ * 15 - heroism 16 - sturdiness 17 - giant strength
+ * 18 - fire resistance 19 - treasure finding 20 - instant healing
+ * 21 - cure dianthroritis 22 - poison 23 - see invisible
+ */
+u_char potprob[] = {0, 0, 1, 1, 1, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 9, 9, 9, 10, 10, 10, 11, 11, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 20, 22, 22, 23, 23};
+
+u_char nlpts[] = {0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 5, 6, 7};
+u_char nch[] = {0, 0, 0, 1, 1, 1, 2, 2, 3, 4};
+u_char nplt[] = {0, 0, 0, 0, 1, 1, 2, 2, 3, 4};
+u_char ndgg[] = {0, 0, 0, 1, 1, 1, 1, 2, 2, 3, 3, 4, 5};
+u_char nsw[] = {0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 3};
diff --git a/larn/datfiles/larn.help b/larn/datfiles/larn.help
new file mode 100644
index 0000000..0e5edc5
--- /dev/null
+++ b/larn/datfiles/larn.help
@@ -0,0 +1,140 @@
+5 Welcome to the game of Larn. At this moment, you face a great problem.
+Your daughter has contracted a strange disease, and none of your home remedies
+seem to have any effect. You sense that she is in mortal danger, and you must
+try to save her. Time ago you heard of a land of great danger and opportunity.
+Perhaps here is the solution you need.
+
+ It has been said that there once was a great magician who called himself
+Polinneaus. Many years ago, after having many miraculous successes, Polinneaus
+retired to the caverns of Larn, where he devoted most of his time to the
+creation of magic. Rumors have it that one day Polinneaus set out to dispel
+an attacking army in a forest some distance to the north. It is believed that
+here he met his demise.
+
+ The caverns of Larn, it is thought, must be magnificent in design,
+and contain much magic and treasure. One option you have is to undertake a
+journey into these caverns.
+
+
+ Good Luck! You're going to need it!
+
+
+
+
+ Help File for The Caverns of Larn
+
+h move to the left H run left . stay here
+j move down J run down Z teleport yourself
+k move up K run up c cast a spell
+l move to the right L run right r read a scroll
+y move northwest Y run northwest q quaff a potion
+u move northeast U run northeast W wear armor
+b move southwest B run southwest T take off armor
+n move southeast N run southeast w wield a weapon
+^ identify a trap g give present pack weight P give tax status
+d drop an item i inventory your pockets Q quit the game
+v print program version S save the game D list all items found
+? this help screen A create diagnostic file e eat something
+ (wizards only)
+larn ++ restore checkpointed game
+larn -s list the scoreboard
+larn -i list scores with inventories
+larn -n suppress welcome message when beginning a game
+larn -h print out all the command line options
+larn -<number> specify difficulty of the game (may be used with -n)
+larn -o<optsfile> specify the .larnopts file to be used
+larn -c create new scoreboards -- prompts for a password
+ Special Notes
+
+When dropping gold, if you type '*' as your amount, all your gold gets dropped.
+In general, typing in '*' means all of what your interested in. This is true
+when visiting the bank, or when contributing at altars.
+
+Larn may need a VT100 to operate. A check is made of the environment variable
+"TERM" and it must be equal to "vt100". This only applies if
+the game has been compiled with "VT100" defined in the Makefile. If compiled
+to use termcap, there are no terminal restrictions, save needing cm, ce, & cl
+termcap entries.
+
+When in the store, trading post, school, or home, an <escape> will get you out.
+
+larn -l print out the larn log file
+
+When casting a spell, if you need a list of spells you can cast, type 'D' as
+the first letter of your spell. The available list of spells will be shown,
+after which you may enter the spell code. This only works on the 1st letter
+of the spell you are casting.
+
+The Author of Larn is Noah Morgan (1982-3), Copying for Profit is Prohibited
+Copyright 1986 by Noah Morgan, All Rights Reserved.
+ Background Information for Larn
+
+ Welcome to the game of Larn. At this moment, you face a great problem.
+Your daughter has contracted a strange disease, and none of your home remedies
+seem to have any effect. You sense that she is in mortal danger, and you must
+try to save her. Time ago you heard of a land of great danger and opportunity.
+Perhaps here is the solution you need.
+
+ It has been said that there once was a great magician who called himself
+Polinneaus. Many years ago, after having many miraculous successes, Polinneaus
+retired to the caverns of Larn, where he devoted most of his time to the
+creation of magic. Rumors have it that one day Polinneaus set out to dispel
+an attacking army in a forest some distance to the north. It is believed that
+here he met his demise.
+
+ The caverns of Larn, it is thought, must be magnificent in design,
+and contain much magic and treasure. One option you have is to undertake a
+journey into these caverns.
+
+ Good Luck! You're going to need it!
+
+
+
+ How to use the .larnopts option file
+
+The file ".larnopts", if used, should be in your home directory (see -o).
+A sequence of words terminated by whitespace is used to specify options.
+
+ Word Meaning
+
+ bold-objects select bold display of objects
+ inverse-objects select inverse video display of objects
+ no-introduction do not display intro message
+ enable-checkpointing turn on periodic checkpointing
+ no-beep disable beeping of the terminal
+ male choose your sex to be a man
+ female choose your sex to be a woman
+ name: "your name" choose your playing name
+ monster: "monst name" choose a name for a monster
+ savefile: "save-file-name" define what the savegame filename will be
+
+Your name and monster names must be enclosed in double quotation marks and may
+be up to 34 characters long. Longer names are truncated. Anything enclosed in
+quotation marks is considered one word, and must be separated from other words
+by whitespace.
+
+ Explanation of the Larn scoreboard facility
+
+ Larn supports TWO scoreboards, one for winners, and one for deceased
+characters. Each player (by userid or playerid, see UIDSCORE in Makefile)
+is allowed one slot on each scoreboard, if the score is in the top ten for
+that scoreboard. This design helps insure that frequent players of Larn
+do not hog the scoreboard, and gives more players a chance for glory. Level
+of difficulty is also noted on the scoreboards, and this takes precedence
+over score for determining what entry is on the scoreboard. For example:
+if "Yar, the Bug Slayer" has a score of 128003 on the scoreboard at diff 0,
+then his game at diff 1 and a score of 4112 would replace his previous
+entry on the scoreboard. Note that when a player dies, his inventory is
+stored in the scoreboard so that everyone can see what items the player had
+at the time of his death.
+
+
+
+
+
+
+
+
+
+
+
diff --git a/larn/datfiles/larnmaze b/larn/datfiles/larnmaze
new file mode 100644
index 0000000..37a89c3
--- /dev/null
+++ b/larn/datfiles/larnmaze
@@ -0,0 +1,288 @@
+@###################################################################
+# # . # # # # # . #
+# D D . . D . #
+###D########################################## # # ###D###
+# -# #. # # ################ . .#
+# ####### ######## ############ D #### # # #
+# ... #.# # # # . # # # #### # ############ # ###D###
+# #.# # # ## # # # ############ #### # #- # # # #. #
+# . # # # # ## #- # # # - D #### # # . D # #.# # ... #
+# # #.# # # # # # . . # # # # # # #-# # ~.! #
+###D### ### #######D## # ############ ###### ########## ### #######
+# # @ .# # ..... ...#
+###D###########################################################D###
+# . #.....# # # # -# # # # # #
+# ..... . D D D D. #
+# #.....# # # # # # .# # # #
+###################################################################
+
+###################################################################
+#.. . D # . # #- #
+############# ######### # ## ### ##### ## #### ###### ####### ### #
+#.#!#~# # # .-# # #- # # # # -# # # #
+# # #.# . # ####### # # # # # # # # # #####
+# # ..# ##### # # # #### # ## ## ## # ###### ####### # #
+# - ..D # D # . D # # # #.##### ## ## # #. #.# #..# # ### #
+####### ####### ### # # # # # # D # D D #..D # #
+#- # # # #### # ###### # ## # #. # #..# #####
+### #######################- # # # ###################### # #
+# ... # . #..# ### # - .. . #. ### #
+# # # # ### #################### # # #
+# ### #
+################################################################# #
+#- D ### # # # #
+# . # # # D #
+###################################################################
+
+###################################################################
+# .. #
+# ############## ############################################## # #
+# # # # # .. # # #
+# #D## # # ############D################# ########### # # #
+######### #- # # # #- D # # ~ # # # # # # #
+# # # # # # # ### # # # D - # # ####### # # # #
+# .... # #### # # # #!# # # ###### .. #.# # # # # # #
+# .... # # # # ### # # # # #########D#### # ### # # # # #
+# .... ######## # # D # #- # #.. # ...#.# # #.# # # # # #
+# # # ### # #### #.- D - #.# #.#.# # # # # #
+#####DD## ######## # # D D. # # # #...# # # # # #
+# # ..# # # # ############################## # ##### # # # # #
+# ......# # # # # # # # # #
+# ####### ###### ################################ ######### # ### #
+# .D. # #
+###################################################################
+
+###################################################################
+# ## ## ### ## #
+# ##### ## ..- ## ##.## ## #
+# # ! ## ## . ## ## .## .. ## #
+# #....###### ## ## ## . ## ## #
+# # - # ## ##D# ## . ## ## #
+# #####D ~ ####### ###........ ## ... ## ## #
+# # # ## ## .... ## . ## ## #
+# #. ######## ## ## . - ####D#### - #
+# #.- #...## ## ... ## ### ...... ## ## #
+# #. #..## ## ######### ## ... ## ### ## .. #
+# #.. #.## ## ## - ## . #### ## ## ##### ## #
+# #####D## ## ## ###### ## ## ####### ## #
+# D -.## #### ######## ##DD## ######## #D #
+# ###### ... ## ########## ## #
+#### . . ###
+###################################################################
+
+###################################################################
+# #
+# ####.########################################## ## ##########
+# # #.#.#.# #.. #. # # #
+####### # # # # # ##### #! # ########### # ### #
+# # # # # # # ##...## # # # # # #-# #
+# ..- D # # # # # ## . ## ####D##### .. ### # # # #
+# # # # ##. ~ .## # # ### # # ##### # #
+############ # # # # ## . . .## #...# .. #.# # # # #
+# .. .# # # # # ## - ## # # D.D # ######### #
+# . D # # # # ##.......## ####D#####.# # #
+# - . # # # # # ## ## # . # ### ########### #
+############ #.#.# # ###D### # . #....# # #
+# # #-# D .. # .# ###....## ##-### #
+#### ########################################## ###### ### #
+# . #
+###################################################################
+
+###################################################################
+# #
+# ###########################D####### ## #
+# ####################### # ##...... ## ## #
+# ########D###### # # !##.... ## ## #
+# ############ ## ## ... # # #...## ~ ## ###### #
+# # # # # ...... # # # .. ##### ## #
+# # - .. # # # ########### # # . ######### ## #
+# ##### ##### # # # # #... ## ## #
+# # # ########## ######## ########DD#### ####### #
+# # # .... # # # # # ## ## #
+# ##### # # ....# # ######### # # ##### ## #
+# #- ######D##########..# # ######### # - #.. ## ## #
+# ##### # # # #... #.....## #
+# # ################### ###############... #### # ######
+# # #
+###################################################################
+
+###################################################################
+# #
+########### ##### ##### #####D#### ##### ###### #
+#.. # #-..D ###### ## # ## # ## ..## ### #### #
+# #### ### ##### # ## # ##### # ## ### ## # # #
+# # # # # # ##### # ## # # #### # #..##### - ### #
+# # # ####### ### #...# # ######## # # #~. .... # #
+# # # # #...# # # ######### ############## #
+# # ###### ####### # ### ### # .. # #
+# # # - # #-#####!# # # #### ## #### # #
+# ###### # ########## #.. ....# # ######## # # # ## ## #
+# -.# ##### ####...## # . .# #..# # ## # #
+# #### ###### #.### #####.#### #### ### ## # #
+# #- #######....#### ...... #.# . # ## # #
+# ####### ##### ###########.############### ### #
+# . #
+###################################################################
+
+###################################################################
+# # #- . # # # # # # # # #
+# # # #####.##### # # # # # # # # # # ###########D########### #
+# # ##### # # ### # # # # # # # # # ### #.... # # # #
+# # # # # # # # # # # # # # #.!. -.. #.##.# # #
+# # . - # # ####### # # # # # # # # # ########## ## # #
+#.####### # # D # . # # #
+#. -# ################################################### #
+# ##### D. . #
+# # ### ###D### ### ### ###D### ### ### ###D### ###.### #
+# # ###### ### .# .# #. # # # # # # # # . # #
+# ### # -# ### .#. - .# # # #...- # # .. # # -..# # . # #
+# # # ### #. ~ .# # # # # # # # .. .# # # #
+#.###### # ### # # #.....# # # # # # # # # #
+# # ####### ####### ####### ####### ####### ####### ####### #
+# # ... .. #
+###################################################################
+
+###################################################################
+# # . # ### # # # # . #
+# D D . # . D . #
+############################################### # # ###D###
+# -# #. # # ################ . .#
+# #######D######## ############ # #### # # #
+# ... #. # #!~ . # # # #--# # ############ #####D###
+# #.# # # ## # # # ############ #--# # #- # # # #. #
+# . # #-# # ## #- # # # - D ## # # # . D #### #.# # ... #
+# # #-# # # # # # . . # # # # # # #-# # -.- #
+###D### ### ####### ## # ############ ###### ########## ### ###D###
+# # @ . ..... ...#
+##################################### ###############D###
+# . #.....# # # # -# # # # #
+# ..... . D D D D. #
+# #.....# # # # # .# # # #
+###################################################################
+
+###################################################################
+#.. . # D . #- #
+### ######### ######### # ## ### ##### ## ########### ####### ### #
+#.# # # # .-# # #- # # # # -# # # #
+# # #.# . # ####### # # # # # # # # # #####
+# # ..####### # # # #### # ## ## ##!# ###### ####### # #
+# - ..D # D # . D # # # #.##### ## ## # #. #.# #..# # ### #
+####### ####### ### # # # # # # D # D D #..D # #
+#- # # # #### # ###### # ## # #. # #..# #####
+### #######################- # # # ###################### # #
+# ... # . #..# ### # - .. . #. ### #
+# # # # #-# #################### # # #
+# # -# #
+################################-################################ #
+#-..... # ####D ### # # # #
+#~..... # # # # D #
+###################################################################
+
+###################################################################
+# .. #
+# ############## ############################################## # #
+# # # # # .. # # #
+# #D## # # ############D################# ########### # # #
+######### #- # # # #- D # #.!..# # # # # # #
+# # # # # # # # ### ## # #....D - # # ####### # # # #
+# .... # #### # # # #~# # # ###### .. #.# # # # # # #
+###....## # # # ###.# # # #########D## # # ### # # # # #
+# .... ######## # # D .# #- # .. # ...#.# # #.# # # # # #
+# # ## # ### .# #### .- D - #.# #.#.# # # # # #
+#####DD## ######## #... .# . # # # #.#. # # # # #
+# # ..# # # ############################## # # ##### # # # #
+# ......# # # # # # # # # # #
+# ####### ###### ################################D# ######### ### #
+# .D. #-.-#
+###################################################################
+
+###################################################################
+# ## ## ## ## ## #
+# ############## ## ..- ## ## . ## ## #
+# # # ## ## . ## ## . ##.. ## #
+# #....###### ## ## ######## ####### . ## ## #
+# # - # ## ## ##D# ## . ## ## #
+# # D###### ###........ ## ... ## ###### #
+# # # ## ## .... ## . ## ## - # #
+# #. ######## ## ## . - ####D####.. D #
+# #.- #...## ## ... ## ### ...... ## ##### #
+# #. #..## ## ######### ## ... ## ### ## .. #
+# #.. #.## ## ## - ## . #### ## ## ##### ## #
+# #####D####### # ## ## ##..## ## ## ## ## ## #
+# D -.## # # #### ##.-.-## ##DD## ### #### ## #
+# ######. ...# #### ## #### ##### ## ##D#
+####~!.... D . . #
+###################################################################
+
+###################################################################
+# #
+# ####.########################################## ## ##########
+# # #.#.#.# #.. . # # #
+####### # # # # # ############# # ########### # ### #
+# # # # # # # --##...##-- # # # # #-# #
+# ..- D # # # # # #-## . ##-# ####D##### .. ### # # # #
+# # # # ###. .### # ~ # ### ### ##### # #
+############ # # # # ## . . .## #...# .. #.# # #
+# .. .# # # # # ## - ## # # D. # ######### #
+# . D # # # # ###.......### ####D#####.# # #
+# - . # # # # # -## ##- # . ### ########### #
+############ #.#.# # ######D###### # . ....# # #
+# #!#-# # .. # . ###....## ##-### #
+#### ############################## ########## ###### ### #
+# D . #
+###################################################################
+
+###################################################################
+# #
+# ###############D####################D# #
+# ####################### # ##...... # #
+# ########D###### # # ##.... # #
+# ############### ## ## ... # # #...## ##### #
+# # ~ # # #!...... # # # .. ##### # #
+# # - .. # # # ########### # # . ######### # #
+# ##### ######## # # # # # #... ## # #
+# # # # ########## ######## ########D #### ###### #
+# # # ....# # # # # # ## # #
+# ##### # ###### # ....# # ######### # # #####D# #
+# #- ## # #######..# # ######### # - #.. #
+# ##### # # # #... #..... #
+# # ################### ###############... #### ######
+# # #
+###################################################################
+
+###################################################################
+# # #
+########### ##### # #####D#### ##### ###### #
+#.. # #~..D ###### ## # ## # ## ..## ### #### #
+# #### ### ##### # ## # ##### # ## ### ## # # #
+# # # # # # ##### # ## # # #### # #..##### - ### #
+# # # ####### ### #...# # ######## # # #!. .... # #
+# # # # #...# # # ######### ############## #
+# # ###### ####### # ### ### # .. # #
+# # # - # #- # # # ######## #### # #
+# # # ########## #.. ....# # ######## # ## ## #
+# # -. ##### ####...## # . .# .. # # #
+# # ###### ##### #####.#### ####### # #
+# # - #######....#### ...... #.# . # ## # #
+# ######### ##### ###########.############### ### #
+# . #
+###################################################################
+
+###################################################################
+# D D #-..........# # # # # #
+#D#D# #####.#####.# # # # # # #############D########### #
+# # ##### #.#~###.# # # # # # ### .... # # # #
+#D# # #.......# # # # # # # . . -.. #. #.# # #
+# # . - # #.####### # # # # # ################## # #
+#D####### #. # D # . # # #
+#. D D -# ################################################### #
+# ###### . . #
+# # D ### ###D### ####### ###D### ####### ###D### ####### #
+# # ###### # # .# .# #. # # # # # # # # . # #
+# ### -# # # .#. - .# # # #...- # # .. # # -..# # . # #
+#DD# # # # #. .# # # # # # # # .. .# # # #
+#.###### # # # # #.....# # # # # # ! # # # #
+# # ####### ####### ### ### ####### ### ### ####### ### ### #
+# # ... .. #
+###################################################################
+
diff --git a/larn/datfiles/larnopts b/larn/datfiles/larnopts
new file mode 100644
index 0000000..17216ed
--- /dev/null
+++ b/larn/datfiles/larnopts
@@ -0,0 +1,12 @@
+process-name: "Winnie-the-Pooh"
+enable-checkpointing
+bold-objects
+male
+play-day-play
+no-introduction
+name: "King of the Realm"
+monster: "abominable snowman"
+monster: "tooth fairy"
+monster: "Yaccerous Lexicous"
+savefile: "/save/noah/games/Larn12.0.sav"
+
diff --git a/larn/diag.c b/larn/diag.c
new file mode 100644
index 0000000..a3482c1
--- /dev/null
+++ b/larn/diag.c
@@ -0,0 +1,411 @@
+/* $NetBSD: diag.c,v 1.13 2012/06/19 05:30:43 dholland Exp $ */
+
+/* diag.c Larn is copyrighted 1986 by Noah Morgan. */
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: diag.c,v 1.13 2012/06/19 05:30:43 dholland Exp $");
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/times.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "header.h"
+#include "extern.h"
+
+static void greedy(void);
+static void fsorry(void);
+static void fcheat(void);
+
+static struct tms cputime;
+
+/*
+ ***************************
+ DIAG -- dungeon diagnostics
+ ***************************
+
+ subroutine to print out data for debugging
+ */
+#ifdef EXTRA
+static int rndcount[16];
+void
+diag()
+{
+ int i, j;
+ int hit, dam;
+ cursors();
+ lwclose();
+ if (lcreat(diagfile) < 0) { /* open the diagnostic file */
+ lcreat((char *) 0);
+ lprcat("\ndiagnostic failure\n");
+ return (-1);
+ }
+ write(1, "\nDiagnosing . . .\n", 18);
+ lprcat("\n\nBeginning of DIAG diagnostics ----------\n");
+
+ /* for the character attributes */
+
+ lprintf("\n\nPlayer attributes:\n\nHit points: %2ld(%2ld)", (long) c[HP], (long) c[HPMAX]);
+ lprintf("\ngold: %ld Experience: %ld Character level: %ld Level in caverns: %ld",
+ (long) c[GOLD], (long) c[EXPERIENCE], (long) c[LEVEL], (long) level);
+ lprintf("\nTotal types of monsters: %ld", (long) MAXMONST + 8);
+
+ lprcat("\f\nHere's the dungeon:\n\n");
+
+ i = level;
+ for (j = 0; j < MAXLEVEL + MAXVLEVEL; j++) {
+ newcavelevel(j);
+ lprintf("\nMaze for level %s:\n", levelname[level]);
+ diagdrawscreen();
+ }
+ newcavelevel(i);
+
+ lprcat("\f\nNow for the monster data:\n\n");
+ lprcat(" Monster Name LEV AC DAM ATT DEF GOLD HP EXP \n");
+ lprcat("--------------------------------------------------------------------------\n");
+ for (i = 0; i <= MAXMONST + 8; i++) {
+ lprintf("%19s %2ld %3ld ", monster[i].name, (long) monster[i].level, (long) monster[i].armorclass);
+ lprintf(" %3ld %3ld %3ld ", (long) monster[i].damage, (long) monster[i].attack, (long) monster[i].defense);
+ lprintf("%6ld %3ld %6ld\n", (long) monster[i].gold, (long) monster[i].hitpoints, (long) monster[i].experience);
+ }
+
+ lprcat("\n\nHere's a Table for the to hit percentages\n");
+ lprcat("\n We will be assuming that players level = 2 * monster level");
+ lprcat("\n and that the players dexterity and strength are 16.");
+ lprcat("\n to hit: if (rnd(22) < (2[monst AC] + your level + dex + WC/8 -1)/2) then hit");
+ lprcat("\n damage = rund(8) + WC/2 + STR - c[HARDGAME] - 4");
+ lprcat("\n to hit: if rnd(22) < to hit then player hits\n");
+ lprcat("\n Each entry is as follows: to hit / damage / number hits to kill\n");
+ lprcat("\n monster WC = 4 WC = 20 WC = 40");
+ lprcat("\n---------------------------------------------------------------");
+ for (i = 0; i <= MAXMONST + 8; i++) {
+ hit = 2 * monster[i].armorclass + 2 * monster[i].level + 16;
+ dam = 16 - c[HARDGAME];
+ lprintf("\n%20s %2ld/%2ld/%2ld %2ld/%2ld/%2ld %2ld/%2ld/%2ld",
+ monster[i].name,
+ (long) (hit / 2), (long) max(0, dam + 2), (long) (monster[i].hitpoints / (dam + 2) + 1),
+ (long) ((hit + 2) / 2), (long) max(0, dam + 10), (long) (monster[i].hitpoints / (dam + 10) + 1),
+ (long) ((hit + 5) / 2), (long) max(0, dam + 20), (long) (monster[i].hitpoints / (dam + 20) + 1));
+ }
+
+ lprcat("\n\nHere's the list of available potions:\n\n");
+ for (i = 0; i < MAXPOTION; i++)
+ lprintf("%20s\n", &potionhide[i][1]);
+ lprcat("\n\nHere's the list of available scrolls:\n\n");
+ for (i = 0; i < MAXSCROLL; i++)
+ lprintf("%20s\n", &scrollhide[i][1]);
+ lprcat("\n\nHere's the spell list:\n\n");
+ lprcat("spell name description\n");
+ lprcat("-------------------------------------------------------------------------------------------\n\n");
+ for (j = 0; j < SPNUM; j++) {
+ lprc(' ');
+ lprcat(spelcode[j]);
+ lprintf(" %21s %s\n", spelname[j], speldescript[j]);
+ }
+
+ lprcat("\n\nFor the c[] array:\n");
+ for (j = 0; j < 100; j += 10) {
+ lprintf("\nc[%2ld] = ", (long) j);
+ for (i = 0; i < 9; i++)
+ lprintf("%5ld ", (long) c[i + j]);
+ }
+
+ lprcat("\n\nTest of random number generator ----------------");
+ lprcat("\n for 25,000 calls divided into 16 slots\n\n");
+
+ for (i = 0; i < 16; i++)
+ rndcount[i] = 0;
+ for (i = 0; i < 25000; i++)
+ rndcount[rund(16)]++;
+ for (i = 0; i < 16; i++) {
+ lprintf(" %5ld", (long) rndcount[i]);
+ if (i == 7)
+ lprc('\n');
+ }
+
+ lprcat("\n\n");
+ lwclose();
+ lcreat((char *) 0);
+ lprcat("Done Diagnosing . . .");
+ return (0);
+}
+/*
+ subroutine to count the number of occurrences of an object
+ */
+int
+dcount(l)
+ int l;
+{
+ int i, j, p;
+ int k;
+ k = 0;
+ for (i = 0; i < MAXX; i++)
+ for (j = 0; j < MAXY; j++)
+ for (p = 0; p < MAXLEVEL; p++)
+ if (cell[p * MAXX * MAXY + i * MAXY + j].item == l)
+ k++;
+ return (k);
+}
+
+/*
+ subroutine to draw the whole screen as the player knows it
+ */
+void
+diagdrawscreen()
+{
+ int i, j, k;
+
+ for (i = 0; i < MAXY; i++)
+ /* for the east west walls of this line */
+ {
+ for (j = 0; j < MAXX; j++)
+ if (k = mitem[j][i])
+ lprc(monstnamelist[k]);
+ else
+ lprc(objnamelist[item[j][i]]);
+ lprc('\n');
+ }
+}
+#endif
+
+
+/*
+ to save the game in a file
+ */
+static time_t zzz = 0;
+int
+savegame(char *fname)
+{
+ int i, k;
+ struct sphere *sp;
+ struct stat statbuf;
+
+ nosignal = 1;
+ lflush();
+ savelevel();
+ ointerest();
+ if (lcreat(fname) < 0) {
+ lcreat((char *) 0);
+ lprintf("\nCan't open file <%s> to save game\n", fname);
+ nosignal = 0;
+ return (-1);
+ }
+ set_score_output();
+ lwrite((char *) beenhere, MAXLEVEL + MAXVLEVEL);
+ for (k = 0; k < MAXLEVEL + MAXVLEVEL; k++)
+ if (beenhere[k])
+ lwrite((char *) &cell[k * MAXX * MAXY], sizeof(struct cel) * MAXY * MAXX);
+ times(&cputime); /* get cpu time */
+ c[CPUTIME] += (cputime.tms_utime + cputime.tms_stime) / 60;
+ lwrite((char *) &c[0], 100 * sizeof(long));
+ lprint((long) gltime);
+ lprc(level);
+ lprc(playerx);
+ lprc(playery);
+ lwrite((char *) iven, 26);
+ lwrite((char *) ivenarg, 26 * sizeof(short));
+ for (k = 0; k < MAXSCROLL; k++)
+ lprc(scrollname[k][0]);
+ for (k = 0; k < MAXPOTION; k++)
+ lprc(potionname[k][0]);
+ lwrite((char *) spelknow, SPNUM);
+ lprc(wizard);
+ lprc(rmst); /* random monster generation counter */
+ for (i = 0; i < 90; i++)
+ lprc(itm[i].qty);
+ lwrite((char *) course, 25);
+ lprc(cheat);
+ lprc(VERSION);
+ for (i = 0; i < MAXMONST; i++)
+ lprc(monster[i].genocided); /* genocide info */
+ for (sp = spheres; sp; sp = sp->p)
+ lwrite((char *) sp, sizeof(struct sphere)); /* save spheres of
+ * annihilation */
+ time(&zzz);
+ lprint((long) (zzz - initialtime));
+ lwrite((char *) &zzz, sizeof(long));
+ if (fstat(io_outfd, &statbuf) < 0)
+ lprint(0L);
+ else
+ lprint((long) statbuf.st_ino); /* inode # */
+ lwclose();
+ lastmonst[0] = 0;
+#ifndef VT100
+ setscroll();
+#endif /* VT100 */
+ lcreat((char *) 0);
+ nosignal = 0;
+ return (0);
+}
+
+void
+restoregame(char *fname)
+{
+ int i, k;
+ struct sphere *sp, *sp2;
+ struct stat filetimes;
+ cursors();
+ lprcat("\nRestoring . . .");
+ lflush();
+ if (lopen(fname) <= 0) {
+ lcreat((char *) 0);
+ lprintf("\nCan't open file <%s>to restore game\n", fname);
+ nap(2000);
+ c[GOLD] = c[BANKACCOUNT] = 0;
+ died(-265);
+ return;
+ }
+ lrfill((char *) beenhere, MAXLEVEL + MAXVLEVEL);
+ for (k = 0; k < MAXLEVEL + MAXVLEVEL; k++)
+ if (beenhere[k])
+ lrfill((char *) &cell[k * MAXX * MAXY], sizeof(struct cel) * MAXY * MAXX);
+
+ lrfill((char *) &c[0], 100 * sizeof(long));
+ gltime = larn_lrint();
+ level = c[CAVELEVEL] = lgetc();
+ playerx = lgetc();
+ playery = lgetc();
+ lrfill((char *) iven, 26);
+ lrfill((char *) ivenarg, 26 * sizeof(short));
+ for (k = 0; k < MAXSCROLL; k++)
+ scrollname[k] = lgetc() ? scrollhide[k] : "";
+ for (k = 0; k < MAXPOTION; k++)
+ potionname[k] = lgetc() ? potionhide[k] : "";
+ lrfill((char *) spelknow, SPNUM);
+ wizard = lgetc();
+ rmst = lgetc(); /* random monster creation flag */
+
+ for (i = 0; i < 90; i++)
+ itm[i].qty = lgetc();
+ lrfill((char *) course, 25);
+ cheat = lgetc();
+ if (VERSION != lgetc()) { /* version number */
+ cheat = 1;
+ lprcat("Sorry, But your save file is for an older version of larn\n");
+ nap(2000);
+ c[GOLD] = c[BANKACCOUNT] = 0;
+ died(-266);
+ return;
+ }
+ for (i = 0; i < MAXMONST; i++)
+ monster[i].genocided = lgetc(); /* genocide info */
+ for (sp = 0, i = 0; i < c[SPHCAST]; i++) {
+ sp2 = sp;
+ sp = (struct sphere *) malloc(sizeof(struct sphere));
+ if (sp == 0) {
+ write(2, "Can't malloc() for sphere space\n", 32);
+ break;
+ }
+ lrfill((char *) sp, sizeof(struct sphere)); /* get spheres of
+ * annihilation */
+ sp->p = 0; /* null out pointer */
+ if (i == 0)
+ spheres = sp; /* beginning of list */
+ else
+ sp2->p = sp;
+ }
+
+ time(&zzz);
+ initialtime = zzz - larn_lrint();
+ /* get the creation and modification time of file */
+ fstat(io_infd, &filetimes);
+ lrfill((char *) &zzz, sizeof(long));
+ zzz += 6;
+ if (filetimes.st_ctime > zzz)
+ fsorry(); /* file create time */
+ else if (filetimes.st_mtime > zzz)
+ fsorry(); /* file modify time */
+ if (c[HP] < 0) {
+ died(284);
+ return;
+ } /* died a post mortem death */
+ oldx = oldy = 0;
+ /* XXX the following will break on 64-bit inode numbers */
+ i = larn_lrint(); /* inode # */
+ if (i && (filetimes.st_ino != (ino_t) i))
+ fsorry();
+ lrclose();
+ if (strcmp(fname, ckpfile) == 0) {
+ if (lappend(fname) < 0)
+ fcheat();
+ else {
+ lprc(' ');
+ lwclose();
+ }
+ lcreat((char *) 0);
+ } else if (unlink(fname) < 0)
+ fcheat(); /* can't unlink save file */
+ /* for the greedy cheater checker */
+ for (k = 0; k < 6; k++)
+ if (c[k] > 99)
+ greedy();
+ if (c[HPMAX] > 999 || c[SPELLMAX] > 125)
+ greedy();
+ if (c[LEVEL] == 25 && c[EXPERIENCE] > skill[24]) { /* if patch up lev 25
+ * player */
+ long tmp;
+ tmp = c[EXPERIENCE] - skill[24]; /* amount to go up */
+ c[EXPERIENCE] = skill[24];
+ raiseexperience((long) tmp);
+ }
+ getlevel();
+ lasttime = gltime;
+}
+
+/*
+ subroutine to not allow greedy cheaters
+ */
+static void
+greedy(void)
+{
+#if WIZID
+ if (wizard)
+ return;
+#endif
+
+ lprcat("\n\nI am so sorry, but your character is a little TOO good! Since this\n");
+ lprcat("cannot normally happen from an honest game, I must assume that you cheated.\n");
+ lprcat("In that you are GREEDY as well as a CHEATER, I cannot allow this game\n");
+ lprcat("to continue.\n");
+ nap(5000);
+ c[GOLD] = c[BANKACCOUNT] = 0;
+ died(-267);
+ return;
+}
+
+/*
+ subroutine to not allow altered save files and terminate the attempted
+ restart
+ */
+static void
+fsorry(void)
+{
+ lprcat("\nSorry, but your savefile has been altered.\n");
+ lprcat("However, seeing as I am a good sport, I will let you play.\n");
+ lprcat("Be advised though, you won't be placed on the normal scoreboard.");
+ cheat = 1;
+ nap(4000);
+}
+
+/*
+ subroutine to not allow game if save file can't be deleted
+ */
+static void
+fcheat(void)
+{
+#if WIZID
+ if (wizard)
+ return;
+#endif
+
+ lprcat("\nSorry, but your savefile can't be deleted. This can only mean\n");
+ lprcat("that you tried to CHEAT by protecting the directory the savefile\n");
+ lprcat("is in. Since this is unfair to the rest of the larn community, I\n");
+ lprcat("cannot let you play this game.\n");
+ nap(5000);
+ c[GOLD] = c[BANKACCOUNT] = 0;
+ died(-268);
+ return;
+}
diff --git a/larn/display.c b/larn/display.c
new file mode 100644
index 0000000..3f4389b
--- /dev/null
+++ b/larn/display.c
@@ -0,0 +1,637 @@
+/* $NetBSD: display.c,v 1.10 2012/06/19 05:30:43 dholland Exp $ */
+
+/* display.c Larn is copyrighted 1986 by Noah Morgan. */
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: display.c,v 1.10 2012/06/19 05:30:43 dholland Exp $");
+#endif /* not lint */
+
+#include "header.h"
+#include "extern.h"
+
+#define makecode(_a,_b,_c) (((_a)<<16) + ((_b)<<8) + (_c))
+
+static void bot_hpx(void);
+static void bot_spellx(void);
+static void botside(void);
+static void botsub(int, const char *);
+static void seepage(void);
+
+static int minx, maxx, miny, maxy, k, m;
+static char bot1f = 0, bot2f = 0, bot3f = 0;
+static char always = 0;
+/*
+ bottomline()
+
+ now for the bottom line of the display
+ */
+void
+bottomline(void)
+{
+ recalc();
+ bot1f = 1;
+}
+
+void
+bottomhp(void)
+{
+ bot2f = 1;
+}
+
+void
+bottomspell(void)
+{
+ bot3f = 1;
+}
+
+void
+bottomdo(void)
+{
+ if (bot1f) {
+ bot3f = bot1f = bot2f = 0;
+ bot_linex();
+ return;
+ }
+ if (bot2f) {
+ bot2f = 0;
+ bot_hpx();
+ }
+ if (bot3f) {
+ bot3f = 0;
+ bot_spellx();
+ }
+}
+
+void
+bot_linex(void)
+{
+ int i;
+ if (cbak[SPELLS] <= -50 || (always)) {
+ cursor(1, 18);
+ if (c[SPELLMAX] > 99)
+ lprintf("Spells:%3ld(%3ld)", (long) c[SPELLS], (long) c[SPELLMAX]);
+ else
+ lprintf("Spells:%3ld(%2ld) ", (long) c[SPELLS], (long) c[SPELLMAX]);
+ lprintf(" AC: %-3ld WC: %-3ld Level", (long) c[AC], (long) c[WCLASS]);
+ if (c[LEVEL] > 99)
+ lprintf("%3ld", (long) c[LEVEL]);
+ else
+ lprintf(" %-2ld", (long) c[LEVEL]);
+ lprintf(" Exp: %-9ld %s\n", (long) c[EXPERIENCE], class[c[LEVEL] - 1]);
+ lprintf("HP: %3ld(%3ld) STR=%-2ld INT=%-2ld ",
+ (long) c[HP], (long) c[HPMAX], (long) (c[STRENGTH] + c[STREXTRA]), (long) c[INTELLIGENCE]);
+ lprintf("WIS=%-2ld CON=%-2ld DEX=%-2ld CHA=%-2ld LV:",
+ (long) c[WISDOM], (long) c[CONSTITUTION], (long) c[DEXTERITY], (long) c[CHARISMA]);
+
+ if ((level == 0) || (wizard))
+ c[TELEFLAG] = 0;
+ if (c[TELEFLAG])
+ lprcat(" ?");
+ else
+ lprcat(levelname[level]);
+ lprintf(" Gold: %-6ld", (long) c[GOLD]);
+ always = 1;
+ botside();
+ c[TMP] = c[STRENGTH] + c[STREXTRA];
+ for (i = 0; i < 100; i++)
+ cbak[i] = c[i];
+ return;
+ }
+ botsub(makecode(SPELLS, 8, 18), "%3ld");
+ if (c[SPELLMAX] > 99)
+ botsub(makecode(SPELLMAX, 12, 18), "%3ld)");
+ else
+ botsub(makecode(SPELLMAX, 12, 18), "%2ld) ");
+ botsub(makecode(HP, 5, 19), "%3ld");
+ botsub(makecode(HPMAX, 9, 19), "%3ld");
+ botsub(makecode(AC, 21, 18), "%-3ld");
+ botsub(makecode(WCLASS, 30, 18), "%-3ld");
+ botsub(makecode(EXPERIENCE, 49, 18), "%-9ld");
+ if (c[LEVEL] != cbak[LEVEL]) {
+ cursor(59, 18);
+ lprcat(class[c[LEVEL] - 1]);
+ }
+ if (c[LEVEL] > 99)
+ botsub(makecode(LEVEL, 40, 18), "%3ld");
+ else
+ botsub(makecode(LEVEL, 40, 18), " %-2ld");
+ c[TMP] = c[STRENGTH] + c[STREXTRA];
+ botsub(makecode(TMP, 18, 19), "%-2ld");
+ botsub(makecode(INTELLIGENCE, 25, 19), "%-2ld");
+ botsub(makecode(WISDOM, 32, 19), "%-2ld");
+ botsub(makecode(CONSTITUTION, 39, 19), "%-2ld");
+ botsub(makecode(DEXTERITY, 46, 19), "%-2ld");
+ botsub(makecode(CHARISMA, 53, 19), "%-2ld");
+ if ((level != cbak[CAVELEVEL]) || (c[TELEFLAG] != cbak[TELEFLAG])) {
+ if ((level == 0) || (wizard))
+ c[TELEFLAG] = 0;
+ cbak[TELEFLAG] = c[TELEFLAG];
+ cbak[CAVELEVEL] = level;
+ cursor(59, 19);
+ if (c[TELEFLAG])
+ lprcat(" ?");
+ else
+ lprcat(levelname[level]);
+ }
+ botsub(makecode(GOLD, 69, 19), "%-6ld");
+ botside();
+}
+
+/*
+ special subroutine to update only the gold number on the bottomlines
+ called from ogold()
+ */
+void
+bottomgold(void)
+{
+ botsub(makecode(GOLD, 69, 19), "%-6ld");
+ /* botsub(GOLD,"%-6ld",69,19); */
+}
+
+/*
+ special routine to update hp and level fields on bottom lines
+ called in monster.c hitplayer() and spattack()
+ */
+static void
+bot_hpx(void)
+{
+ if (c[EXPERIENCE] != cbak[EXPERIENCE]) {
+ recalc();
+ bot_linex();
+ } else
+ botsub(makecode(HP, 5, 19), "%3ld");
+}
+
+/*
+ special routine to update number of spells called from regen()
+ */
+static void
+bot_spellx(void)
+{
+ botsub(makecode(SPELLS, 9, 18), "%2ld");
+}
+
+/*
+ common subroutine for a more economical bottomline()
+ */
+static struct bot_side_def {
+ int typ;
+ const char *string;
+}
+ bot_data[] =
+{
+ { STEALTH, "stealth"},
+ { UNDEADPRO, "undead pro" },
+ { SPIRITPRO, "spirit pro" },
+ { CHARMCOUNT, "Charm"},
+ { TIMESTOP, "Time Stop" },
+ { HOLDMONST, "Hold Monst" },
+ { GIANTSTR, "Giant Str"},
+ { FIRERESISTANCE, "Fire Resit" },
+ { DEXCOUNT, "Dexterity" },
+ { STRCOUNT, "Strength"},
+ { SCAREMONST, "Scare" },
+ { HASTESELF, "Haste Self" },
+ { CANCELLATION, "Cancel"},
+ { INVISIBILITY, "Invisible" },
+ { ALTPRO, "Protect 3" },
+ { PROTECTIONTIME, "Protect 2"},
+ { WTW, "Wall-Walk" }
+};
+
+static void
+botside(void)
+{
+ int i, idx;
+ for (i = 0; i < 17; i++) {
+ idx = bot_data[i].typ;
+ if ((always) || (c[idx] != cbak[idx])) {
+ if ((always) || (cbak[idx] == 0)) {
+ if (c[idx]) {
+ cursor(70, i + 1);
+ lprcat(bot_data[i].string);
+ }
+ } else if (c[idx] == 0) {
+ cursor(70, i + 1);
+ lprcat(" ");
+ }
+ cbak[idx] = c[idx];
+ }
+ }
+ always = 0;
+}
+
+static void
+botsub(int idx, const char *str)
+{
+ int x, y;
+ y = idx & 0xff;
+ x = (idx >> 8) & 0xff;
+ idx >>= 16;
+ if (c[idx] != cbak[idx]) {
+ cbak[idx] = c[idx];
+ cursor(x, y);
+ lprintf(str, (long) c[idx]);
+ }
+}
+
+/*
+ * subroutine to draw only a section of the screen
+ * only the top section of the screen is updated.
+ * If entire lines are being drawn, then they will be cleared first.
+ */
+/* for limited screen drawing */
+static int d_xmin = 0, d_xmax = MAXX, d_ymin = 0, d_ymax = MAXY;
+
+void
+draws(int xmin, int xmax, int ymin, int ymax)
+{
+ int i, idx;
+ if (xmin == 0 && xmax == MAXX) { /* clear section of screen as
+ * needed */
+ if (ymin == 0)
+ cl_up(79, ymax);
+ else
+ for (i = ymin; i < ymin; i++)
+ cl_line(1, i + 1);
+ xmin = -1;
+ }
+ d_xmin = xmin;
+ d_xmax = xmax;
+ d_ymin = ymin;
+ d_ymax = ymax; /* for limited screen drawing */
+ drawscreen();
+ if (xmin <= 0 && xmax == MAXX) { /* draw stuff on right side
+ * of screen as needed */
+ for (i = ymin; i < ymax; i++) {
+ idx = bot_data[i].typ;
+ if (c[idx]) {
+ cursor(70, i + 1);
+ lprcat(bot_data[i].string);
+ }
+ cbak[idx] = c[idx];
+ }
+ }
+}
+
+/*
+ drawscreen()
+
+ subroutine to redraw the whole screen as the player knows it
+ */
+u_char screen[MAXX][MAXY]; /* template for the screen */
+static u_char d_flag;
+void
+drawscreen(void)
+{
+ int i, j, kk;
+ int lastx, lasty; /* variables used to optimize the
+ * object printing */
+ if (d_xmin == 0 && d_xmax == MAXX && d_ymin == 0 && d_ymax == MAXY) {
+ d_flag = 1;
+ clear(); /* clear the screen */
+ } else {
+ d_flag = 0;
+ cursor(1, 1);
+ }
+ if (d_xmin < 0)
+ d_xmin = 0; /* d_xmin=-1 means display all without
+ * bottomline */
+
+ for (i = d_ymin; i < d_ymax; i++)
+ for (j = d_xmin; j < d_xmax; j++)
+ if (know[j][i] == 0)
+ screen[j][i] = ' ';
+ else if ((kk = mitem[j][i]) != 0)
+ screen[j][i] = monstnamelist[kk];
+ else if ((kk = item[j][i]) == OWALL)
+ screen[j][i] = '#';
+ else
+ screen[j][i] = ' ';
+
+ for (i = d_ymin; i < d_ymax; i++) {
+ j = d_xmin;
+ while ((screen[j][i] == ' ') && (j < d_xmax))
+ j++;
+ /* was m=0 */
+ if (j >= d_xmax)
+ m = d_xmin; /* don't search backwards if blank
+ * line */
+ else { /* search backwards for end of line */
+ m = d_xmax - 1;
+ while ((screen[m][i] == ' ') && (m > d_xmin))
+ --m;
+ if (j <= m)
+ cursor(j + 1, i + 1);
+ else
+ continue;
+ }
+ while (j <= m) {
+ if (j <= m - 3) {
+ for (kk = j; kk <= j + 3; kk++)
+ if (screen[kk][i] != ' ')
+ kk = 1000;
+ if (kk < 1000) {
+ while (screen[j][i] == ' ' && j <= m)
+ j++;
+ cursor(j + 1, i + 1);
+ }
+ }
+ lprc(screen[j++][i]);
+ }
+ }
+ // setbold(); /* print out only bold objects now */
+
+ for (lastx = lasty = 127, i = d_ymin; i < d_ymax; i++)
+ for (j = d_xmin; j < d_xmax; j++) {
+ if ((kk = item[j][i]) != 0)
+ if (kk != OWALL)
+ if ((know[j][i]) && (mitem[j][i] == 0))
+ if (objnamelist[kk] != ' ') {
+ if (lasty != i + 1 || lastx != j)
+ cursor(lastx = j + 1, lasty = i + 1);
+ else
+ lastx++;
+ lprc(objnamelist[kk]);
+ }
+ }
+
+ // resetbold();
+ if (d_flag) {
+ always = 1;
+ botside();
+ always = 1;
+ bot_linex();
+ }
+ oldx = 99;
+ d_xmin = 0, d_xmax = MAXX, d_ymin = 0, d_ymax = MAXY; /* for limited screen
+ * drawing */
+}
+
+
+/*
+ showcell(x,y)
+
+ subroutine to display a cell location on the screen
+ */
+void
+showcell(int x, int y)
+{
+ int i, j, kk, mm;
+ if (c[BLINDCOUNT])
+ // 20150211 bkw:
+ /* return */; /* see nothing if blind */
+ if (c[AWARENESS]) {
+ minx = x - 3;
+ maxx = x + 3;
+ miny = y - 3;
+ maxy = y + 3;
+ } else {
+ minx = x - 1;
+ maxx = x + 1;
+ miny = y - 1;
+ maxy = y + 1;
+ }
+
+ if (minx < 0)
+ minx = 0;
+ if (maxx > MAXX - 1)
+ maxx = MAXX - 1;
+ if (miny < 0)
+ miny = 0;
+ if (maxy > MAXY - 1)
+ maxy = MAXY - 1;
+
+ for (j = miny; j <= maxy; j++)
+ for (mm = minx; mm <= maxx; mm++)
+ if (know[mm][j] == 0) {
+ cursor(mm + 1, j + 1);
+ x = maxx;
+ while (know[x][j])
+ --x;
+ for (i = mm; i <= x; i++) {
+ if ((kk = mitem[i][j]) != 0)
+ lprc(monstnamelist[kk]);
+ else
+ switch (kk = item[i][j]) {
+ case OWALL:
+ case 0:
+ case OIVTELETRAP:
+ case OTRAPARROWIV:
+ case OIVDARTRAP:
+ case OIVTRAPDOOR:
+ lprc(objnamelist[kk]);
+ break;
+
+ default:
+ setbold();
+ lprc(objnamelist[kk]);
+ resetbold();
+ };
+ know[i][j] = 1;
+ }
+ mm = maxx;
+ }
+}
+
+/*
+ this routine shows only the spot that is given it. the spaces around
+ these coordinated are not shown
+ used in godirect() in monster.c for missile weapons display
+ */
+void
+show1cell(int x, int y)
+{
+ if (c[BLINDCOUNT])
+ return; /* see nothing if blind */
+ cursor(x + 1, y + 1);
+ if ((k = mitem[x][y]) != 0)
+ lprc(monstnamelist[k]);
+ else
+ switch (k = item[x][y]) {
+ case OWALL:
+ case 0:
+ case OIVTELETRAP:
+ case OTRAPARROWIV:
+ case OIVDARTRAP:
+ case OIVTRAPDOOR:
+ lprc(objnamelist[k]);
+ break;
+
+ default:
+ setbold();
+ lprc(objnamelist[k]);
+ resetbold();
+ };
+ know[x][y] |= 1; /* we end up knowing about it */
+}
+
+/*
+ showplayer()
+
+ subroutine to show where the player is on the screen
+ cursor values start from 1 up
+ */
+void
+showplayer(void)
+{
+/* 20150211 bkw: put back the player's @. Took the function body
+ from some old larn source originally posted to USEnet in the
+ paleozoic era. */
+ if(oldx != 99) show1cell( oldx, oldy );
+ cursor(playerx+1,playery+1);
+ lprc('@');
+ cursor(playerx+1,playery+1);
+ oldx = playerx;
+ oldy = playery;
+}
+
+/*
+ moveplayer(dir)
+
+ subroutine to move the player from one room to another
+ returns 0 if can't move in that direction or hit a monster or on an object
+ else returns 1
+ nomove is set to 1 to stop the next move (inadvertent monsters hitting
+ players when walking into walls) if player walks off screen or into wall
+ */
+short diroffx[] = {0, 0, 1, 0, -1, 1, -1, 1, -1};
+short diroffy[] = {0, 1, 0, -1, 0, -1, -1, 1, 1};
+int
+moveplayer(int dir)
+ /* from = present room # direction =
+ * [1-north] [2-east] [3-south] [4-west]
+ * [5-northeast] [6-northwest] [7-southeast]
+ * [8-southwest] if direction=0, don't
+ * move--just show where he is */
+{
+ int kk, mm, i, j;
+ if (c[CONFUSE])
+ if (c[LEVEL] < rnd(30))
+ dir = rund(9); /* if confused any dir */
+ kk = playerx + diroffx[dir];
+ mm = playery + diroffy[dir];
+ if (kk < 0 || kk >= MAXX || mm < 0 || mm >= MAXY) {
+ nomove = 1;
+ return (yrepcount = 0);
+ }
+ i = item[kk][mm];
+ j = mitem[kk][mm];
+ if (i == OWALL && c[WTW] == 0) {
+ nomove = 1;
+ return (yrepcount = 0);
+ } /* hit a wall */
+ if (kk == 33 && mm == MAXY - 1 && level == 1) {
+ newcavelevel(0);
+ for (kk = 0; kk < MAXX; kk++)
+ for (mm = 0; mm < MAXY; mm++)
+ if (item[kk][mm] == OENTRANCE) {
+ playerx = kk;
+ playery = mm;
+ positionplayer();
+ drawscreen();
+ return (0);
+ }
+ }
+ if (j > 0) {
+ hitmonster(kk, mm);
+ return (yrepcount = 0);
+ } /* hit a monster */
+ lastpx = playerx;
+ lastpy = playery;
+ playerx = kk;
+ playery = mm;
+ if (i && i != OTRAPARROWIV && i != OIVTELETRAP && i != OIVDARTRAP && i != OIVTRAPDOOR)
+ return (yrepcount = 0);
+ else
+ return (1);
+}
+
+
+/*
+ * function to show what magic items have been discovered thus far
+ * enter with -1 for just spells, anything else will give scrolls & potions
+ */
+static int lincount, count;
+void
+seemagic(int arg)
+{
+ int i, number = 0;
+ count = lincount = 0;
+ nosignal = 1;
+
+ if (arg == -1) { /* if display spells while casting one */
+ for (number = i = 0; i < SPNUM; i++)
+ if (spelknow[i])
+ number++;
+ number = (number + 2) / 3 + 4; /* # lines needed to display */
+ cl_up(79, number);
+ cursor(1, 1);
+ } else {
+ resetscroll();
+ clear();
+ }
+
+ lprcat("The magic spells you have discovered thus far:\n\n");
+ for (i = 0; i < SPNUM; i++)
+ if (spelknow[i]) {
+ lprintf("%s %-20s ", spelcode[i], spelname[i]);
+ seepage();
+ }
+ if (arg == -1) {
+ seepage();
+ more();
+ nosignal = 0;
+ draws(0, MAXX, 0, number);
+ return;
+ }
+ lincount += 3;
+ if (count != 0) {
+ count = 2;
+ seepage();
+ }
+ lprcat("\nThe magic scrolls you have found to date are:\n\n");
+ count = 0;
+ for (i = 0; i < MAXSCROLL; i++)
+ if (scrollname[i][0])
+ if (scrollname[i][1] != ' ') {
+ lprintf("%-26s", &scrollname[i][1]);
+ seepage();
+ }
+ lincount += 3;
+ if (count != 0) {
+ count = 2;
+ seepage();
+ }
+ lprcat("\nThe magic potions you have found to date are:\n\n");
+ count = 0;
+ for (i = 0; i < MAXPOTION; i++)
+ if (potionname[i][0])
+ if (potionname[i][1] != ' ') {
+ lprintf("%-26s", &potionname[i][1]);
+ seepage();
+ }
+ if (lincount != 0)
+ more();
+ nosignal = 0;
+ setscroll();
+ drawscreen();
+}
+
+/*
+ * subroutine to paginate the seemagic function
+ */
+static void
+seepage(void)
+{
+ if (++count == 3) {
+ lincount++;
+ count = 0;
+ lprc('\n');
+ if (lincount > 17) {
+ lincount = 0;
+ more();
+ clear();
+ }
+ }
+}
diff --git a/larn/extern.h b/larn/extern.h
new file mode 100644
index 0000000..f0c681f
--- /dev/null
+++ b/larn/extern.h
@@ -0,0 +1,226 @@
+/* $NetBSD: extern.h,v 1.16 2011/08/29 20:30:37 joerg Exp $ */
+
+/*
+ * Copyright (c) 1997 Christos Zoulas. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* action.c */
+void act_remove_gems(int);
+void act_sit_throne(int);
+void act_drink_fountain(void);
+void act_wash_fountain(void);
+void act_desecrate_altar(void);
+void act_donation_pray(void);
+void act_just_pray(void);
+void act_ignore_altar(void);
+void act_open_chest(int, int);
+
+/* bill.c */
+__dead void mailbill(void);
+
+/* config.c */
+
+/* create.c */
+void makeplayer(void);
+void newcavelevel(int);
+void eat(int, int);
+int fillmonst(int);
+
+/* data.c */
+
+/* diag.c */
+void diag(void);
+int dcount(int);
+void diagdrawscreen(void);
+int savegame(char *);
+void restoregame(char *);
+
+/* display.c */
+void bottomline(void);
+void bottomhp(void);
+void bottomspell(void);
+void bottomdo(void);
+void bot_linex(void);
+void bottomgold(void);
+void draws(int, int, int, int);
+void drawscreen(void);
+void showcell(int, int);
+void show1cell(int, int);
+void showplayer(void);
+int moveplayer(int);
+void seemagic(int);
+
+/* fortune.c */
+const char *fortune(void);
+
+/* global.c */
+void raiselevel(void);
+void loselevel(void);
+void raiseexperience(long);
+void loseexperience(long);
+void losehp(int);
+void losemhp(int);
+void raisehp(int);
+void raisemhp(int);
+void raisemspells(int);
+void losemspells(int);
+int makemonst(int);
+void positionplayer(void);
+void recalc(void);
+void quit(void);
+void more(void);
+int take(int, int);
+int drop_object(int);
+void enchantarmor(void);
+void enchweapon(void);
+int pocketfull(void);
+int nearbymonst(void);
+int stealsomething(void);
+int emptyhanded(void);
+void creategem(void);
+void adjustcvalues(int, int);
+int getpassword(void);
+int getyn(void);
+int packweight(void);
+int rnd(int);
+int rund(int);
+
+/* help.c */
+void help(void);
+void welcome(void);
+
+/* io.c */
+void setupvt100(void);
+void clearvt100(void);
+int ttgetch(void);
+void scbr(void);
+void sncbr(void);
+void newgame(void);
+void lprintf(const char *, ...) __printflike(1, 2);
+void lprint(long);
+void lwrite(char *, int);
+long lgetc(void);
+long larn_lrint(void);
+void lrfill(char *, int);
+char *lgetw(void);
+char *lgetl(void);
+int lcreat(char *);
+int lopen(char *);
+int lappend(char *);
+void lrclose(void);
+void lwclose(void);
+void lprcat(const char *);
+void cursor(int, int);
+void cursors(void);
+void init_term(void);
+void cl_line(int, int);
+void cl_up(int, int);
+void cl_dn(int, int);
+void standout(const char *);
+void set_score_output(void);
+void lflush(void);
+char *tmcapcnv(char *, char *);
+void beep(void);
+
+/* main.c */
+int main(int, char **);
+void qshowstr(void);
+void show3(int);
+void parse2(void);
+unsigned long readnum(long);
+void szero(char *);
+
+/* monster.c */
+void createmonster(int);
+void createitem(int, int);
+void cast(void);
+void godirect(int, int, const char *, int, int);
+int vxy(int *, int *);
+void hitmonster(int, int);
+void hitplayer(int, int);
+void dropgold(int);
+void something(int);
+int newobject(int, int *);
+void checkloss(int);
+int annihilate(void);
+int newsphere(int, int, int, int);
+int rmsphere(int, int);
+
+/* moreobj.c */
+void oaltar(void);
+void othrone(int);
+void odeadthrone(void);
+void ochest(void);
+void ofountain(void);
+void fntchange(int);
+
+/* movem.c */
+void movemonst(void);
+
+/* nap.c */
+void nap(int);
+
+/* object.c */
+void lookforobject(void);
+void oteleport(int);
+void quaffpotion(int);
+void adjusttime(long);
+void read_scroll(int);
+void readbook(int);
+void iopts(void);
+void ignore(void);
+
+/* regen.c */
+void regen(void);
+
+/* savelev.c */
+void savelevel(void);
+void getlevel(void);
+
+/* scores.c */
+int makeboard(void);
+int hashewon(void);
+long paytaxes(long);
+void showscores(void);
+void showallscores(void);
+void died(int);
+void diedlog(void);
+int getplid(char *);
+
+/* signal.c */
+void sigsetup(void);
+
+/* store.c */
+void dndstore(void);
+void oschool(void);
+void obank(void);
+void obank2(void);
+void ointerest(void);
+void otradepost(void);
+void olrs(void);
+
+/* tok.c */
+int yylex(void);
+void flushall(void);
+void sethard(int);
+void readopts(void);
diff --git a/larn/fortune.c b/larn/fortune.c
new file mode 100644
index 0000000..4eb7f67
--- /dev/null
+++ b/larn/fortune.c
@@ -0,0 +1,94 @@
+/* $NetBSD: fortune.c,v 1.7 2009/08/12 08:04:05 dholland Exp $ */
+
+/*-
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)fortune.c 5.5 (Berkeley) 6/10/91";
+#else
+__RCSID("$NetBSD: fortune.c,v 1.7 2009/08/12 08:04:05 dholland Exp $");
+#endif
+#endif /* not lint */
+
+#include <stdlib.h>
+
+#include "header.h"
+#include "extern.h"
+/* fortune.c Larn is copyrighted 1986 by Noah Morgan. */
+
+/*
+ * function to return a random fortune from the fortune file
+ */
+
+static const char *flines[] = {
+ "gem value = gem * 2 ^ perfection",
+ "sitting down can have unexpected results",
+ "don't pry into the affairs of others",
+ "drinking can be hazardous to your health",
+ "beware of the gusher!",
+ "some monsters are greedy",
+ "nymphs have light fingers",
+ "try kissing a disenchantress!",
+ "hammers and brains don't mix",
+ "what does a potion of cure dianthroritis taste like?",
+ "hit point gain/loss when raising a level depends on constitution",
+ "healing a mighty wizard can be exhilarating",
+ "be sure to pay your taxes",
+ "are Vampires afraid of something?",
+ "some dragons can fly",
+ "dos thou strive for perfection?",
+ "patience is a virtue, unless your daughter dies",
+ "what does the Eye of Larn see in its guardian?",
+ "a level 25 player casts like crazy!",
+ "energy rings affect spell regeneration",
+ "difficulty affects regeneration",
+ "control of the pesty spirits is most helpful",
+ "don't fall into a bottomless pit",
+ "dexterity allows you to carry more",
+ "you can get 2 points of WC for the price of one",
+ "never enter the dungeon naked! the monsters will laugh at you!",
+ "did someone put itching powder in your armor?",
+ "you klutz!",
+ "avoid opening doors. you never know whats on the other side.",
+ "infinite regeneration ---> temptation",
+ "the greatest weapon in the game has not the highest Weapon Class",
+ "you can't buy the most powerful scroll",
+ "identify things before you use them",
+ "there's more than one way through a wall"
+};
+
+#define NFORTUNES 34
+
+const char *
+fortune(void)
+{
+ return (flines[random() % NFORTUNES]);
+}
diff --git a/larn/global.c b/larn/global.c
new file mode 100644
index 0000000..013aa6f
--- /dev/null
+++ b/larn/global.c
@@ -0,0 +1,861 @@
+/* $NetBSD: global.c,v 1.14 2012/06/19 05:30:43 dholland Exp $ */
+
+/*
+ * global.c Larn is copyrighted 1986 by Noah Morgan.
+ *
+ * raiselevel() subroutine to raise the player one level
+ * loselevel() subroutine to lower the player by one level
+ * raiseexperience(x) subroutine to increase experience points
+ * loseexperience(x) subroutine to lose experience points
+ * losehp(x) subroutine to remove hit points from the player
+ * losemhp(x) subroutine to remove max # hit points from the player
+ * raisehp(x) subroutine to gain hit points
+ * raisemhp(x) subroutine to gain maximum hit points
+ * losemspells(x) subroutine to lose maximum spells
+ * raisemspells(x) subroutine to gain maximum spells
+ * makemonst(lev) function to return monster number for a randomly
+ * selected monster
+ * positionplayer() function to be sure player is not in a wall
+ * recalc() function to recalculate the armor class of the player
+ * quit() subroutine to ask if the player really wants to quit
+ */
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: global.c,v 1.14 2012/06/19 05:30:43 dholland Exp $");
+#endif /* not lint */
+
+#include <string.h>
+#include <unistd.h>
+#include "header.h"
+#include "extern.h"
+extern int score[], dropflag;
+extern char *what[], *who[];
+extern char winner[];
+extern char sciv[SCORESIZE + 1][26][2];
+extern const char *password;
+
+/*
+ raiselevel()
+
+ subroutine to raise the player one level
+ uses the skill[] array to find level boundarys
+ uses c[EXPERIENCE] c[LEVEL]
+ */
+void
+raiselevel(void)
+{
+ if (c[LEVEL] < MAXPLEVEL)
+ raiseexperience((long) (skill[c[LEVEL]] - c[EXPERIENCE]));
+}
+
+/*
+ loselevel()
+
+ subroutine to lower the players character level by one
+ */
+void
+loselevel(void)
+{
+ if (c[LEVEL] > 1)
+ loseexperience((long) (c[EXPERIENCE] - skill[c[LEVEL] - 1] + 1));
+}
+
+/*
+ raiseexperience(x)
+
+ subroutine to increase experience points
+ */
+void
+raiseexperience(long x)
+{
+ int i, tmp;
+ i = c[LEVEL];
+ c[EXPERIENCE] += x;
+ while (c[EXPERIENCE] >= skill[c[LEVEL]] && (c[LEVEL] < MAXPLEVEL)) {
+ tmp = (c[CONSTITUTION] - c[HARDGAME]) >> 1;
+ c[LEVEL]++;
+ raisemhp((int) (rnd(3) + rnd((tmp > 0) ? tmp : 1)));
+ raisemspells((int) rund(3));
+ if (c[LEVEL] < 7 - c[HARDGAME])
+ raisemhp((int) (c[CONSTITUTION] >> 2));
+ }
+ if (c[LEVEL] != i) {
+ cursors();
+ beep();
+ lprintf("\nWelcome to level %ld", (long) c[LEVEL]); /* if we changed levels */
+ }
+ bottomline();
+}
+
+/*
+ loseexperience(x)
+
+ subroutine to lose experience points
+ */
+void
+loseexperience(long x)
+{
+ int i, tmp;
+ i = c[LEVEL];
+ c[EXPERIENCE] -= x;
+ if (c[EXPERIENCE] < 0)
+ c[EXPERIENCE] = 0;
+ while (c[EXPERIENCE] < skill[c[LEVEL] - 1]) {
+ if (--c[LEVEL] <= 1)
+ c[LEVEL] = 1; /* down one level */
+ tmp = (c[CONSTITUTION] - c[HARDGAME]) >> 1; /* lose hpoints */
+ losemhp((int) rnd((tmp > 0) ? tmp : 1)); /* lose hpoints */
+ if (c[LEVEL] < 7 - c[HARDGAME])
+ losemhp((int) (c[CONSTITUTION] >> 2));
+ losemspells((int) rund(3)); /* lose spells */
+ }
+ if (i != c[LEVEL]) {
+ cursors();
+ beep();
+ lprintf("\nYou went down to level %ld!", (long) c[LEVEL]);
+ }
+ bottomline();
+}
+
+/*
+ losehp(x)
+ losemhp(x)
+
+ subroutine to remove hit points from the player
+ warning -- will kill player if hp goes to zero
+ */
+void
+losehp(int x)
+{
+ if ((c[HP] -= x) <= 0) {
+ beep();
+ lprcat("\n");
+ nap(3000);
+ died(lastnum);
+ }
+}
+
+void
+losemhp(int x)
+{
+ c[HP] -= x;
+ if (c[HP] < 1)
+ c[HP] = 1;
+ c[HPMAX] -= x;
+ if (c[HPMAX] < 1)
+ c[HPMAX] = 1;
+}
+
+/*
+ raisehp(x)
+ raisemhp(x)
+
+ subroutine to gain maximum hit points
+ */
+void
+raisehp(int x)
+{
+ if ((c[HP] += x) > c[HPMAX])
+ c[HP] = c[HPMAX];
+}
+
+void
+raisemhp(int x)
+{
+ c[HPMAX] += x;
+ c[HP] += x;
+}
+
+/*
+ raisemspells(x)
+
+ subroutine to gain maximum spells
+ */
+void
+raisemspells(int x)
+{
+ c[SPELLMAX] += x;
+ c[SPELLS] += x;
+}
+
+/*
+ losemspells(x)
+
+ subroutine to lose maximum spells
+ */
+void
+losemspells(int x)
+{
+ if ((c[SPELLMAX] -= x) < 0)
+ c[SPELLMAX] = 0;
+ if ((c[SPELLS] -= x) < 0)
+ c[SPELLS] = 0;
+}
+
+/*
+ makemonst(lev)
+ int lev;
+
+ function to return monster number for a randomly selected monster
+ for the given cave level
+ */
+int
+makemonst(int lev)
+{
+ int tmp, x;
+ if (lev < 1)
+ lev = 1;
+ if (lev > 12)
+ lev = 12;
+ tmp = WATERLORD;
+ if (lev < 5)
+ while (tmp == WATERLORD)
+ tmp = rnd((x = monstlevel[lev - 1]) ? x : 1);
+ else
+ while (tmp == WATERLORD)
+ tmp = rnd((x = monstlevel[lev - 1] - monstlevel[lev - 4]) ? x : 1) + monstlevel[lev - 4];
+
+ while (monster[tmp].genocided && tmp < MAXMONST)
+ tmp++; /* genocided? */
+ return (tmp);
+}
+
+/*
+ positionplayer()
+
+ function to be sure player is not in a wall
+ */
+void
+positionplayer(void)
+{
+ int try;
+ try = 2;
+ while ((item[playerx][playery] || mitem[playerx][playery]) && (try))
+ if (++playerx >= MAXX - 1) {
+ playerx = 1;
+ if (++playery >= MAXY - 1) {
+ playery = 1;
+ --try;
+ }
+ }
+ if (try == 0)
+ lprcat("Failure in positionplayer\n");
+}
+
+/*
+ recalc() function to recalculate the armor class of the player
+ */
+void
+recalc(void)
+{
+ int i, j, k;
+ c[AC] = c[MOREDEFENSES];
+ if (c[WEAR] >= 0)
+ switch (iven[c[WEAR]]) {
+ case OSHIELD:
+ c[AC] += 2 + ivenarg[c[WEAR]];
+ break;
+ case OLEATHER:
+ c[AC] += 2 + ivenarg[c[WEAR]];
+ break;
+ case OSTUDLEATHER:
+ c[AC] += 3 + ivenarg[c[WEAR]];
+ break;
+ case ORING:
+ c[AC] += 5 + ivenarg[c[WEAR]];
+ break;
+ case OCHAIN:
+ c[AC] += 6 + ivenarg[c[WEAR]];
+ break;
+ case OSPLINT:
+ c[AC] += 7 + ivenarg[c[WEAR]];
+ break;
+ case OPLATE:
+ c[AC] += 9 + ivenarg[c[WEAR]];
+ break;
+ case OPLATEARMOR:
+ c[AC] += 10 + ivenarg[c[WEAR]];
+ break;
+ case OSSPLATE:
+ c[AC] += 12 + ivenarg[c[WEAR]];
+ break;
+ }
+
+ if (c[SHIELD] >= 0)
+ if (iven[c[SHIELD]] == OSHIELD)
+ c[AC] += 2 + ivenarg[c[SHIELD]];
+ if (c[WIELD] < 0)
+ c[WCLASS] = 0;
+ else {
+ i = ivenarg[c[WIELD]];
+ switch (iven[c[WIELD]]) {
+ case ODAGGER:
+ c[WCLASS] = 3 + i;
+ break;
+ case OBELT:
+ c[WCLASS] = 7 + i;
+ break;
+ case OSHIELD:
+ c[WCLASS] = 8 + i;
+ break;
+ case OSPEAR:
+ c[WCLASS] = 10 + i;
+ break;
+ case OFLAIL:
+ c[WCLASS] = 14 + i;
+ break;
+ case OBATTLEAXE:
+ c[WCLASS] = 17 + i;
+ break;
+ case OLANCE:
+ c[WCLASS] = 19 + i;
+ break;
+ case OLONGSWORD:
+ c[WCLASS] = 22 + i;
+ break;
+ case O2SWORD:
+ c[WCLASS] = 26 + i;
+ break;
+ case OSWORD:
+ c[WCLASS] = 32 + i;
+ break;
+ case OSWORDofSLASHING:
+ c[WCLASS] = 30 + i;
+ break;
+ case OHAMMER:
+ c[WCLASS] = 35 + i;
+ break;
+ default:
+ c[WCLASS] = 0;
+ }
+ }
+ c[WCLASS] += c[MOREDAM];
+
+ /* now for regeneration abilities based on rings */
+ c[REGEN] = 1;
+ c[ENERGY] = 0;
+ j = 0;
+ for (k = 25; k > 0; k--)
+ if (iven[k]) {
+ j = k;
+ k = 0;
+ }
+ for (i = 0; i <= j; i++) {
+ switch (iven[i]) {
+ case OPROTRING:
+ c[AC] += ivenarg[i] + 1;
+ break;
+ case ODAMRING:
+ c[WCLASS] += ivenarg[i] + 1;
+ break;
+ case OBELT:
+ c[WCLASS] += ((ivenarg[i] << 1)) + 2;
+ break;
+
+ case OREGENRING:
+ c[REGEN] += ivenarg[i] + 1;
+ break;
+ case ORINGOFEXTRA:
+ c[REGEN] += 5 * (ivenarg[i] + 1);
+ break;
+ case OENERGYRING:
+ c[ENERGY] += ivenarg[i] + 1;
+ break;
+ }
+ }
+}
+
+
+/*
+ quit()
+
+ subroutine to ask if the player really wants to quit
+ */
+void
+quit(void)
+{
+ int i;
+ cursors();
+ strcpy(lastmonst, "");
+ lprcat("\n\nDo you really want to quit?");
+ while (1) {
+ i = ttgetch();
+ if (i == 'y') {
+ died(300);
+ return;
+ }
+ if ((i == 'n') || (i == '\33')) {
+ lprcat(" no");
+ lflush();
+ return;
+ }
+ lprcat("\n");
+ setbold();
+ lprcat("Yes");
+ resetbold();
+ lprcat(" or ");
+ setbold();
+ lprcat("No");
+ resetbold();
+ lprcat(" please? Do you want to quit? ");
+ }
+}
+
+/*
+ function to ask --more-- then the user must enter a space
+ */
+void
+more(void)
+{
+ lprcat("\n --- press ");
+ standout("space");
+ lprcat(" to continue --- ");
+ while (ttgetch() != ' ');
+}
+
+/*
+ function to put something in the players inventory
+ returns 0 if success, 1 if a failure
+ */
+int
+take(int theitem, int arg)
+{
+ int i, limit;
+ /* cursors(); */
+ if ((limit = 15 + (c[LEVEL] >> 1)) > 26)
+ limit = 26;
+ for (i = 0; i < limit; i++)
+ if (iven[i] == 0) {
+ iven[i] = theitem;
+ ivenarg[i] = arg;
+ limit = 0;
+ switch (theitem) {
+ case OPROTRING:
+ case ODAMRING:
+ case OBELT:
+ limit = 1;
+ break;
+ case ODEXRING:
+ c[DEXTERITY] += ivenarg[i] + 1;
+ limit = 1;
+ break;
+ case OSTRRING:
+ c[STREXTRA] += ivenarg[i] + 1;
+ limit = 1;
+ break;
+ case OCLEVERRING:
+ c[INTELLIGENCE] += ivenarg[i] + 1;
+ limit = 1;
+ break;
+ case OHAMMER:
+ c[DEXTERITY] += 10;
+ c[STREXTRA] += 10;
+ c[INTELLIGENCE] -= 10;
+ limit = 1;
+ break;
+
+ case OORBOFDRAGON:
+ c[SLAYING]++;
+ break;
+ case OSPIRITSCARAB:
+ c[NEGATESPIRIT]++;
+ break;
+ case OCUBEofUNDEAD:
+ c[CUBEofUNDEAD]++;
+ break;
+ case ONOTHEFT:
+ c[NOTHEFT]++;
+ break;
+ case OSWORDofSLASHING:
+ c[DEXTERITY] += 5;
+ limit = 1;
+ break;
+ };
+ lprcat("\nYou pick up:");
+ srcount = 0;
+ show3(i);
+ if (limit)
+ bottomline();
+ return (0);
+ }
+ lprcat("\nYou can't carry anything else");
+ return (1);
+}
+
+/*
+ subroutine to drop an object
+ returns 1 if something there already else 0
+ */
+int
+drop_object(int k)
+{
+ int theitem;
+ if ((k < 0) || (k > 25))
+ return (0);
+ theitem = iven[k];
+ cursors();
+ if (theitem == 0) {
+ lprintf("\nYou don't have item %c! ", k + 'a');
+ return (1);
+ }
+ if (item[playerx][playery]) {
+ beep();
+ lprcat("\nThere's something here already");
+ return (1);
+ }
+ if (playery == MAXY - 1 && playerx == 33)
+ return (1); /* not in entrance */
+ item[playerx][playery] = theitem;
+ iarg[playerx][playery] = ivenarg[k];
+ srcount = 0;
+ lprcat("\n You drop:");
+ show3(k); /* show what item you dropped */
+ know[playerx][playery] = 0;
+ iven[k] = 0;
+ if (c[WIELD] == k)
+ c[WIELD] = -1;
+ if (c[WEAR] == k)
+ c[WEAR] = -1;
+ if (c[SHIELD] == k)
+ c[SHIELD] = -1;
+ adjustcvalues(theitem, ivenarg[k]);
+ dropflag = 1; /* say dropped an item so wont ask to pick it
+ * up right away */
+ return (0);
+}
+
+/*
+ function to enchant armor player is currently wearing
+ */
+void
+enchantarmor(void)
+{
+ int tmp;
+ if (c[WEAR] < 0) {
+ if (c[SHIELD] < 0) {
+ cursors();
+ beep();
+ lprcat("\nYou feel a sense of loss");
+ return;
+ } else {
+ tmp = iven[c[SHIELD]];
+ if (tmp != OSCROLL)
+ if (tmp != OPOTION) {
+ ivenarg[c[SHIELD]]++;
+ bottomline();
+ }
+ }
+ }
+ tmp = iven[c[WEAR]];
+ if (tmp != OSCROLL)
+ if (tmp != OPOTION) {
+ ivenarg[c[WEAR]]++;
+ bottomline();
+ }
+}
+
+/*
+ function to enchant a weapon presently being wielded
+ */
+void
+enchweapon(void)
+{
+ int tmp;
+ if (c[WIELD] < 0) {
+ cursors();
+ beep();
+ lprcat("\nYou feel a sense of loss");
+ return;
+ }
+ tmp = iven[c[WIELD]];
+ if (tmp != OSCROLL)
+ if (tmp != OPOTION) {
+ ivenarg[c[WIELD]]++;
+ if (tmp == OCLEVERRING)
+ c[INTELLIGENCE]++;
+ else if (tmp == OSTRRING)
+ c[STREXTRA]++;
+ else if (tmp == ODEXRING)
+ c[DEXTERITY]++;
+ bottomline();
+ }
+}
+
+/*
+ routine to tell if player can carry one more thing
+ returns 1 if pockets are full, else 0
+ */
+int
+pocketfull(void)
+{
+ int i, limit;
+ if ((limit = 15 + (c[LEVEL] >> 1)) > 26)
+ limit = 26;
+ for (i = 0; i < limit; i++)
+ if (iven[i] == 0)
+ return (0);
+ return (1);
+}
+
+/*
+ function to return 1 if a monster is next to the player else returns 0
+ */
+int
+nearbymonst(void)
+{
+ int tmp, tmp2;
+ for (tmp = playerx - 1; tmp < playerx + 2; tmp++)
+ for (tmp2 = playery - 1; tmp2 < playery + 2; tmp2++)
+ if (mitem[tmp][tmp2])
+ return (1); /* if monster nearby */
+ return (0);
+}
+
+/*
+ function to steal an item from the players pockets
+ returns 1 if steals something else returns 0
+ */
+int
+stealsomething(void)
+{
+ int i, j;
+ j = 100;
+ while (1) {
+ i = rund(26);
+ if (iven[i])
+ if (c[WEAR] != i)
+ if (c[WIELD] != i)
+ if (c[SHIELD] != i) {
+ srcount = 0;
+ show3(i);
+ adjustcvalues(iven[i], ivenarg[i]);
+ iven[i] = 0;
+ return (1);
+ }
+ if (--j <= 0)
+ return (0);
+ }
+}
+
+/*
+ function to return 1 is player carrys nothing else return 0
+ */
+int
+emptyhanded(void)
+{
+ int i;
+ for (i = 0; i < 26; i++)
+ if (iven[i])
+ if (i != c[WIELD])
+ if (i != c[WEAR])
+ if (i != c[SHIELD])
+ return (0);
+ return (1);
+}
+
+/*
+ function to create a gem on a square near the player
+ */
+void
+creategem(void)
+{
+ int i, j;
+ switch (rnd(4)) {
+ case 1:
+ i = ODIAMOND;
+ j = 50;
+ break;
+ case 2:
+ i = ORUBY;
+ j = 40;
+ break;
+ case 3:
+ i = OEMERALD;
+ j = 30;
+ break;
+ default:
+ i = OSAPPHIRE;
+ j = 20;
+ break;
+ };
+ createitem(i, rnd(j) + j / 10);
+}
+
+/*
+ function to change character levels as needed when dropping an object
+ that affects these characteristics
+ */
+void
+adjustcvalues(int theitem, int arg)
+{
+ int flag;
+ flag = 0;
+ switch (theitem) {
+ case ODEXRING:
+ c[DEXTERITY] -= arg + 1;
+ flag = 1;
+ break;
+ case OSTRRING:
+ c[STREXTRA] -= arg + 1;
+ flag = 1;
+ break;
+ case OCLEVERRING:
+ c[INTELLIGENCE] -= arg + 1;
+ flag = 1;
+ break;
+ case OHAMMER:
+ c[DEXTERITY] -= 10;
+ c[STREXTRA] -= 10;
+ c[INTELLIGENCE] += 10;
+ flag = 1;
+ break;
+ case OSWORDofSLASHING:
+ c[DEXTERITY] -= 5;
+ flag = 1;
+ break;
+ case OORBOFDRAGON:
+ --c[SLAYING];
+ return;
+ case OSPIRITSCARAB:
+ --c[NEGATESPIRIT];
+ return;
+ case OCUBEofUNDEAD:
+ --c[CUBEofUNDEAD];
+ return;
+ case ONOTHEFT:
+ --c[NOTHEFT];
+ return;
+ case OLANCE:
+ c[LANCEDEATH] = 0;
+ return;
+ case OPOTION:
+ case OSCROLL:
+ return;
+
+ default:
+ flag = 1;
+ };
+ if (flag)
+ bottomline();
+}
+
+/*
+ function to ask user for a password (no echo)
+ returns 1 if entered correctly, 0 if not
+ */
+static char gpwbuf[33];
+int
+getpassword(void)
+{
+ int i, j;
+ char *gpwp;
+ scbr(); /* system("stty -echo cbreak"); */
+ gpwp = gpwbuf;
+ lprcat("\nEnter Password: ");
+ lflush();
+ i = strlen(password);
+ for (j = 0; j < i; j++)
+ *gpwp++ = ttgetch();
+ gpwbuf[i] = 0;
+ sncbr(); /* system("stty echo -cbreak"); */
+ if (strcmp(gpwbuf, password) != 0) {
+ lprcat("\nSorry\n");
+ lflush();
+ return (0);
+ } else
+ return (1);
+}
+
+/*
+ subroutine to get a yes or no response from the user
+ returns y or n
+ */
+int
+getyn(void)
+{
+ int i;
+ i = 0;
+ while (i != 'y' && i != 'n' && i != '\33')
+ i = ttgetch();
+ return (i);
+}
+
+/*
+ function to calculate the pack weight of the player
+ returns the number of pounds the player is carrying
+ */
+int
+packweight(void)
+{
+ int i, j, k;
+ k = c[GOLD] / 1000;
+ j = 25;
+ while ((iven[j] == 0) && (j > 0))
+ --j;
+ for (i = 0; i <= j; i++)
+ switch (iven[i]) {
+ case 0:
+ break;
+ case OSSPLATE:
+ case OPLATEARMOR:
+ k += 40;
+ break;
+ case OPLATE:
+ k += 35;
+ break;
+ case OHAMMER:
+ k += 30;
+ break;
+ case OSPLINT:
+ k += 26;
+ break;
+ case OSWORDofSLASHING:
+ case OCHAIN:
+ case OBATTLEAXE:
+ case O2SWORD:
+ k += 23;
+ break;
+ case OLONGSWORD:
+ case OSWORD:
+ case ORING:
+ case OFLAIL:
+ k += 20;
+ break;
+ case OLANCE:
+ case OSTUDLEATHER:
+ k += 15;
+ break;
+ case OLEATHER:
+ case OSPEAR:
+ k += 8;
+ break;
+ case OORBOFDRAGON:
+ case OBELT:
+ k += 4;
+ break;
+ case OSHIELD:
+ k += 7;
+ break;
+ case OCHEST:
+ k += 30 + ivenarg[i];
+ break;
+ default:
+ k++;
+ };
+ return (k);
+}
+
+#ifndef MACRORND
+/* macros to generate random numbers 1<=rnd(N)<=N 0<=rund(N)<=N-1 */
+int
+rnd(int x)
+{
+ return ((((randx = randx * 1103515245 + 12345) >> 7) % (x)) + 1);
+}
+
+int
+rund(int x)
+{
+ return ((((randx = randx * 1103515245 + 12345) >> 7) % (x)));
+}
+#endif /* MACRORND */
diff --git a/larn/header.h b/larn/header.h
new file mode 100644
index 0000000..121a472
--- /dev/null
+++ b/larn/header.h
@@ -0,0 +1,444 @@
+/* $NetBSD: header.h,v 1.22 2008/08/29 00:37:38 gmcgarry Exp $ */
+
+/* header.h Larn is copyrighted 1986 by Noah Morgan. */
+
+#include <sys/types.h>
+
+#define MAXLEVEL 11
+/* max # levels in the dungeon */
+#define MAXVLEVEL 3
+/* max # of levels in the temple of the luran */
+#define MAXX 67
+#define MAXY 17
+
+#define SCORESIZE 10
+/* this is the number of people on a scoreboard max */
+#define MAXPLEVEL 100
+/* maximum player level allowed */
+#define MAXMONST 56
+/* maximum # monsters in the dungeon */
+#define SPNUM 38
+/* maximum number of spells in existence */
+#define MAXSCROLL 28
+/* maximum number of scrolls that are possible */
+#define MAXPOTION 35
+/* maximum number of potions that are possible */
+#define TIMELIMIT 30000
+/* the maximum number of moves before the game is called */
+#define TAXRATE 1/20
+/* the tax rate for the LRS */
+#define MAXOBJ 93
+/* the maximum number of objects n < MAXOBJ */
+
+/* this is the structure definition of the monster data */
+struct monst {
+ const char *name;
+ char level;
+ short armorclass;
+ char damage;
+ char attack;
+ char defense;
+ char genocided;
+ char intelligence; /* monsters intelligence -- used to
+ * choose movement */
+ short gold;
+ short hitpoints;
+ unsigned long experience;
+};
+
+/* this is the structure definition for the items in the dnd store */
+struct _itm {
+ short price;
+ u_char obj;
+ u_char arg;
+ char qty;
+};
+
+/* this is the structure that holds the entire dungeon specifications */
+struct cel {
+ short hitp; /* monster's hit points */
+ char mitem; /* the monster ID */
+ char item; /* the object's ID */
+ short iarg; /* the object's argument */
+ char know; /* have we been here before */
+};
+
+/* this is the structure for maintaining & moving the spheres of annihilation */
+struct sphere {
+ struct sphere *p; /* pointer to next structure */
+ char x, y, lev; /* location of the sphere */
+ char dir; /* direction sphere is going in */
+ short lifetime; /* duration of the sphere */
+};
+
+/* defines for the character attribute array c[] */
+#define STRENGTH 0 /* characters physical strength not due to
+ * objects */
+#define INTELLIGENCE 1
+#define WISDOM 2
+#define CONSTITUTION 3
+#define DEXTERITY 4
+#define CHARISMA 5
+#define HPMAX 6
+#define HP 7
+#define GOLD 8
+#define EXPERIENCE 9
+#define LEVEL 10
+#define REGEN 11
+#define WCLASS 12
+#define AC 13
+#define BANKACCOUNT 14
+#define SPELLMAX 15
+#define SPELLS 16
+#define ENERGY 17
+#define ECOUNTER 18
+#define MOREDEFENSES 19
+#define WEAR 20
+#define PROTECTIONTIME 21
+#define WIELD 22
+#define AMULET 23
+#define REGENCOUNTER 24
+#define MOREDAM 25
+#define DEXCOUNT 26
+#define STRCOUNT 27
+#define BLINDCOUNT 28
+#define CAVELEVEL 29
+#define CONFUSE 30
+#define ALTPRO 31
+#define HERO 32
+#define CHARMCOUNT 33
+#define INVISIBILITY 34
+#define CANCELLATION 35
+#define HASTESELF 36
+#define EYEOFLARN 37
+#define AGGRAVATE 38
+#define GLOBE 39
+#define TELEFLAG 40
+#define SLAYING 41
+#define NEGATESPIRIT 42
+#define SCAREMONST 43
+#define AWARENESS 44
+#define HOLDMONST 45
+#define TIMESTOP 46
+#define HASTEMONST 47
+#define CUBEofUNDEAD 48
+#define GIANTSTR 49
+#define FIRERESISTANCE 50
+#define BESSMANN 51
+#define NOTHEFT 52
+#define HARDGAME 53
+#define CPUTIME 54
+#define BYTESIN 55
+#define BYTESOUT 56
+#define MOVESMADE 57
+#define MONSTKILLED 58
+#define SPELLSCAST 59
+#define LANCEDEATH 60
+#define SPIRITPRO 61
+#define UNDEADPRO 62
+#define SHIELD 63
+#define STEALTH 64
+#define ITCHING 65
+#define LAUGHING 66
+#define DRAINSTRENGTH 67
+#define CLUMSINESS 68
+#define INFEEBLEMENT 69
+#define HALFDAM 70
+#define SEEINVISIBLE 71
+#define FILLROOM 72
+#define RANDOMWALK 73
+#define SPHCAST 74 /* nz if an active sphere of annihilation */
+#define WTW 75 /* walk through walls */
+#define STREXTRA 76 /* character strength due to objects or
+ * enchantments */
+#define TMP 77 /* misc scratch space */
+#define LIFEPROT 78 /* life protection counter */
+
+/* defines for the objects in the game */
+
+#define OALTAR 1
+#define OTHRONE 2
+#define OORB 3
+#define OPIT 4
+#define OSTAIRSUP 5
+#define OELEVATORUP 6
+#define OFOUNTAIN 7
+#define OSTATUE 8
+#define OTELEPORTER 9
+#define OSCHOOL 10
+#define OMIRROR 11
+#define ODNDSTORE 12
+#define OSTAIRSDOWN 13
+#define OELEVATORDOWN 14
+#define OBANK2 15
+#define OBANK 16
+#define ODEADFOUNTAIN 17
+#define OMAXGOLD 70
+#define OGOLDPILE 18
+#define OOPENDOOR 19
+#define OCLOSEDDOOR 20
+#define OWALL 21
+#define OTRAPARROW 66
+#define OTRAPARROWIV 67
+
+#define OLARNEYE 22
+
+#define OPLATE 23
+#define OCHAIN 24
+#define OLEATHER 25
+#define ORING 60
+#define OSTUDLEATHER 61
+#define OSPLINT 62
+#define OPLATEARMOR 63
+#define OSSPLATE 64
+#define OSHIELD 68
+#define OELVENCHAIN 92
+
+#define OSWORDofSLASHING 26
+#define OHAMMER 27
+#define OSWORD 28
+#define O2SWORD 29
+#define OSPEAR 30
+#define ODAGGER 31
+#define OBATTLEAXE 57
+#define OLONGSWORD 58
+#define OFLAIL 59
+#define OLANCE 65
+#define OVORPAL 90
+#define OSLAYER 91
+
+#define ORINGOFEXTRA 32
+#define OREGENRING 33
+#define OPROTRING 34
+#define OENERGYRING 35
+#define ODEXRING 36
+#define OSTRRING 37
+#define OCLEVERRING 38
+#define ODAMRING 39
+
+#define OBELT 40
+
+#define OSCROLL 41
+#define OPOTION 42
+#define OBOOK 43
+#define OCHEST 44
+#define OAMULET 45
+
+#define OORBOFDRAGON 46
+#define OSPIRITSCARAB 47
+#define OCUBEofUNDEAD 48
+#define ONOTHEFT 49
+
+#define ODIAMOND 50
+#define ORUBY 51
+#define OEMERALD 52
+#define OSAPPHIRE 53
+
+#define OENTRANCE 54
+#define OVOLDOWN 55
+#define OVOLUP 56
+#define OHOME 69
+
+#define OKGOLD 71
+#define ODGOLD 72
+#define OIVDARTRAP 73
+#define ODARTRAP 74
+#define OTRAPDOOR 75
+#define OIVTRAPDOOR 76
+#define OTRADEPOST 77
+#define OIVTELETRAP 78
+#define ODEADTHRONE 79
+#define OANNIHILATION 80 /* sphere of annihilation */
+#define OTHRONE2 81
+#define OLRS 82 /* Larn Revenue Service */
+#define OCOOKIE 83
+#define OURN 84
+#define OBRASSLAMP 85
+#define OHANDofFEAR 86 /* hand of fear */
+#define OSPHTAILSMAN 87 /* tailsman of the sphere */
+#define OWWAND 88 /* wand of wonder */
+#define OPSTAFF 89 /* staff of power */
+/* used up to 92 */
+
+/* defines for the monsters as objects */
+
+#define BAT 1
+#define GNOME 2
+#define HOBGOBLIN 3
+#define JACKAL 4
+#define KOBOLD 5
+#define ORC 6
+#define SNAKE 7
+#define CENTIPEDE 8
+#define JACULI 9
+#define TROGLODYTE 10
+#define ANT 11
+#define EYE 12
+#define LEPRECHAUN 13
+#define NYMPH 14
+#define QUASIT 15
+#define RUSTMONSTER 16
+#define ZOMBIE 17
+#define ASSASSINBUG 18
+#define BUGBEAR 19
+#define HELLHOUND 20
+#define ICELIZARD 21
+#define CENTAUR 22
+#define TROLL 23
+#define YETI 24
+#define WHITEDRAGON 25
+#define ELF 26
+#define CUBE 27
+#define METAMORPH 28
+#define VORTEX 29
+#define ZILLER 30
+#define VIOLETFUNGI 31
+#define WRAITH 32
+#define FORVALAKA 33
+#define LAMANOBE 34
+#define OSEQUIP 35
+#define ROTHE 36
+#define XORN 37
+#define VAMPIRE 38
+#define INVISIBLESTALKER 39
+#define POLTERGEIST 40
+#define DISENCHANTRESS 41
+#define SHAMBLINGMOUND 42
+#define YELLOWMOLD 43
+#define UMBERHULK 44
+#define GNOMEKING 45
+#define MIMIC 46
+#define WATERLORD 47
+#define BRONZEDRAGON 48
+#define GREENDRAGON 49
+#define PURPLEWORM 50
+#define XVART 51
+#define SPIRITNAGA 52
+#define SILVERDRAGON 53
+#define PLATINUMDRAGON 54
+#define GREENURCHIN 55
+#define REDDRAGON 56
+#define DEMONLORD 57
+#define DEMONPRINCE 64
+
+#ifndef NULL
+#define NULL 0
+#endif
+#define BUFBIG 4096 /* size of the output buffer */
+#define MAXIBUF 4096 /* size of the input buffer */
+#define LOGNAMESIZE 40 /* max size of the players name */
+#define PSNAMESIZE 40 /* max size of the process name */
+
+#ifndef NODEFS
+extern char VERSION, SUBVERSION;
+extern u_char beenhere[], boldon, cheat, ckpflag;
+extern const char *class[];
+extern u_char course[];
+extern char diagfile[], helpfile[], ckpfile[], larnlevels[],
+ playerids[], optsfile[1024], psname[], savefilename[],
+ scorefile[];
+extern u_char *inbuffer;
+extern u_char item[MAXX][MAXY], iven[], know[MAXX][MAXY];
+extern const char *levelname[];
+extern char logfile[], loginname[], logname[], lastmonst[];
+extern u_char *lpbuf, *lpend;
+extern u_char *lpnt, moved[MAXX][MAXY], mitem[MAXX][MAXY], monstlevel[];
+extern char monstnamelist[], objnamelist[];
+extern u_char nch[], ndgg[], nlpts[], nomove, nosignal, nowelcome;
+extern u_char nplt[], nsw[];
+extern const char *objectname[];
+extern const char *potionhide[], *potionname[];
+extern const char *spelcode[], *spelname[], *spelmes[];
+extern char aborted[], spelweird[MAXMONST + 8][SPNUM];
+extern u_char potprob[];
+extern u_char predostuff, restorflag, scprob[];
+extern u_char screen[MAXX][MAXY], sex;
+extern const char *speldescript[];
+extern const char *scrollhide[], *scrollname[];
+extern u_char spelknow[];
+extern u_char splev[], stealth[MAXX][MAXY], wizard;
+extern short diroffx[], diroffy[], hitflag, hit2flag, hit3flag, hitp[MAXX][MAXY];
+extern short iarg[MAXX][MAXY], ivenarg[], lasthx, lasthy, lastnum, lastpx,
+ lastpy;
+extern short nobeep, oldx, oldy, playerx, playery, level;
+extern int enable_scroll, srcount, yrepcount, userid, wisid,
+ io_outfd, io_infd;
+extern gid_t gid, egid;
+extern long outstanding_taxes, skill[], gltime, c[], cbak[];
+extern time_t initialtime;
+extern unsigned long randx;
+extern struct cel *cell;
+extern struct monst monster[];
+extern struct sphere *spheres;
+extern struct _itm itm[];
+extern int rmst, lasttime;
+
+/* macro to create scroll #'s with probability of occurrence */
+#define newscroll() (scprob[rund(81)])
+/* macro to return a potion # created with probability of occurrence */
+#define newpotion() (potprob[rund(41)])
+/* macro to return the + points on created leather armor */
+#define newleather() (nlpts[rund(c[HARDGAME]?13:15)])
+/* macro to return the + points on chain armor */
+#define newchain() (nch[rund(10)])
+/* macro to return + points on plate armor */
+#define newplate() (nplt[rund(c[HARDGAME]?4:12)])
+/* macro to return + points on new daggers */
+#define newdagger() (ndgg[rund(13)])
+/* macro to return + points on new swords */
+#define newsword() (nsw[rund(c[HARDGAME]?6:13)])
+/* macro to destroy object at present location */
+#define forget() (item[playerx][playery]=know[playerx][playery]=0)
+/* macro to wipe out a monster at a location */
+#define disappear(x,y) (mitem[x][y]=know[x][y]=0)
+
+#ifdef VT100
+/* macro to turn on bold display for the terminal */
+#define setbold() (lprcat(boldon?"\33[1m":"\33[7m"))
+/* macro to turn off bold display for the terminal */
+#define resetbold() (lprcat("\33[m"))
+/* macro to setup the scrolling region for the terminal */
+#define setscroll() (lprcat("\33[20;24r"))
+/* macro to clear the scrolling region for the terminal */
+#define resetscroll() (lprcat("\33[;24r"))
+/* macro to clear the screen and home the cursor */
+#define clear() (lprcat("\33[2J\33[f"), cbak[SPELLS]= -50)
+#define cltoeoln() lprcat("\33[K")
+#else /* VT100 */
+/* defines below are for use in the termcap mode only */
+#define ST_START 1
+#define ST_END 2
+#define BOLD 3
+#define END_BOLD 4
+#define CLEAR 5
+#define CL_LINE 6
+#define CL_DOWN 14
+#define CURSOR 15
+/* macro to turn on bold display for the terminal */
+#define setbold() (*lpnt++ = ST_START)
+/* macro to turn off bold display for the terminal */
+#define resetbold() (*lpnt++ = ST_END)
+/* macro to setup the scrolling region for the terminal */
+#define setscroll() enable_scroll=1
+/* macro to clear the scrolling region for the terminal */
+#define resetscroll() enable_scroll=0
+/* macro to clear the screen and home the cursor */
+#define clear() (*lpnt++ =CLEAR, cbak[SPELLS]= -50)
+/* macro to clear to end of line */
+#define cltoeoln() (*lpnt++ = CL_LINE)
+#endif /* VT100 */
+
+/* macro to output one byte to the output buffer */
+#define lprc(ch) ((lpnt>=lpend)?(void)(*lpnt++ = (ch), lflush()):(void)(*lpnt++ = (ch)))
+
+/* macro to seed the random number generator */
+#define seedrand(x) (randx=x)
+#ifdef MACRORND
+/* macros to generate random numbers 1<=rnd(N)<=N 0<=rund(N)<=N-1 */
+#define rnd(x) ((((randx=randx*1103515245+12345)>>7)%(x))+1)
+#define rund(x) ((((randx=randx*1103515245+12345)>>7)%(x)) )
+#endif /* MACRORND */
+/* macros for miscellaneous data conversion */
+#define min(x,y) (((x)>(y))?(y):(x))
+#define max(x,y) (((x)>(y))?(x):(y))
+#endif /* NODEFS */
diff --git a/larn/help.c b/larn/help.c
new file mode 100644
index 0000000..477052c
--- /dev/null
+++ b/larn/help.c
@@ -0,0 +1,129 @@
+/* $NetBSD: help.c,v 1.9 2012/06/19 05:30:43 dholland Exp $ */
+
+/* help.c Larn is copyrighted 1986 by Noah Morgan. */
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: help.c,v 1.9 2012/06/19 05:30:43 dholland Exp $");
+#endif /* not lint */
+
+#include <unistd.h>
+
+#include "header.h"
+#include "extern.h"
+
+static void retcont(void);
+static int openhelp(void);
+
+/*
+ * help function to display the help info
+ *
+ * format of the .larn.help file
+ *
+ * 1st character of file: # of pages of help available (ascii digit)
+ * page (23 lines) for the introductory message (not counted in above)
+ * pages of help text (23 lines per page)
+ */
+void
+help(void)
+{
+ int i, j;
+#ifndef VT100
+ char tmbuf[128]; /* intermediate translation buffer
+ * when not a VT100 */
+#endif /* VT100 */
+ if ((j = openhelp()) < 0)
+ return; /* open the help file and get # pages */
+ for (i = 0; i < 23; i++)
+ lgetl(); /* skip over intro message */
+ for (; j > 0; j--) {
+ clear();
+ for (i = 0; i < 23; i++)
+#ifdef VT100
+ lprcat(lgetl()); /* print out each line that
+ * we read in */
+#else /* VT100 */
+ {
+ tmcapcnv(tmbuf, lgetl());
+ lprcat(tmbuf);
+ } /* intercept \33's */
+#endif /* VT100 */
+ if (j > 1) {
+ lprcat(" ---- Press ");
+ standout("return");
+ lprcat(" to exit, ");
+ standout("space");
+ lprcat(" for more help ---- ");
+ i = 0;
+ while ((i != ' ') && (i != '\n') && (i != '\33'))
+ i = ttgetch();
+ if ((i == '\n') || (i == '\33')) {
+ lrclose();
+ setscroll();
+ drawscreen();
+ return;
+ }
+ }
+ }
+ lrclose();
+ retcont();
+ drawscreen();
+}
+
+/*
+ * function to display the welcome message and background
+ */
+void
+welcome(void)
+{
+ int i;
+#ifndef VT100
+ char tmbuf[128]; /* intermediate translation buffer
+ * when not a VT100 */
+#endif /* VT100 */
+ if (openhelp() < 0)
+ return; /* open the help file */
+ clear();
+ for (i = 0; i < 23; i++)
+#ifdef VT100
+ lprcat(lgetl());/* print out each line that we read in */
+#else /* VT100 */
+ {
+ tmcapcnv(tmbuf, lgetl());
+ lprcat(tmbuf);
+ } /* intercept \33's */
+#endif /* VT100 */
+ lrclose();
+ retcont(); /* press return to continue */
+}
+
+/*
+ * function to say press return to continue and reset scroll when done
+ */
+static void
+retcont(void)
+{
+ cursor(1, 24);
+ lprcat("Press ");
+ standout("return");
+ lprcat(" to continue: ");
+ while (ttgetch() != '\n');
+ setscroll();
+}
+
+/*
+ * routine to open the help file and return the first character - '0'
+ */
+static int
+openhelp(void)
+{
+ if (lopen(helpfile) < 0) {
+ lprintf("Can't open help file \"%s\" ", helpfile);
+ lflush();
+ sleep(4);
+ drawscreen();
+ setscroll();
+ return (-1);
+ }
+ resetscroll();
+ return (lgetc() - '0');
+}
diff --git a/larn/io.c b/larn/io.c
new file mode 100644
index 0000000..245ce8a
--- /dev/null
+++ b/larn/io.c
@@ -0,0 +1,991 @@
+/* $NetBSD: io.c,v 1.27 2012/06/19 05:30:43 dholland Exp $ */
+
+/*
+ * io.c Larn is copyrighted 1986 by Noah Morgan.
+ *
+ * Below are the functions in this file:
+ *
+ * setupvt100() Subroutine to set up terminal in correct mode for game
+ * clearvt100() Subroutine to clean up terminal when the game is over
+ * ttgetch() Routine to read in one character from the terminal
+ * scbr() Function to set cbreak -echo for the terminal
+ * sncbr() Function to set -cbreak echo for the terminal
+ * newgame() Subroutine to save the initial time and seed rnd()
+ *
+ * FILE OUTPUT ROUTINES
+ *
+ * lprintf(format,args . . .) printf to the output buffer lprint(integer)
+ * end binary integer to output buffer lwrite(buf,len)
+ * rite a buffer to the output buffer lprcat(str)
+ * ent string to output buffer
+ *
+ * FILE OUTPUT MACROS (in header.h)
+ *
+ * lprc(character) put the character into the output
+ * buffer
+ *
+ * FILE INPUT ROUTINES
+ *
+ * long lgetc() read one character from input buffer
+ * long larn_lrint() read one integer from input buffer
+ * lrfill(address,number) put input bytes into a buffer char
+ * *lgetw() get a whitespace ended word from
+ * input char *lgetl() get a \n or EOF ended line
+ * from input
+ *
+ * FILE OPEN / CLOSE ROUTINES
+ *
+ * lcreat(filename) create a new file for write
+ * lopen(filename) open a file for read
+ * lappend(filename) open for append to an existing file
+ * lrclose() close the input file
+ * lwclose() close output file lflush()
+ * lush the output buffer
+ *
+ * Other Routines
+ *
+ * cursor(x,y) position cursor at [x,y]
+ * cursors() position cursor at [1,24]
+ * (saves memory) cl_line(x,y) Clear line at [1,y] and leave
+ * cursor at [x,y] cl_up(x,y) Clear screen
+ * from [x,1] to current line. cl_dn(x,y)
+ * lear screen from [1,y] to end of display. standout(str)
+ * rint the string in standout mode. set_score_output()
+ * alled when output should be literally printed. * ttputch(ch)
+ * rint one character in decoded output buffer. * flush_buf()
+ * lush buffer with decoded output. * init_term()
+ * erminal initialization -- setup termcap info * char *tmcapcnv(sd,ss)
+ * outine to convert VT100 \33's to termcap format beep()
+ * e to emit a beep if enabled (see no-beep in .larnopts)
+ *
+ * Note: ** entries are available only in termcap mode.
+ */
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: io.c,v 1.27 2012/06/19 05:30:43 dholland Exp $");
+#endif /* not lint */
+
+#include "header.h"
+#include "extern.h"
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <term.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <ctype.h>
+
+#ifdef TERMIO
+#include <termio.h>
+#define sgttyb termio
+#define stty(_a,_b) ioctl(_a,TCSETA,_b)
+#define gtty(_a,_b) ioctl(_a,TCGETA,_b)
+#endif
+#ifdef TERMIOS
+#include <termios.h>
+#define sgttyb termios
+#define stty(_a,_b) tcsetattr(_a,TCSADRAIN,_b)
+#define gtty(_a,_b) tcgetattr(_a,_b)
+#endif
+
+#if defined(TERMIO) || defined(TERMIOS)
+static int rawflg = 0;
+static char saveeof, saveeol;
+#define doraw(_a) \
+ if(!rawflg) { \
+ ++rawflg; \
+ saveeof = _a.c_cc[VMIN]; \
+ saveeol = _a.c_cc[VTIME]; \
+ } \
+ _a.c_cc[VMIN] = 1; \
+ _a.c_cc[VTIME] = 1; \
+ _a.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL)
+#define unraw(_a) \
+ _a.c_cc[VMIN] = saveeof; \
+ _a.c_cc[VTIME] = saveeol; \
+ _a.c_lflag |= ICANON|ECHO|ECHOE|ECHOK|ECHONL
+
+#else /* not TERMIO or TERMIOS */
+
+#ifndef BSD
+#define CBREAK RAW /* V7 has no CBREAK */
+#endif
+
+#define doraw(_a) (_a.sg_flags |= CBREAK,_a.sg_flags &= ~ECHO)
+#define unraw(_a) (_a.sg_flags &= ~CBREAK,_a.sg_flags |= ECHO)
+#include <sgtty.h>
+#endif /* not TERMIO or TERMIOS */
+
+#ifndef NOVARARGS /* if we have varargs */
+#include <stdarg.h>
+#else /* NOVARARGS */ /* if we don't have varargs */
+typedef char *va_list;
+#define va_dcl int va_alist;
+#define va_start(plist) plist = (char *) &va_alist
+#define va_end(plist)
+#define va_arg(plist,mode) ((mode *)(plist += sizeof(mode)))[-1]
+#endif /* NOVARARGS */
+
+static int ttputch(int ch);
+static void flush_buf(void);
+
+#define LINBUFSIZE 128 /* size of the lgetw() and lgetl() buffer */
+int io_outfd; /* output file numbers */
+int io_infd; /* input file numbers */
+static struct sgttyb ttx;/* storage for the tty modes */
+static int ipoint = MAXIBUF, iepoint = MAXIBUF; /* input buffering
+ * pointers */
+static char lgetwbuf[LINBUFSIZE]; /* get line (word) buffer */
+
+/*
+ * setupvt100() Subroutine to set up terminal in correct mode for game
+ *
+ * Attributes off, clear screen, set scrolling region, set tty mode
+ */
+void
+setupvt100(void)
+{
+ clear();
+ setscroll();
+ scbr(); /* system("stty cbreak -echo"); */
+}
+
+/*
+ * clearvt100() Subroutine to clean up terminal when the game is over
+ *
+ * Attributes off, clear screen, unset scrolling region, restore tty mode
+ */
+void
+clearvt100(void)
+{
+ resetscroll();
+ clear();
+ sncbr(); /* system("stty -cbreak echo"); */
+}
+
+/*
+ * ttgetch() Routine to read in one character from the terminal
+ */
+int
+ttgetch(void)
+{
+ char byt;
+#ifdef EXTRA
+ c[BYTESIN]++;
+#endif
+ lflush(); /* be sure output buffer is flushed */
+ read(0, &byt, 1); /* get byte from terminal */
+ return (byt);
+}
+
+/*
+ * scbr() Function to set cbreak -echo for the terminal
+ *
+ * like: system("stty cbreak -echo")
+ */
+void
+scbr(void)
+{
+ gtty(0, &ttx);
+ doraw(ttx);
+ stty(0, &ttx);
+}
+
+/*
+ * sncbr() Function to set -cbreak echo for the terminal
+ *
+ * like: system("stty -cbreak echo")
+ */
+void
+sncbr(void)
+{
+ gtty(0, &ttx);
+ unraw(ttx);
+ stty(0, &ttx);
+}
+
+/*
+ * newgame() Subroutine to save the initial time and seed rnd()
+ */
+void
+newgame(void)
+{
+ long *p, *pe;
+ for (p = c, pe = c + 100; p < pe; *p++ = 0);
+ time(&initialtime);
+ seedrand(initialtime);
+ srandom(initialtime);
+ lcreat((char *) 0); /* open buffering for output to terminal */
+}
+
+/*
+ * lprintf(format,args . . .) printf to the output buffer
+ * char *format;
+ * ??? args . . .
+ *
+ * Enter with the format string in "format", as per printf() usage
+ * and any needed arguments following it
+ * Note: lprintf() only supports %s, %c and %d, with width modifier and left
+ * or right justification.
+ * No correct checking for output buffer overflow is done, but flushes
+ * are done beforehand if needed.
+ * Returns nothing of value.
+ */
+void
+lprintf(const char *fmt, ...)
+{
+ va_list ap;
+ char buf[BUFBIG/2];
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ if (lpnt >= lpend)
+ lflush();
+
+ lprcat(buf);
+}
+
+/*
+ * lprint(long-integer) send binary integer to output buffer
+ * long integer;
+ *
+ * +---------+---------+---------+---------+
+ * | high | | | low |
+ * | order | | | order |
+ * | byte | | | byte |
+ * +---------+---------+---------+---------+
+ * 31 --- 24 23 --- 16 15 --- 8 7 --- 0
+ *
+ * The save order is low order first, to high order (4 bytes total)
+ * and is written to be system independent.
+ * No checking for output buffer overflow is done, but flushes if needed!
+ * Returns nothing of value.
+ */
+void
+lprint(long x)
+{
+ if (lpnt >= lpend)
+ lflush();
+ *lpnt++ = 255 & x;
+ *lpnt++ = 255 & (x >> 8);
+ *lpnt++ = 255 & (x >> 16);
+ *lpnt++ = 255 & (x >> 24);
+}
+
+/*
+ * lwrite(buf,len) write a buffer to the output buffer
+ * char *buf;
+ * int len;
+ *
+ * Enter with the address and number of bytes to write out
+ * Returns nothing of value
+ */
+void
+lwrite(char *buf, int len)
+{
+ char *s;
+ u_char *t;
+ int num2;
+
+ if (len > 399) { /* don't copy data if can just write it */
+#ifdef EXTRA
+ c[BYTESOUT] += len;
+#endif
+
+#ifndef VT100
+ for (s = buf; len > 0; --len)
+ lprc(*s++);
+#else /* VT100 */
+ lflush();
+ write(io_outfd, buf, len);
+#endif /* VT100 */
+ } else
+ while (len) {
+ if (lpnt >= lpend)
+ lflush(); /* if buffer is full flush it */
+ num2 = lpbuf + BUFBIG - lpnt; /* # bytes left in
+ * output buffer */
+ if (num2 > len)
+ num2 = len;
+ t = lpnt;
+ len -= num2;
+ while (num2--)
+ *t++ = *buf++; /* copy in the bytes */
+ lpnt = t;
+ }
+}
+
+/*
+ * long lgetc() Read one character from input buffer
+ *
+ * Returns 0 if EOF, otherwise the character
+ */
+long
+lgetc(void)
+{
+ int i;
+ if (ipoint != iepoint)
+ return (inbuffer[ipoint++]);
+ if (iepoint != MAXIBUF)
+ return (0);
+ if ((i = read(io_infd, inbuffer, MAXIBUF)) <= 0) {
+ if (i != 0)
+ write(1, "error reading from input file\n", 30);
+ iepoint = ipoint = 0;
+ return (0);
+ }
+ ipoint = 1;
+ iepoint = i;
+ return (*inbuffer);
+}
+
+/*
+ * long lrint() Read one integer from input buffer
+ *
+ * +---------+---------+---------+---------+
+ * | high | | | low |
+ * | order | | | order |
+ * | byte | | | byte |
+ * +---------+---------+---------+---------+
+ * 31 --- 24 23 --- 16 15 --- 8 7 --- 0
+ *
+ * The save order is low order first, to high order (4 bytes total)
+ * Returns the int read
+ */
+long
+larn_lrint(void)
+{
+ unsigned long i;
+ i = 255 & lgetc();
+ i |= (255 & lgetc()) << 8;
+ i |= (255 & lgetc()) << 16;
+ i |= (255 & lgetc()) << 24;
+ return (i);
+}
+
+/*
+ * lrfill(address,number) put input bytes into a buffer
+ * char *address;
+ * int number;
+ *
+ * Reads "number" bytes into the buffer pointed to by "address".
+ * Returns nothing of value
+ */
+void
+lrfill(char *adr, int num)
+{
+ u_char *pnt;
+ int num2;
+
+ while (num) {
+ if (iepoint == ipoint) {
+ if (num > 5) { /* fast way */
+ if (read(io_infd, adr, num) != num)
+ write(2, "error reading from input file\n", 30);
+ num = 0;
+ } else {
+ *adr++ = lgetc();
+ --num;
+ }
+ } else {
+ num2 = iepoint - ipoint; /* # of bytes left in
+ * the buffer */
+ if (num2 > num)
+ num2 = num;
+ pnt = inbuffer + ipoint;
+ num -= num2;
+ ipoint += num2;
+ while (num2--)
+ *adr++ = *pnt++;
+ }
+ }
+}
+
+/*
+ * char *lgetw() Get a whitespace ended word from input
+ *
+ * Returns pointer to a buffer that contains word. If EOF, returns a NULL
+ */
+char *
+lgetw(void)
+{
+ char *lgp, cc;
+ int n = LINBUFSIZE, quote = 0;
+ lgp = lgetwbuf;
+ do
+ cc = lgetc();
+ while ((cc <= 32) && (cc > '\0')); /* eat whitespace */
+ for (;; --n, cc = lgetc()) {
+ if ((cc == '\0') && (lgp == lgetwbuf))
+ return (NULL); /* EOF */
+ if ((n <= 1) || ((cc <= 32) && (quote == 0))) {
+ *lgp = '\0';
+ return (lgetwbuf);
+ }
+ if (cc != '"')
+ *lgp++ = cc;
+ else
+ quote ^= 1;
+ }
+}
+
+/*
+ * char *lgetl() Function to read in a line ended by newline or EOF
+ *
+ * Returns pointer to a buffer that contains the line. If EOF, returns NULL
+ */
+char *
+lgetl(void)
+{
+ int i = LINBUFSIZE, ch;
+ char *str = lgetwbuf;
+ for (;; --i) {
+ if ((*str++ = ch = lgetc()) == '\0') {
+ if (str == lgetwbuf + 1)
+ return (NULL); /* EOF */
+ ot: *str = '\0';
+ return (lgetwbuf); /* line ended by EOF */
+ }
+ if ((ch == '\n') || (i <= 1))
+ goto ot;/* line ended by \n */
+ }
+}
+
+/*
+ * lcreat(filename) Create a new file for write
+ * char *filename;
+ *
+ * lcreat((char*)0); means to the terminal
+ * Returns -1 if error, otherwise the file descriptor opened.
+ */
+int
+lcreat(char *str)
+{
+ lflush();
+ lpnt = lpbuf;
+ lpend = lpbuf + BUFBIG;
+ if (str == NULL)
+ return (io_outfd = 1);
+ if ((io_outfd = creat(str, 0644)) < 0) {
+ io_outfd = 1;
+ lprintf("error creating file <%s>: %s\n", str,
+ strerror(errno));
+ lflush();
+ return (-1);
+ }
+ return (io_outfd);
+}
+
+/*
+ * lopen(filename) Open a file for read
+ * char *filename;
+ *
+ * lopen(0) means from the terminal
+ * Returns -1 if error, otherwise the file descriptor opened.
+ */
+int
+lopen(char *str)
+{
+ ipoint = iepoint = MAXIBUF;
+ if (str == NULL)
+ return (io_infd = 0);
+ if ((io_infd = open(str, O_RDONLY)) < 0) {
+ lwclose();
+ io_outfd = 1;
+ lpnt = lpbuf;
+ return (-1);
+ }
+ return (io_infd);
+}
+
+/*
+ * lappend(filename) Open for append to an existing file
+ * char *filename;
+ *
+ * lappend(0) means to the terminal
+ * Returns -1 if error, otherwise the file descriptor opened.
+ */
+int
+lappend(char *str)
+{
+ lpnt = lpbuf;
+ lpend = lpbuf + BUFBIG;
+ if (str == NULL)
+ return (io_outfd = 1);
+ if ((io_outfd = open(str, 2)) < 0) {
+ io_outfd = 1;
+ return (-1);
+ }
+ lseek(io_outfd, 0, SEEK_END); /* seek to end of file */
+ return (io_outfd);
+}
+
+/*
+ * lrclose() close the input file
+ *
+ * Returns nothing of value.
+ */
+void
+lrclose(void)
+{
+ if (io_infd > 0) {
+ close(io_infd);
+ io_infd = 0;
+ }
+}
+
+/*
+ * lwclose() close output file flushing if needed
+ *
+ * Returns nothing of value.
+ */
+void
+lwclose(void)
+{
+ lflush();
+ if (io_outfd > 2) {
+ close(io_outfd);
+ io_outfd = 1;
+ }
+}
+
+/*
+ * lprcat(string) append a string to the output buffer
+ * avoids calls to lprintf (time consuming)
+ */
+void
+lprcat(const char *str)
+{
+ u_char *str2;
+ if (lpnt >= lpend)
+ lflush();
+ str2 = lpnt;
+ while ((*str2++ = *str++) != '\0')
+ continue;
+ lpnt = str2 - 1;
+}
+
+#ifdef VT100
+/*
+ * cursor(x,y) Subroutine to set the cursor position
+ *
+ * x and y are the cursor coordinates, and lpbuff is the output buffer where
+ * escape sequence will be placed.
+ */
+static char *y_num[] = {
+"\33[", "\33[", "\33[2", "\33[3", "\33[4", "\33[5", "\33[6",
+"\33[7", "\33[8", "\33[9", "\33[10", "\33[11", "\33[12", "\33[13", "\33[14",
+"\33[15", "\33[16", "\33[17", "\33[18", "\33[19", "\33[20", "\33[21", "\33[22",
+"\33[23", "\33[24"};
+
+static char *x_num[] = {
+"H", "H", ";2H", ";3H", ";4H", ";5H", ";6H", ";7H", ";8H", ";9H",
+";10H", ";11H", ";12H", ";13H", ";14H", ";15H", ";16H", ";17H", ";18H", ";19H",
+";20H", ";21H", ";22H", ";23H", ";24H", ";25H", ";26H", ";27H", ";28H", ";29H",
+";30H", ";31H", ";32H", ";33H", ";34H", ";35H", ";36H", ";37H", ";38H", ";39H",
+";40H", ";41H", ";42H", ";43H", ";44H", ";45H", ";46H", ";47H", ";48H", ";49H",
+";50H", ";51H", ";52H", ";53H", ";54H", ";55H", ";56H", ";57H", ";58H", ";59H",
+";60H", ";61H", ";62H", ";63H", ";64H", ";65H", ";66H", ";67H", ";68H", ";69H",
+";70H", ";71H", ";72H", ";73H", ";74H", ";75H", ";76H", ";77H", ";78H", ";79H",
+";80H"};
+
+void
+cursor(x, y)
+ int x, y;
+{
+ char *p;
+ if (lpnt >= lpend)
+ lflush();
+
+ p = y_num[y]; /* get the string to print */
+ while (*p)
+ *lpnt++ = *p++; /* print the string */
+
+ p = x_num[x]; /* get the string to print */
+ while (*p)
+ *lpnt++ = *p++; /* print the string */
+}
+#else /* VT100 */
+/*
+ * cursor(x,y) Put cursor at specified coordinates staring at [1,1] (termcap)
+ */
+void
+cursor(int x, int y)
+{
+ if (lpnt >= lpend)
+ lflush();
+
+ *lpnt++ = CURSOR;
+ *lpnt++ = x;
+ *lpnt++ = y;
+}
+#endif /* VT100 */
+
+/*
+ * Routine to position cursor at beginning of 24th line
+ */
+void
+cursors(void)
+{
+ cursor(1, 24);
+}
+
+#ifndef VT100
+/*
+ * Warning: ringing the bell is control code 7. Don't use in defines.
+ * Don't change the order of these defines.
+ * Also used in helpfiles. Codes used in helpfiles should be \E[1 to \E[7 with
+ * obvious meanings.
+ */
+
+static char *outbuf = 0; /* translated output buffer */
+/*
+ * init_term() Terminal initialization -- setup termcap info
+ */
+void
+init_term(void)
+{
+ setupterm(NULL, 0, NULL); /* will exit if invalid term */
+ if (!cursor_address) {
+ fprintf(stderr, "term does not have cursor_address.\n");
+ exit(1);
+ }
+ if (!clr_eol) {
+ fprintf(stderr, "term does not have clr_eol.\n");
+ exit(1);
+ }
+ if (!clear_screen) {
+ fprintf(stderr, "term does not have clear_screen.\n");
+ exit(1);
+ }
+ if ((outbuf = malloc(BUFBIG + 16)) == 0) { /* get memory for
+ * decoded output buffer */
+ fprintf(stderr, "Error malloc'ing memory for decoded output buffer\n");
+ died(-285); /* malloc() failure */
+ }
+
+}
+#endif /* VT100 */
+
+/*
+ * cl_line(x,y) Clear the whole line indicated by 'y' and leave cursor at [x,y]
+ */
+void
+cl_line(int x, int y)
+{
+#ifdef VT100
+ cursor(x, y);
+ lprcat("\33[2K");
+#else /* VT100 */
+ cursor(1, y);
+ *lpnt++ = CL_LINE;
+ cursor(x, y);
+#endif /* VT100 */
+}
+
+/*
+ * cl_up(x,y) Clear screen from [x,1] to current position. Leave cursor at [x,y]
+ */
+void
+cl_up(int x, int y)
+{
+#ifdef VT100
+ cursor(x, y);
+ lprcat("\33[1J\33[2K");
+#else /* VT100 */
+ int i;
+ cursor(1, 1);
+ for (i = 1; i <= y; i++) {
+ *lpnt++ = CL_LINE;
+ *lpnt++ = '\n';
+ }
+ cursor(x, y);
+#endif /* VT100 */
+}
+
+/*
+ * cl_dn(x,y) Clear screen from [1,y] to end of display. Leave cursor at [x,y]
+ */
+void
+cl_dn(int x, int y)
+{
+#ifdef VT100
+ cursor(x, y);
+ lprcat("\33[J\33[2K");
+#else /* VT100 */
+ int i;
+ cursor(1, y);
+ if (!clr_eos) {
+ *lpnt++ = CL_LINE;
+ for (i = y; i <= 24; i++) {
+ *lpnt++ = CL_LINE;
+ if (i != 24)
+ *lpnt++ = '\n';
+ }
+ cursor(x, y);
+ } else
+ *lpnt++ = CL_DOWN;
+ cursor(x, y);
+#endif /* VT100 */
+}
+
+/*
+ * standout(str) Print the argument string in inverse video (standout mode).
+ */
+void
+standout(const char *str)
+{
+#ifdef VT100
+ setbold();
+ while (*str)
+ *lpnt++ = *str++;
+ resetbold();
+#else /* VT100 */
+ *lpnt++ = ST_START;
+ while (*str)
+ *lpnt++ = *str++;
+ *lpnt++ = ST_END;
+#endif /* VT100 */
+}
+
+/*
+ * set_score_output() Called when output should be literally printed.
+ */
+void
+set_score_output(void)
+{
+ enable_scroll = -1;
+}
+
+/*
+ * lflush() Flush the output buffer
+ *
+ * Returns nothing of value.
+ * for termcap version: Flush output in output buffer according to output
+ * status as indicated by `enable_scroll'
+ */
+#ifndef VT100
+static int scrline = 18; /* line # for wraparound instead of scrolling
+ * if no DL */
+void
+lflush(void)
+{
+ int lpoint;
+ u_char *str;
+ static int curx = 0;
+ static int cury = 0;
+
+ if ((lpoint = lpnt - lpbuf) > 0) {
+#ifdef EXTRA
+ c[BYTESOUT] += lpoint;
+#endif
+ if (enable_scroll <= -1) {
+ flush_buf();
+ if (write(io_outfd, lpbuf, lpoint) != lpoint)
+ write(2, "error writing to output file\n", 29);
+ lpnt = lpbuf; /* point back to beginning of buffer */
+ return;
+ }
+ for (str = lpbuf; str < lpnt; str++) {
+ if (*str >= 32) {
+ ttputch(*str);
+ curx++;
+ } else
+ switch (*str) {
+ case CLEAR:
+ tputs(clear_screen, 0, ttputch);
+ curx = cury = 0;
+ break;
+
+ case CL_LINE:
+ tputs(clr_eol, 0, ttputch);
+ break;
+
+ case CL_DOWN:
+ tputs(clr_eos, 0, ttputch);
+ break;
+
+ case ST_START:
+ tputs(enter_standout_mode, 0, ttputch);
+ break;
+
+ case ST_END:
+ tputs(exit_standout_mode, 0, ttputch);
+ break;
+
+ case CURSOR:
+ curx = *++str - 1;
+ cury = *++str - 1;
+ tputs(tiparm(cursor_address,
+ cury, curx), 0, ttputch);
+ break;
+
+ case '\n':
+ if ((cury == 23) && enable_scroll) {
+ if (!delete_line ||
+ !insert_line)
+ { /* wraparound or scroll? */
+ if (++scrline > 23)
+ scrline = 19;
+
+ if (++scrline > 23)
+ scrline = 19;
+ tputs(tiparm(
+ cursor_address,
+ scrline, 0),
+ 0, ttputch);
+ tputs(clr_eol, 0,
+ ttputch);
+
+ if (--scrline < 19)
+ scrline = 23;
+ tputs(tiparm(
+ cursor_address,
+ scrline, 0),
+ 0, ttputch);
+ tputs(clr_eol, 0,
+ ttputch);
+ } else {
+ tputs(tiparm(
+ cursor_address,
+ 19, 0),
+ 0, ttputch);
+ tputs(delete_line, 0,
+ ttputch);
+ tputs(tiparm(
+ cursor_address,
+ 23, 0),
+ 0, ttputch);
+ /*
+ * tputs (AL, 0,
+ * ttputch);
+ */
+ }
+ } else {
+ ttputch('\n');
+ cury++;
+ }
+ curx = 0;
+ break;
+
+ default:
+ ttputch(*str);
+ curx++;
+ };
+ }
+ }
+ lpnt = lpbuf;
+ flush_buf(); /* flush real output buffer now */
+}
+#else /* VT100 */
+/*
+ * lflush() flush the output buffer
+ *
+ * Returns nothing of value.
+ */
+void
+lflush()
+{
+ int lpoint;
+ if ((lpoint = lpnt - lpbuf) > 0) {
+#ifdef EXTRA
+ c[BYTESOUT] += lpoint;
+#endif
+ if (write(io_outfd, lpbuf, lpoint) != lpoint)
+ write(2, "error writing to output file\n", 29);
+ }
+ lpnt = lpbuf; /* point back to beginning of buffer */
+}
+#endif /* VT100 */
+
+#ifndef VT100
+static int vindex = 0;
+/*
+ * ttputch(ch) Print one character in decoded output buffer.
+ */
+static int
+ttputch(int ch)
+{
+ outbuf[vindex++] = ch;
+ if (vindex >= BUFBIG)
+ flush_buf();
+ return (0);
+}
+
+/*
+ * flush_buf() Flush buffer with decoded output.
+ */
+static void
+flush_buf(void)
+{
+ if (vindex)
+ write(io_outfd, outbuf, vindex);
+ vindex = 0;
+}
+
+/*
+ * char *tmcapcnv(sd,ss) Routine to convert VT100 escapes to termcap
+ * format
+ * Processes only the \33[#m sequence (converts . files for termcap use
+ */
+char *
+tmcapcnv(char *sd, char *ss)
+{
+ int tmstate = 0; /* 0=normal, 1=\33 2=[ 3=# */
+ char tmdigit = 0; /* the # in \33[#m */
+ while (*ss) {
+ switch (tmstate) {
+ case 0:
+ if (*ss == '\33') {
+ tmstate++;
+ break;
+ }
+ ign: *sd++ = *ss;
+ ign2: tmstate = 0;
+ break;
+ case 1:
+ if (*ss != '[')
+ goto ign;
+ tmstate++;
+ break;
+ case 2:
+ if (isdigit((u_char)*ss)) {
+ tmdigit = *ss - '0';
+ tmstate++;
+ break;
+ }
+ if (*ss == 'm') {
+ *sd++ = ST_END;
+ goto ign2;
+ }
+ goto ign;
+ case 3:
+ if (*ss == 'm') {
+ if (tmdigit)
+ *sd++ = ST_START;
+ else
+ *sd++ = ST_END;
+ goto ign2;
+ }
+ default:
+ goto ign;
+ };
+ ss++;
+ }
+ *sd = 0; /* NULL terminator */
+ return (sd);
+}
+#endif /* VT100 */
+
+/*
+ * beep() Routine to emit a beep if enabled (see no-beep in .larnopts)
+ */
+void
+beep(void)
+{
+ if (!nobeep)
+ *lpnt++ = '\7';
+}
diff --git a/larn/larn.6 b/larn/larn.6
new file mode 100644
index 0000000..7fadc6b
--- /dev/null
+++ b/larn/larn.6
@@ -0,0 +1,157 @@
+.\" $NetBSD: larn.6,v 1.14 2010/04/24 01:13:37 dholland Exp $
+.\"
+.\" Copyright (c) 1990 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)larn.6 5.5 (Berkeley) 12/30/93
+.\"
+.Dd April 23, 2010
+.Dt LARN 6
+.Os
+.Sh NAME
+.Nm larn
+.Nd exploring the caverns of Larn
+.Sh SYNOPSIS
+.Nm larn
+.Op Fl chilns
+.\".Op Fl H Ar number
+.Op Fl o Ar optsfile
+.Op Fl ##
+.Op ++
+.Sh DESCRIPTION
+.Nm
+is a fantasy games in which your child has contracted
+a strange disease, and none of your home remedies seem to have any effect.
+You set out to find a remedy in a limited
+amount of time, and to collect gold along the way of course!
+.Pp
+The options are:
+.Pp
+.Bl -tag -width flag
+.It Fl c
+Clear the high scores file.
+.\" .It Fl H
+.\" Set the difficulty (hardness) level.
+.It Fl h
+Show the command line options.
+.It Fl i
+Show the high scores, including inventory data.
+.It Fl l
+Show the log of all games.
+.It Fl n
+Suppress the welcome message and start the game immediately.
+.It Fl o
+.\" Ar optsfile
+Read the specified options file instead of
+.Pa ~/.larnopts .
+.It Fl s
+Show the high scores.
+.It Fl ##
+Set the difficulty (hardness) level.
+.It ++
+Restore game from the checkpoint (auto-save) file.
+.El
+.Sh COMMANDS
+These are the movement commands:
+.Bl -column " print program version" " give present pack weight"
+.It h move to the left H run left . stay here
+.It j move down J run down Z teleport yourself
+.It k move up K run up c cast a spell
+.It l move to the right L run right r read a scroll
+.It y move northwest Y run northwest q quaff a potion
+.It u move northeast U run northeast W wear armor
+.It b move southwest B run southwest T take off armor
+.It n move southeast N run southeast w wield a weapon
+.It ^ identify a trap g give present pack weight P give tax status
+.It d drop an item i inventory your pockets Q quit the game
+.It v print program version S save the game D list all items found
+.It ? this help screen A create diagnostic file e eat something
+.It (wizards only)
+.El
+.Sh OPTIONS FILE
+The file
+.Pa ~/.larnopts
+may be used to set a few options for
+.Nm .
+A sequence of words terminated by whitespace is used to specify options.
+.Pp
+.Bl -tag -width "savefile: xxsave-file-namexx" -compact
+.It Sy Word
+.Sy Meaning
+.Pp
+.It bold-objects
+Select bold display of objects.
+.It inverse-objects
+Select inverse video display of objects.
+.It no-introduction
+Do not display intro message.
+.It enable-checkpointing
+Turn on periodic checkpointing.
+.It no-beep
+Disable beeping of the terminal.
+.It male
+Choose your sex to be a man.
+.It female
+Choose your sex to be a woman.
+.It name: Dq your name
+Choose your playing name.
+.It monster: Dq monst name
+Choose a name for a monster.
+.It savefile: Dq save-file-name
+Define what the savegame filename will be.
+.El
+.Pp
+Your name and monster names must be enclosed in double quotation marks and may
+be up to 34 characters long.
+Longer names are truncated.
+Anything enclosed in quotation marks is considered one word, and must be
+separated from other words by whitespace.
+.Sh SPECIAL NOTES
+When
+.Sy dropping gold ,
+if you type '*' as your amount, all your gold gets dropped.
+In general, typing in '*' means all of what you are interested in.
+This is true when visiting the bank, or when contributing at altars.
+.Pp
+You can get out of the store, trading post, school, or home by hitting
+.Aq Sy esc .
+.Pp
+When casting a spell, if you need a list of spells you can cast, type
+.Ic D
+as the first letter of your spell.
+The available list of spells will be shown,
+after which you may enter the spell code.
+This only works on the 1st letter of the spell you are casting.
+.Sh FILES
+.Bl -tag -width "/var/games/larn.scores" -compact
+.It Pa /var/games/larn.scores
+Score file.
+.It Pa ~/.larnopts
+Options file.
+.El
+.Sh AUTHORS
+.An Noah Morgan
diff --git a/larn/main.c b/larn/main.c
new file mode 100644
index 0000000..4a5af8f
--- /dev/null
+++ b/larn/main.c
@@ -0,0 +1,1341 @@
+/* $NetBSD: main.c,v 1.25 2012/06/19 05:30:43 dholland Exp $ */
+
+/* main.c */
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: main.c,v 1.25 2012/06/19 05:30:43 dholland Exp $");
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <pwd.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include "header.h"
+#include "extern.h"
+
+static void showstr(void);
+static void t_setup(int);
+static void t_endup(int);
+static void showwear(void);
+static void showwield(void);
+static void showread(void);
+static void showeat(void);
+static void showquaff(void);
+static void show1(int, const char *[]);
+static void randmonst(void);
+static void parse(void);
+static void run(int);
+static void wield(void);
+static void ydhi(int);
+static void ycwi(int);
+static void wear(void);
+static void dropobj(void);
+static void readscr(void);
+static void eatcookie(void);
+static void quaff(void);
+static int whatitem(const char *);
+
+static char copyright[] = "\nLarn is copyrighted 1986 by Noah Morgan.\n";
+int srcount = 0; /* line counter for showstr() */
+int dropflag = 0; /* if 1 then don't lookforobject() next round */
+int rmst = 80; /* random monster creation counter */
+int userid; /* the players login user id number */
+gid_t gid, egid; /* used for security */
+u_char nowelcome = 0, nomove = 0; /* if (nomove) then don't
+ * count next iteration as a
+ * move */
+static char viewflag = 0;
+/*
+ * if viewflag then we have done a 99 stay here and don't showcell in the
+ * main loop
+ */
+u_char restorflag = 0; /* 1 means restore has been done */
+static char cmdhelp[] = "\
+Cmd line format: larn [-slicnh] [-o<optsfile>] [-##] [++]\n\
+ -s show the scoreboard\n\
+ -l show the logfile (wizard id only)\n\
+ -i show scoreboard with inventories of dead characters\n\
+ -c create new scoreboard (wizard id only)\n\
+ -n suppress welcome message on starting game\n\
+ -## specify level of difficulty (example: -5)\n\
+ -h print this help text\n\
+ ++ restore game from checkpoint file\n\
+ -o<optsfile> specify .larnopts filename to be used instead of \"~/.larnopts\"\n\
+";
+#ifdef VT100
+/*
+20150211 bkw: assume any term type is vt100-compatible
+static char *termtypes[] = {"vt100", "vt101", "vt102", "vt103", "vt125",
+ "vt131", "vt140", "vt180", "vt220", "vt240", "vt241", "vt320", "vt340",
+"vt341"};
+*/
+#endif /* VT100 */
+/*
+ ************
+ MAIN PROGRAM
+ ************
+ */
+int
+main(int argc, char **argv)
+{
+ int i;
+ int hard;
+ const char *ptr = 0;
+ struct passwd *pwe;
+
+ i = 0;
+ egid = getegid();
+ gid = getgid();
+ setegid(gid); /* give up "games" if we have it */
+ /*
+ * first task is to identify the player
+ */
+#ifndef VT100
+ init_term(); /* setup the terminal (find out what type)
+ * for termcap */
+#endif /* VT100 */
+ /* try to get login name */
+ if (((ptr = getlogin()) == 0) || (*ptr == 0)) {
+ /* can we get it from /etc/passwd? */
+ if ((pwe = getpwuid(getuid())) != NULL)
+ ptr = pwe->pw_name;
+ else if ((ptr = getenv("USER")) == 0)
+ if ((ptr = getenv("LOGNAME")) == 0) {
+ noone: write(2, "Can't find your logname. Who Are You?\n", 39);
+ exit(1);
+ }
+ }
+ if (ptr == 0)
+ goto noone;
+ if (strlen(ptr) == 0)
+ goto noone;
+ /*
+ * second task is to prepare the pathnames the player will need
+ */
+ strcpy(loginname, ptr); /* save loginname of the user for logging
+ * purposes */
+ strcpy(logname, ptr); /* this will be overwritten with the players
+ * name */
+ if ((ptr = getenv("HOME")) == NULL)
+ ptr = ".";
+ strcpy(savefilename, ptr);
+ strcat(savefilename, "/Larn.sav"); /* save file name in home
+ * directory */
+ snprintf(optsfile, sizeof(optsfile), "%s/.larnopts", ptr);
+ /* the .larnopts filename */
+
+ /*
+ * now malloc the memory for the dungeon
+ */
+ cell = (struct cel *) malloc(sizeof(struct cel) * (MAXLEVEL + MAXVLEVEL) * MAXX * MAXY);
+ if (cell == 0)
+ died(-285); /* malloc failure */
+ lpbuf = malloc((5 * BUFBIG) >> 2); /* output buffer */
+ inbuffer = malloc((5 * MAXIBUF) >> 2); /* output buffer */
+ if ((lpbuf == 0) || (inbuffer == 0))
+ died(-285); /* malloc() failure */
+
+ lcreat((char *) 0);
+ newgame(); /* set the initial clock */
+ hard = -1;
+
+#ifdef VT100
+ /*
+ * check terminal type to avoid users who have not vt100 type terminals
+ */
+ /*
+ // 20150211 bkw: assume any term type is vt100-compatible
+ ttype = getenv("TERM");
+ for (j = 1, i = 0; i < sizeof(termtypes) / sizeof(char *); i++)
+ if (strcmp(ttype, termtypes[i]) == 0) {
+ j = 0;
+ break;
+ }
+ if (j) {
+ lprcat("Sorry, Larn needs a VT100 family terminal for all its features.\n");
+ lflush();
+ exit(1);
+ }
+ */
+#endif /* VT100 */
+
+ /*
+ * now make scoreboard if it is not there (don't clear)
+ */
+ if (access(scorefile, 0) == -1) /* not there */
+ makeboard();
+
+ /*
+ * now process the command line arguments
+ */
+ for (i = 1; i < argc; i++) {
+ if (argv[i][0] == '-')
+ switch (argv[i][1]) {
+ case 's':
+ showscores();
+ exit(0); /* show scoreboard */
+
+ case 'l': /* show log file */
+ diedlog();
+ exit(0);
+
+ case 'i':
+ showallscores();
+ exit(0); /* show all scoreboard */
+
+ case 'c': /* anyone with password can create
+ * scoreboard */
+ lprcat("Preparing to initialize the scoreboard.\n");
+ if (getpassword() != 0) { /* make new scoreboard */
+ makeboard();
+ lprc('\n');
+ showscores();
+ }
+ exit(0);
+
+ case 'n': /* no welcome msg */
+ nowelcome = 1;
+ argv[i][0] = 0;
+ break;
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': /* for hardness */
+ sscanf(&argv[i][1], "%d", &hard);
+ break;
+
+ case 'h': /* print out command line arguments */
+ write(1, cmdhelp, sizeof(cmdhelp));
+ exit(0);
+
+ case 'o': /* specify a .larnopts filename */
+ strncpy(optsfile, argv[i] + 2, 127);
+ break;
+
+ default:
+ printf("Unknown option <%s>\n", argv[i]);
+ exit(1);
+ };
+
+ if (argv[i][0] == '+') {
+ clear();
+ restorflag = 1;
+ if (argv[i][1] == '+') {
+ hitflag = 1;
+ restoregame(ckpfile); /* restore checkpointed
+ * game */
+ }
+ i = argc;
+ }
+ }
+
+ readopts(); /* read the options file if there is one */
+
+
+#ifdef UIDSCORE
+ userid = geteuid(); /* obtain the user's effective id number */
+#else /* UIDSCORE */
+ userid = getplid(logname); /* obtain the players id number */
+#endif /* UIDSCORE */
+ if (userid < 0) {
+ write(2, "Can't obtain playerid\n", 22);
+ exit(1);
+ }
+#ifdef HIDEBYLINK
+ /*
+ * this section of code causes the program to look like something else to ps
+ */
+ if (strcmp(psname, argv[0])) { /* if a different process name only */
+ if ((i = access(psname, 1)) < 0) { /* link not there */
+ if (link(argv[0], psname) >= 0) {
+ argv[0] = psname;
+ execv(psname, argv);
+ }
+ } else
+ unlink(psname);
+ }
+ for (i = 1; i < argc; i++) {
+ szero(argv[i]); /* zero the argument to avoid ps snooping */
+ }
+#endif /* HIDEBYLINK */
+
+ if (access(savefilename, 0) == 0) { /* restore game if need to */
+ clear();
+ restorflag = 1;
+ hitflag = 1;
+ restoregame(savefilename); /* restore last game */
+ }
+ sigsetup(); /* trap all needed signals */
+ sethard(hard); /* set up the desired difficulty */
+ setupvt100(); /* setup the terminal special mode */
+ if (c[HP] == 0) { /* create new game */
+ makeplayer(); /* make the character that will play */
+ newcavelevel(0);/* make the dungeon */
+ predostuff = 1; /* tell signals that we are in the welcome
+ * screen */
+ if (nowelcome == 0)
+ welcome(); /* welcome the player to the game */
+ }
+ drawscreen(); /* show the initial dungeon */
+ predostuff = 2; /* tell the trap functions that they must do
+ * a showplayer() from here on */
+#if 0
+ nice(1); /* games should be run niced */
+#endif
+ yrepcount = hit2flag = 0;
+ while (1) {
+ if (dropflag == 0)
+ lookforobject(); /* see if there is an object
+ * here */
+ else
+ dropflag = 0; /* don't show it just dropped an item */
+ if (hitflag == 0) {
+ if (c[HASTEMONST])
+ movemonst();
+ movemonst();
+ } /* move the monsters */
+ if (viewflag == 0)
+ showcell(playerx, playery);
+ else
+ viewflag = 0; /* show stuff around player */
+ if (hit3flag)
+ flushall();
+ hitflag = hit3flag = 0;
+ nomove = 1;
+ bot_linex(); /* update bottom line */
+ while (nomove) {
+ if (hit3flag)
+ flushall();
+ nomove = 0;
+ parse();
+ } /* get commands and make moves */
+ regen(); /* regenerate hp and spells */
+ if (c[TIMESTOP] == 0)
+ if (--rmst <= 0) {
+ rmst = 120 - (level << 2);
+ fillmonst(makemonst(level));
+ }
+ }
+}
+
+
+/*
+ showstr()
+
+ show character's inventory
+ */
+static void
+showstr(void)
+{
+ int i, number;
+ for (number = 3, i = 0; i < 26; i++)
+ if (iven[i])
+ number++; /* count items in inventory */
+ t_setup(number);
+ qshowstr();
+ t_endup(number);
+}
+
+void
+qshowstr(void)
+{
+ int i, j, k, sigsav;
+ srcount = 0;
+ sigsav = nosignal;
+ nosignal = 1; /* don't allow ^c etc */
+ if (c[GOLD]) {
+ lprintf(".) %ld gold pieces", (long) c[GOLD]);
+ srcount++;
+ }
+ for (k = 26; k >= 0; k--)
+ if (iven[k]) {
+ for (i = 22; i < 84; i++)
+ for (j = 0; j <= k; j++)
+ if (i == iven[j])
+ show3(j);
+ k = 0;
+ }
+ lprintf("\nElapsed time is %ld. You have %ld mobuls left", (long) ((gltime + 99) / 100 + 1), (long) ((TIMELIMIT - gltime) / 100));
+ more();
+ nosignal = sigsav;
+}
+
+/*
+ * subroutine to clear screen depending on # lines to display
+ */
+static void
+t_setup(int count)
+{
+ if (count < 20) { /* how do we clear the screen? */
+ cl_up(79, count);
+ cursor(1, 1);
+ } else {
+ resetscroll();
+ clear();
+ }
+}
+
+/*
+ * subroutine to restore normal display screen depending on t_setup()
+ */
+static void
+t_endup(int count)
+{
+ if (count < 18) /* how did we clear the screen? */
+ draws(0, MAXX, 0, (count > MAXY) ? MAXY : count);
+ else {
+ drawscreen();
+ setscroll();
+ }
+}
+
+/*
+ function to show the things player is wearing only
+ */
+static void
+showwear(void)
+{
+ int i, j, sigsav, count;
+ sigsav = nosignal;
+ nosignal = 1; /* don't allow ^c etc */
+ srcount = 0;
+
+ for (count = 2, j = 0; j <= 26; j++) /* count number of items we
+ * will display */
+ if ((i = iven[j]) != 0)
+ switch (i) {
+ case OLEATHER:
+ case OPLATE:
+ case OCHAIN:
+ case ORING:
+ case OSTUDLEATHER:
+ case OSPLINT:
+ case OPLATEARMOR:
+ case OSSPLATE:
+ case OSHIELD:
+ count++;
+ };
+
+ t_setup(count);
+
+ for (i = 22; i < 84; i++)
+ for (j = 0; j <= 26; j++)
+ if (i == iven[j])
+ switch (i) {
+ case OLEATHER:
+ case OPLATE:
+ case OCHAIN:
+ case ORING:
+ case OSTUDLEATHER:
+ case OSPLINT:
+ case OPLATEARMOR:
+ case OSSPLATE:
+ case OSHIELD:
+ show3(j);
+ };
+ more();
+ nosignal = sigsav;
+ t_endup(count);
+}
+
+/*
+ function to show the things player can wield only
+ */
+static void
+showwield(void)
+{
+ int i, j, sigsav, count;
+ sigsav = nosignal;
+ nosignal = 1; /* don't allow ^c etc */
+ srcount = 0;
+
+ for (count = 2, j = 0; j <= 26; j++) /* count how many items */
+ if ((i = iven[j]) != 0)
+ switch (i) {
+ case ODIAMOND:
+ case ORUBY:
+ case OEMERALD:
+ case OSAPPHIRE:
+ case OBOOK:
+ case OCHEST:
+ case OLARNEYE:
+ case ONOTHEFT:
+ case OSPIRITSCARAB:
+ case OCUBEofUNDEAD:
+ case OPOTION:
+ case OSCROLL:
+ break;
+ default:
+ count++;
+ };
+
+ t_setup(count);
+
+ for (i = 22; i < 84; i++)
+ for (j = 0; j <= 26; j++)
+ if (i == iven[j])
+ switch (i) {
+ case ODIAMOND:
+ case ORUBY:
+ case OEMERALD:
+ case OSAPPHIRE:
+ case OBOOK:
+ case OCHEST:
+ case OLARNEYE:
+ case ONOTHEFT:
+ case OSPIRITSCARAB:
+ case OCUBEofUNDEAD:
+ case OPOTION:
+ case OSCROLL:
+ break;
+ default:
+ show3(j);
+ };
+ more();
+ nosignal = sigsav;
+ t_endup(count);
+}
+
+/*
+ * function to show the things player can read only
+ */
+static void
+showread(void)
+{
+ int i, j, sigsav, count;
+ sigsav = nosignal;
+ nosignal = 1; /* don't allow ^c etc */
+ srcount = 0;
+
+ for (count = 2, j = 0; j <= 26; j++)
+ switch (iven[j]) {
+ case OBOOK:
+ case OSCROLL:
+ count++;
+ };
+ t_setup(count);
+
+ for (i = 22; i < 84; i++)
+ for (j = 0; j <= 26; j++)
+ if (i == iven[j])
+ switch (i) {
+ case OBOOK:
+ case OSCROLL:
+ show3(j);
+ };
+ more();
+ nosignal = sigsav;
+ t_endup(count);
+}
+
+/*
+ * function to show the things player can eat only
+ */
+static void
+showeat(void)
+{
+ int i, j, sigsav, count;
+ sigsav = nosignal;
+ nosignal = 1; /* don't allow ^c etc */
+ srcount = 0;
+
+ for (count = 2, j = 0; j <= 26; j++)
+ switch (iven[j]) {
+ case OCOOKIE:
+ count++;
+ };
+ t_setup(count);
+
+ for (i = 22; i < 84; i++)
+ for (j = 0; j <= 26; j++)
+ if (i == iven[j])
+ switch (i) {
+ case OCOOKIE:
+ show3(j);
+ };
+ more();
+ nosignal = sigsav;
+ t_endup(count);
+}
+
+/*
+ function to show the things player can quaff only
+ */
+static void
+showquaff(void)
+{
+ int i, j, sigsav, count;
+ sigsav = nosignal;
+ nosignal = 1; /* don't allow ^c etc */
+ srcount = 0;
+
+ for (count = 2, j = 0; j <= 26; j++)
+ switch (iven[j]) {
+ case OPOTION:
+ count++;
+ };
+ t_setup(count);
+
+ for (i = 22; i < 84; i++)
+ for (j = 0; j <= 26; j++)
+ if (i == iven[j])
+ switch (i) {
+ case OPOTION:
+ show3(j);
+ };
+ more();
+ nosignal = sigsav;
+ t_endup(count);
+}
+
+static void
+show1(int idx, const char *str2[])
+{
+ lprintf("\n%c) %s", idx + 'a', objectname[iven[idx]]);
+ if (str2 != 0 && str2[ivenarg[idx]][0] != 0)
+ lprintf(" of%s", str2[ivenarg[idx]]);
+}
+
+void
+show3(int indx)
+{
+ switch (iven[indx]) {
+ case OPOTION:
+ show1(indx, potionname);
+ break;
+ case OSCROLL:
+ show1(indx, scrollname);
+ break;
+
+ case OLARNEYE:
+ case OBOOK:
+ case OSPIRITSCARAB:
+ case ODIAMOND:
+ case ORUBY:
+ case OCUBEofUNDEAD:
+ case OEMERALD:
+ case OCHEST:
+ case OCOOKIE:
+ case OSAPPHIRE:
+ case ONOTHEFT:
+ show1(indx, NULL);
+ break;
+
+ default:
+ lprintf("\n%c) %s", indx + 'a', objectname[iven[indx]]);
+ if (ivenarg[indx] > 0)
+ lprintf(" + %ld", (long) ivenarg[indx]);
+ else if (ivenarg[indx] < 0)
+ lprintf(" %ld", (long) ivenarg[indx]);
+ break;
+ }
+ if (c[WIELD] == indx)
+ lprcat(" (weapon in hand)");
+ if ((c[WEAR] == indx) || (c[SHIELD] == indx))
+ lprcat(" (being worn)");
+ if (++srcount >= 22) {
+ srcount = 0;
+ more();
+ clear();
+ }
+}
+
+/*
+ subroutine to randomly create monsters if needed
+ */
+static void
+randmonst(void)
+{
+ if (c[TIMESTOP])
+ return; /* don't make monsters if time is stopped */
+ if (--rmst <= 0) {
+ rmst = 120 - (level << 2);
+ fillmonst(makemonst(level));
+ }
+}
+
+
+
+/*
+ parse()
+
+ get and execute a command
+ */
+static void
+parse(void)
+{
+ int i, j, k, flag;
+ while (1) {
+ k = yylex();
+ switch (k) { /* get the token from the input and switch on
+ * it */
+ case 'h':
+ moveplayer(4);
+ return; /* west */
+ case 'H':
+ run(4);
+ return; /* west */
+ case 'l':
+ moveplayer(2);
+ return; /* east */
+ case 'L':
+ run(2);
+ return; /* east */
+ case 'j':
+ moveplayer(1);
+ return; /* south */
+ case 'J':
+ run(1);
+ return; /* south */
+ case 'k':
+ moveplayer(3);
+ return; /* north */
+ case 'K':
+ run(3);
+ return; /* north */
+ case 'u':
+ moveplayer(5);
+ return; /* northeast */
+ case 'U':
+ run(5);
+ return; /* northeast */
+ case 'y':
+ moveplayer(6);
+ return; /* northwest */
+ case 'Y':
+ run(6);
+ return; /* northwest */
+ case 'n':
+ moveplayer(7);
+ return; /* southeast */
+ case 'N':
+ run(7);
+ return; /* southeast */
+ case 'b':
+ moveplayer(8);
+ return; /* southwest */
+ case 'B':
+ run(8);
+ return; /* southwest */
+
+ case '.':
+ if (yrepcount)
+ viewflag = 1;
+ return; /* stay here */
+
+ case 'w':
+ yrepcount = 0;
+ wield();
+ return; /* wield a weapon */
+
+ case 'W':
+ yrepcount = 0;
+ wear();
+ return; /* wear armor */
+
+ case 'r':
+ yrepcount = 0;
+ if (c[BLINDCOUNT]) {
+ cursors();
+ lprcat("\nYou can't read anything when you're blind!");
+ } else if (c[TIMESTOP] == 0)
+ readscr();
+ return; /* to read a scroll */
+
+ case 'q':
+ yrepcount = 0;
+ if (c[TIMESTOP] == 0)
+ quaff();
+ return; /* quaff a potion */
+
+ case 'd':
+ yrepcount = 0;
+ if (c[TIMESTOP] == 0)
+ dropobj();
+ return; /* to drop an object */
+
+ case 'c':
+ yrepcount = 0;
+ cast();
+ return; /* cast a spell */
+
+ case 'i':
+ yrepcount = 0;
+ nomove = 1;
+ showstr();
+ return; /* status */
+
+ case 'e':
+ yrepcount = 0;
+ if (c[TIMESTOP] == 0)
+ eatcookie();
+ return; /* to eat a fortune cookie */
+
+ case 'D':
+ yrepcount = 0;
+ seemagic(0);
+ nomove = 1;
+ return; /* list spells and scrolls */
+
+ case '?':
+ yrepcount = 0;
+ help();
+ nomove = 1;
+ return; /* give the help screen */
+
+ case 'S':
+ clear();
+ lprcat("Saving . . .");
+ lflush();
+ savegame(savefilename);
+ wizard = 1;
+ died(-257); /* save the game - doesn't return */
+
+ case 'Z':
+ yrepcount = 0;
+ if (c[LEVEL] > 9) {
+ oteleport(1);
+ return;
+ }
+ cursors();
+ lprcat("\nAs yet, you don't have enough experience to use teleportation");
+ return; /* teleport yourself */
+
+ case '^': /* identify traps */
+ flag = yrepcount = 0;
+ cursors();
+ lprc('\n');
+ for (j = playery - 1; j < playery + 2; j++) {
+ if (j < 0)
+ j = 0;
+ if (j >= MAXY)
+ break;
+ for (i = playerx - 1; i < playerx + 2; i++) {
+ if (i < 0)
+ i = 0;
+ if (i >= MAXX)
+ break;
+ switch (item[i][j]) {
+ case OTRAPDOOR:
+ case ODARTRAP:
+ case OTRAPARROW:
+ case OTELEPORTER:
+ lprcat("\nIt's ");
+ lprcat(objectname[item[i][j]]);
+ flag++;
+ };
+ }
+ }
+ if (flag == 0)
+ lprcat("\nNo traps are visible");
+ return;
+
+#if WIZID
+ case '_': /* this is the fudge player password for
+ * wizard mode */
+ yrepcount = 0;
+ cursors();
+ nomove = 1;
+ if (userid != wisid) {
+ lprcat("Sorry, you are not empowered to be a wizard.\n");
+ scbr(); /* system("stty -echo cbreak"); */
+ lflush();
+ return;
+ }
+ if (getpassword() == 0) {
+ scbr(); /* system("stty -echo cbreak"); */
+ return;
+ }
+ wizard = 1;
+ scbr(); /* system("stty -echo cbreak"); */
+ for (i = 0; i < 6; i++)
+ c[i] = 70;
+ iven[0] = iven[1] = 0;
+ take(OPROTRING, 50);
+ take(OLANCE, 25);
+ c[WIELD] = 1;
+ c[LANCEDEATH] = 1;
+ c[WEAR] = c[SHIELD] = -1;
+ raiseexperience(6000000L);
+ c[AWARENESS] += 25000;
+ {
+ int i, j;
+ for (i = 0; i < MAXY; i++)
+ for (j = 0; j < MAXX; j++)
+ know[j][i] = 1;
+ for (i = 0; i < SPNUM; i++)
+ spelknow[i] = 1;
+ for (i = 0; i < MAXSCROLL; i++)
+ scrollname[i] = scrollhide[i];
+ for (i = 0; i < MAXPOTION; i++)
+ potionname[i] = potionhide[i];
+ }
+ for (i = 0; i < MAXSCROLL; i++)
+ if (strlen(scrollname[i]) > 2) { /* no null items */
+ item[i][0] = OSCROLL;
+ iarg[i][0] = i;
+ }
+ for (i = MAXX - 1; i > MAXX - 1 - MAXPOTION; i--)
+ if (strlen(potionname[i - MAXX + MAXPOTION]) > 2) { /* no null items */
+ item[i][0] = OPOTION;
+ iarg[i][0] = i - MAXX + MAXPOTION;
+ }
+ for (i = 1; i < MAXY; i++) {
+ item[0][i] = i;
+ iarg[0][i] = 0;
+ }
+ for (i = MAXY; i < MAXY + MAXX; i++) {
+ item[i - MAXY][MAXY - 1] = i;
+ iarg[i - MAXY][MAXY - 1] = 0;
+ }
+ for (i = MAXX + MAXY; i < MAXX + MAXY + MAXY; i++) {
+ item[MAXX - 1][i - MAXX - MAXY] = i;
+ iarg[MAXX - 1][i - MAXX - MAXY] = 0;
+ }
+ c[GOLD] += 25000;
+ drawscreen();
+ return;
+#endif
+
+ case 'T':
+ yrepcount = 0;
+ cursors();
+ if (c[SHIELD] != -1) {
+ c[SHIELD] = -1;
+ lprcat("\nYour shield is off");
+ bottomline();
+ } else if (c[WEAR] != -1) {
+ c[WEAR] = -1;
+ lprcat("\nYour armor is off");
+ bottomline();
+ } else
+ lprcat("\nYou aren't wearing anything");
+ return;
+
+ case 'g':
+ cursors();
+ lprintf("\nThe stuff you are carrying presently weighs %ld pounds", (long) packweight());
+ case ' ':
+ yrepcount = 0;
+ nomove = 1;
+ return;
+
+ case 'v':
+ yrepcount = 0;
+ cursors();
+ lprintf("\nCaverns of Larn, Version %ld.%ld, Diff=%ld",
+ (long) VERSION, (long) SUBVERSION,
+ (long) c[HARDGAME]);
+ if (wizard)
+ lprcat(" Wizard");
+ nomove = 1;
+ if (cheat)
+ lprcat(" Cheater");
+ lprcat(copyright);
+ return;
+
+ case 'Q':
+ yrepcount = 0;
+ quit();
+ nomove = 1;
+ return; /* quit */
+
+ case 'L' - 64:
+ yrepcount = 0;
+ drawscreen();
+ nomove = 1;
+ return; /* look */
+
+#if WIZID
+#ifdef EXTRA
+ case 'A':
+ yrepcount = 0;
+ nomove = 1;
+ if (wizard) {
+ diag();
+ return;
+ } /* create diagnostic file */
+ return;
+#endif
+#endif
+ case 'P':
+ cursors();
+ if (outstanding_taxes > 0)
+ lprintf("\nYou presently owe %ld gp in taxes.",
+ (long) outstanding_taxes);
+ else
+ lprcat("\nYou do not owe any taxes.");
+ return;
+ };
+ }
+}
+
+void
+parse2(void)
+{
+ if (c[HASTEMONST])
+ movemonst();
+ movemonst(); /* move the monsters */
+ randmonst();
+ regen();
+}
+
+static void
+run(int dir)
+{
+ int i;
+ i = 1;
+ while (i) {
+ i = moveplayer(dir);
+ if (i > 0) {
+ if (c[HASTEMONST])
+ movemonst();
+ movemonst();
+ randmonst();
+ regen();
+ }
+ if (hitflag)
+ i = 0;
+ if (i != 0)
+ showcell(playerx, playery);
+ }
+}
+
+/*
+ function to wield a weapon
+ */
+static void
+wield(void)
+{
+ int i;
+ while (1) {
+ if ((i = whatitem("wield")) == '\33')
+ return;
+ if (i != '.') {
+ if (i == '*')
+ showwield();
+ else if (iven[i - 'a'] == 0) {
+ ydhi(i);
+ return;
+ } else if (iven[i - 'a'] == OPOTION) {
+ ycwi(i);
+ return;
+ } else if (iven[i - 'a'] == OSCROLL) {
+ ycwi(i);
+ return;
+ } else if ((c[SHIELD] != -1) && (iven[i - 'a'] == O2SWORD)) {
+ lprcat("\nBut one arm is busy with your shield!");
+ return;
+ } else {
+ c[WIELD] = i - 'a';
+ if (iven[i - 'a'] == OLANCE)
+ c[LANCEDEATH] = 1;
+ else
+ c[LANCEDEATH] = 0;
+ bottomline();
+ return;
+ }
+ }
+ }
+}
+
+/*
+ common routine to say you don't have an item
+ */
+static void
+ydhi(int x)
+{
+ cursors();
+ lprintf("\nYou don't have item %c!", x);
+}
+static void
+ycwi(int x)
+{
+ cursors();
+ lprintf("\nYou can't wield item %c!", x);
+}
+
+/*
+ function to wear armor
+ */
+static void
+wear(void)
+{
+ int i;
+ while (1) {
+ if ((i = whatitem("wear")) == '\33')
+ return;
+ if (i != '.') {
+ if (i == '*')
+ showwear();
+ else
+ switch (iven[i - 'a']) {
+ case 0:
+ ydhi(i);
+ return;
+ case OLEATHER:
+ case OCHAIN:
+ case OPLATE:
+ case OSTUDLEATHER:
+ case ORING:
+ case OSPLINT:
+ case OPLATEARMOR:
+ case OSSPLATE:
+ if (c[WEAR] != -1) {
+ lprcat("\nYou're already wearing some armor");
+ return;
+ }
+ c[WEAR] = i - 'a';
+ bottomline();
+ return;
+ case OSHIELD:
+ if (c[SHIELD] != -1) {
+ lprcat("\nYou are already wearing a shield");
+ return;
+ }
+ if (iven[c[WIELD]] == O2SWORD) {
+ lprcat("\nYour hands are busy with the two handed sword!");
+ return;
+ }
+ c[SHIELD] = i - 'a';
+ bottomline();
+ return;
+ default:
+ lprcat("\nYou can't wear that!");
+ };
+ }
+ }
+}
+
+/*
+ function to drop an object
+ */
+static void
+dropobj(void)
+{
+ int i;
+ unsigned char *p;
+ long amt;
+ p = &item[playerx][playery];
+ while (1) {
+ if ((i = whatitem("drop")) == '\33')
+ return;
+ if (i == '*')
+ showstr();
+ else {
+ if (i == '.') { /* drop some gold */
+ if (*p) {
+ lprcat("\nThere's something here already!");
+ return;
+ }
+ lprcat("\n\n");
+ cl_dn(1, 23);
+ lprcat("How much gold do you drop? ");
+ if ((amt = readnum((long) c[GOLD])) == 0)
+ return;
+ if (amt > c[GOLD]) {
+ lprcat("\nYou don't have that much!");
+ return;
+ }
+ if (amt <= 32767) {
+ *p = OGOLDPILE;
+ i = amt;
+ } else if (amt <= 327670L) {
+ *p = ODGOLD;
+ i = amt / 10;
+ amt = 10 * i;
+ } else if (amt <= 3276700L) {
+ *p = OMAXGOLD;
+ i = amt / 100;
+ amt = 100 * i;
+ } else if (amt <= 32767000L) {
+ *p = OKGOLD;
+ i = amt / 1000;
+ amt = 1000 * i;
+ } else {
+ *p = OKGOLD;
+ i = 32767;
+ amt = 32767000L;
+ }
+ c[GOLD] -= amt;
+ lprintf("You drop %ld gold pieces", (long)amt);
+ iarg[playerx][playery] = i;
+ bottomgold();
+ know[playerx][playery] = 0;
+ dropflag = 1;
+ return;
+ }
+ drop_object(i - 'a');
+ return;
+ }
+ }
+}
+
+/*
+ * readscr() Subroutine to read a scroll one is carrying
+ */
+static void
+readscr(void)
+{
+ int i;
+ while (1) {
+ if ((i = whatitem("read")) == '\33')
+ return;
+ if (i != '.') {
+ if (i == '*')
+ showread();
+ else {
+ if (iven[i - 'a'] == OSCROLL) {
+ read_scroll(ivenarg[i - 'a']);
+ iven[i - 'a'] = 0;
+ return;
+ }
+ if (iven[i - 'a'] == OBOOK) {
+ readbook(ivenarg[i - 'a']);
+ iven[i - 'a'] = 0;
+ return;
+ }
+ if (iven[i - 'a'] == 0) {
+ ydhi(i);
+ return;
+ }
+ lprcat("\nThere's nothing on it to read");
+ return;
+ }
+ }
+ }
+}
+
+/*
+ * subroutine to eat a cookie one is carrying
+ */
+static void
+eatcookie(void)
+{
+ const char *p;
+ int i;
+
+ while (1) {
+ if ((i = whatitem("eat")) == '\33')
+ return;
+ if (i != '.') {
+ if (i == '*')
+ showeat();
+ else {
+ if (iven[i - 'a'] == OCOOKIE) {
+ lprcat("\nThe cookie was delicious.");
+ iven[i - 'a'] = 0;
+ if (!c[BLINDCOUNT]) {
+ if ((p = fortune()) != NULL) {
+ lprcat(" Inside you find a scrap of paper that says:\n");
+ lprcat(p);
+ }
+ }
+ return;
+ }
+ if (iven[i - 'a'] == 0) {
+ ydhi(i);
+ return;
+ }
+ lprcat("\nYou can't eat that!");
+ return;
+ }
+ }
+ }
+}
+
+/*
+ * subroutine to quaff a potion one is carrying
+ */
+static void
+quaff(void)
+{
+ int i;
+ while (1) {
+ if ((i = whatitem("quaff")) == '\33')
+ return;
+ if (i != '.') {
+ if (i == '*')
+ showquaff();
+ else {
+ if (iven[i - 'a'] == OPOTION) {
+ quaffpotion(ivenarg[i - 'a']);
+ iven[i - 'a'] = 0;
+ return;
+ }
+ if (iven[i - 'a'] == 0) {
+ ydhi(i);
+ return;
+ }
+ lprcat("\nYou wouldn't want to quaff that, would you? ");
+ return;
+ }
+ }
+ }
+}
+
+/*
+ function to ask what player wants to do
+ */
+static int
+whatitem(const char *str)
+{
+ int i;
+ cursors();
+ lprintf("\nWhat do you want to %s [* for all] ? ", str);
+ i = 0;
+ while (i > 'z' || (i < 'a' && i != '*' && i != '\33' && i != '.'))
+ i = ttgetch();
+ if (i == '\33')
+ lprcat(" aborted");
+ return (i);
+}
+
+/*
+ subroutine to get a number from the player
+ and allow * to mean return amt, else return the number entered
+ */
+unsigned long
+readnum(long mx)
+{
+ int i;
+ unsigned long amt = 0;
+ sncbr();
+ if ((i = ttgetch()) == '*')
+ amt = mx; /* allow him to say * for all gold */
+ else
+ while (i != '\n') {
+ if (i == '\033') {
+ scbr();
+ lprcat(" aborted");
+ return (0);
+ }
+ if ((i <= '9') && (i >= '0') && (amt < 99999999))
+ amt = amt * 10 + i - '0';
+ i = ttgetch();
+ }
+ scbr();
+ return (amt);
+}
+
+#ifdef HIDEBYLINK
+/*
+ * routine to zero every byte in a string
+ */
+void
+szero(str)
+ char *str;
+{
+ while (*str)
+ *str++ = 0;
+}
+#endif /* HIDEBYLINK */
diff --git a/larn/monster.c b/larn/monster.c
new file mode 100644
index 0000000..35a329e
--- /dev/null
+++ b/larn/monster.c
@@ -0,0 +1,1911 @@
+/* $NetBSD: monster.c,v 1.18 2012/06/19 05:30:43 dholland Exp $ */
+
+/*
+ * monster.c Larn is copyrighted 1986 by Noah Morgan.
+ *
+ * This file contains the following functions:
+ * ----------------------------------------------------------------------------
+ *
+ * createmonster(monstno) Function to create a monster next to the player
+ * int monstno;
+ *
+ * int cgood(x,y,itm,monst)Function to check location for emptiness
+ * int x,y,itm,monst;
+ *
+ * createitem(it,arg) Routine to place an item next to the player
+ * int it,arg;
+ *
+ * cast() Subroutine called by parse to cast a spell for the user
+ *
+ * speldamage(x) Function to perform spell functions cast by the player
+ * int x;
+ *
+ * loseint() Routine to decrement your int (intelligence) if > 3
+ *
+ * isconfuse() Routine to check to see if player is confused
+ *
+ * nospell(x,monst)Routine to return 1 if a spell doesn't affect a monster
+ * int x,monst;
+ *
+ * fullhit(xx) Function to return full damage against a monst (aka web)
+ * int xx;
+ *
+ * direct(spnum,dam,str,arg)Routine to direct spell damage 1 square in 1 dir
+ * int spnum,dam,arg;
+ * char *str;
+ *
+ * godirect(spnum,dam,str,delay,cshow) Function to perform missile attacks
+ * int spnum,dam,delay;
+ * char *str,cshow;
+ *
+ * ifblind(x,y)Routine to put "monster" or the monster name into lastmosnt
+ * int x,y;
+ *
+ * tdirect(spnum) Routine to teleport away a monster
+ * int spnum;
+ *
+ * omnidirect(sp,dam,str) Routine to damage all monsters 1 square from player
+ * int sp,dam;
+ * char *str;
+ *
+ * dirsub(x,y) Routine to ask for direction, then modify x,y for it
+ * int *x,*y;
+ *
+ * vxy(x,y) Routine to verify/fix (*x,*y) for being within bounds
+ * int *x,*y;
+ *
+ * dirpoly(spnum) Routine to ask for a direction and polymorph a monst
+ * int spnum;
+ *
+ * hitmonster(x,y) Function to hit a monster at the designated coordinates
+ * int x,y;
+ *
+ * hitm(x,y,amt) Function to just hit a monster at a given coordinates
+ * int x,y,amt;
+ *
+ * hitplayer(x,y) Function for the monster to hit the player from (x,y)
+ * int x,y;
+ *
+ * dropsomething(monst) Function to create an object when a monster dies
+ * int monst;
+ *
+ * dropgold(amount) Function to drop some gold around player
+ * int amount;
+ *
+ * something(level) Function to create a random item around player
+ * int level;
+ *
+ * newobject(lev,i) Routine to return a randomly selected new object
+ * int lev,*i;
+ *
+ * spattack(atckno,xx,yy) Function to process special attacks from monsters
+ * int atckno,xx,yy;
+ *
+ * checkloss(x) Routine to subtract hp from user and flag bottomline display
+ * int x;
+ *
+ * annihilate() Routine to annihilate monsters around player, playerx,playery
+ *
+ * newsphere(x,y,dir,lifetime) Function to create a new sphere of annihilation
+ * int x,y,dir,lifetime;
+ *
+ * rmsphere(x,y) Function to delete a sphere of annihilation from list
+ * int x,y;
+ *
+ * sphboom(x,y) Function to perform the effects of a sphere detonation
+ * int x,y;
+ *
+ * genmonst() Function to ask for monster and genocide from game
+ *
+ */
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: monster.c,v 1.18 2012/06/19 05:30:43 dholland Exp $");
+#endif /* not lint */
+
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include "header.h"
+#include "extern.h"
+
+struct isave { /* used for altar reality */
+ char type; /* 0=item, 1=monster */
+ char id; /* item number or monster number */
+ short arg; /* the type of item or hitpoints of monster */
+};
+
+static int cgood(int, int, int, int);
+static void speldamage(int);
+static void loseint(void);
+static int isconfuse(void);
+static int nospell(int, int);
+static int fullhit(int);
+static void direct(int, int, const char *, int);
+static void ifblind(int, int);
+static void tdirect(int);
+static void omnidirect(int, int, const char *);
+static int dirsub(int *, int *);
+static void dirpoly(int);
+static int hitm(int, int, int);
+static void dropsomething(int);
+static int spattack(int, int, int);
+static void sphboom(int, int);
+static void genmonst(void);
+
+/*
+ * createmonster(monstno) Function to create a monster next to the player
+ * int monstno;
+ *
+ * Enter with the monster number (1 to MAXMONST+8)
+ * Returns no value.
+ */
+void
+createmonster(int mon)
+{
+ int x, y, k, i;
+ if (mon < 1 || mon > MAXMONST + 8) { /* check for monster number
+ * out of bounds */
+ beep();
+ lprintf("\ncan't createmonst(%ld)\n", (long) mon);
+ nap(3000);
+ return;
+ }
+ while (monster[mon].genocided && mon < MAXMONST)
+ mon++; /* genocided? */
+ for (k = rnd(8), i = -8; i < 0; i++, k++) { /* choose direction,
+ * then try all */
+ if (k > 8)
+ k = 1; /* wraparound the diroff arrays */
+ x = playerx + diroffx[k];
+ y = playery + diroffy[k];
+ if (cgood(x, y, 0, 1)) { /* if we can create here */
+ mitem[x][y] = mon;
+ hitp[x][y] = monster[mon].hitpoints;
+ stealth[x][y] = know[x][y] = 0;
+ switch (mon) {
+ case ROTHE:
+ case POLTERGEIST:
+ case VAMPIRE:
+ stealth[x][y] = 1;
+ };
+ return;
+ }
+ }
+}
+
+/*
+ * int cgood(x,y,itm,monst) Function to check location for emptiness
+ * int x,y,itm,monst;
+ *
+ * Routine to return TRUE if a location does not have itm or monst there
+ * returns FALSE (0) otherwise
+ * Enter with itm or monst TRUE or FALSE if checking it
+ * Example: if itm==TRUE check for no item at this location
+ * if monst==TRUE check for no monster at this location
+ * This routine will return FALSE if at a wall or the dungeon exit on level 1
+ */
+static int
+cgood(int x, int y, int theitem, int monst)
+{
+#define itm __lose
+ if ((y >= 0) && (y <= MAXY - 1) && (x >= 0) && (x <= MAXX - 1))
+ /* within bounds? */
+ if (item[x][y] != OWALL) /* can't make anything on walls */
+ /* is it free of items? */
+ if (theitem == 0 || (item[x][y] == 0))
+ /* is it free of monsters? */
+ if (monst == 0 || (mitem[x][y] == 0))
+ if ((level != 1) || (x != 33) ||
+ (y != MAXY - 1))
+ /* not exit to level 1 */
+ return (1);
+ return (0);
+}
+
+/*
+ * createitem(it,arg) Routine to place an item next to the player
+ * int it,arg;
+ *
+ * Enter with the item number and its argument (iven[], ivenarg[])
+ * Returns no value, thus we don't know about createitem() failures.
+ */
+void
+createitem(int it, int arg)
+{
+ int x, y, k, i;
+ if (it >= MAXOBJ)
+ return; /* no such object */
+ for (k = rnd(8), i = -8; i < 0; i++, k++) { /* choose direction,
+ * then try all */
+ if (k > 8)
+ k = 1; /* wraparound the diroff arrays */
+ x = playerx + diroffx[k];
+ y = playery + diroffy[k];
+ if (cgood(x, y, 1, 0)) { /* if we can create here */
+ item[x][y] = it;
+ know[x][y] = 0;
+ iarg[x][y] = arg;
+ return;
+ }
+ }
+}
+
+/*
+ * cast() Subroutine called by parse to cast a spell for the user
+ *
+ * No arguments and no return value.
+ */
+static char eys[] = "\nEnter your spell: ";
+void
+cast(void)
+{
+ int i, j, a, b, d;
+ cursors();
+ if (c[SPELLS] <= 0) {
+ lprcat("\nYou don't have any spells!");
+ return;
+ }
+ lprcat(eys);
+ --c[SPELLS];
+ while ((a = ttgetch()) == 'D') {
+ seemagic(-1);
+ cursors();
+ lprcat(eys);
+ }
+ if (a == '\33')
+ goto over; /* to escape casting a spell */
+ if ((b = ttgetch()) == '\33')
+ goto over; /* to escape casting a spell */
+ if ((d = ttgetch()) == '\33') {
+over: lprcat(aborted);
+ c[SPELLS]++;
+ return;
+ } /* to escape casting a spell */
+#ifdef EXTRA
+ c[SPELLSCAST]++;
+#endif
+ for (lprc('\n'), j = -1, i = 0; i < SPNUM; i++) /* seq search for his
+ * spell, hash? */
+ if ((spelcode[i][0] == a) && (spelcode[i][1] == b) && (spelcode[i][2] == d))
+ if (spelknow[i]) {
+ speldamage(i);
+ j = 1;
+ i = SPNUM;
+ }
+ if (j == -1)
+ lprcat(" Nothing Happened ");
+ bottomline();
+}
+
+/*
+ * speldamage(x) Function to perform spell functions cast by the player
+ * int x;
+ *
+ * Enter with the spell number, returns no value.
+ * Please insure that there are 2 spaces before all messages here
+ */
+static void
+speldamage(int x)
+{
+ int i, j, clev;
+ int xl, xh, yl, yh;
+ u_char *p, *kn, *pm;
+
+ if (x >= SPNUM)
+ return; /* no such spell */
+ if (c[TIMESTOP]) {
+ lprcat(" It didn't seem to work");
+ return;
+ } /* not if time stopped */
+ clev = c[LEVEL];
+ if ((rnd(23) == 7) || (rnd(18) > c[INTELLIGENCE])) {
+ lprcat(" It didn't work!");
+ return;
+ }
+ if (clev * 3 + 2 < x) {
+ lprcat(" Nothing happens. You seem inexperienced at this");
+ return;
+ }
+ switch (x) {
+ /* ----- LEVEL 1 SPELLS ----- */
+
+ case 0:
+ if (c[PROTECTIONTIME] == 0)
+ c[MOREDEFENSES] += 2; /* protection field +2 */
+ c[PROTECTIONTIME] += 250;
+ return;
+
+ case 1:
+ i = rnd(((clev + 1) << 1)) + clev + 3;
+ godirect(x, i, (clev >= 2) ? " Your missiles hit the %s" : " Your missile hit the %s", 100, '+'); /* magic missile */
+
+ return;
+
+ case 2:
+ if (c[DEXCOUNT] == 0)
+ c[DEXTERITY] += 3; /* dexterity */
+ c[DEXCOUNT] += 400;
+ return;
+
+ case 3: /* sleep */
+ i = rnd(3) + 1;
+ direct(x, fullhit(i),
+ " While the %s slept, you smashed it %ld times", i);
+ return;
+
+ case 4: /* charm monster */
+ c[CHARMCOUNT] += c[CHARISMA] << 1;
+ return;
+
+ case 5:
+ godirect(x, rnd(10) + 15 + clev, " The sound damages the %s", 70, '@'); /* sonic spear */
+ return;
+
+ /* ----- LEVEL 2 SPELLS ----- */
+
+ case 6: /* web */
+ i = rnd(3) + 2;
+ direct(x, fullhit(i),
+ " While the %s is entangled, you hit %ld times", i);
+ return;
+
+ case 7:
+ if (c[STRCOUNT] == 0)
+ c[STREXTRA] += 3; /* strength */
+ c[STRCOUNT] += 150 + rnd(100);
+ return;
+
+ case 8:
+ yl = playery - 5; /* enlightenment */
+ yh = playery + 6;
+ xl = playerx - 15;
+ xh = playerx + 16;
+ vxy(&xl, &yl);
+ vxy(&xh, &yh); /* check bounds */
+ for (i = yl; i <= yh; i++) /* enlightenment */
+ for (j = xl; j <= xh; j++)
+ know[j][i] = 1;
+ draws(xl, xh + 1, yl, yh + 1);
+ return;
+
+ case 9:
+ raisehp(20 + (clev << 1));
+ return; /* healing */
+
+ case 10:
+ c[BLINDCOUNT] = 0;
+ return; /* cure blindness */
+
+ case 11:
+ createmonster(makemonst(level + 1) + 8);
+ return;
+
+ case 12:
+ if (rnd(11) + 7 <= c[WISDOM])
+ direct(x, rnd(20) + 20 + clev, " The %s believed!", 0);
+ else
+ lprcat(" It didn't believe the illusions!");
+ return;
+
+ case 13: /* if he has the amulet of invisibility then
+ * add more time */
+ for (j = i = 0; i < 26; i++)
+ if (iven[i] == OAMULET)
+ j += 1 + ivenarg[i];
+ c[INVISIBILITY] += (j << 7) + 12;
+ return;
+
+ /* ----- LEVEL 3 SPELLS ----- */
+
+ case 14:
+ godirect(x, rnd(25 + clev) + 25 + clev, " The fireball hits the %s", 40, '*');
+ return; /* fireball */
+
+ case 15:
+ godirect(x, rnd(25) + 20 + clev, " Your cone of cold strikes the %s", 60, 'O'); /* cold */
+ return;
+
+ case 16:
+ dirpoly(x);
+ return; /* polymorph */
+
+ case 17:
+ c[CANCELLATION] += 5 + clev;
+ return; /* cancellation */
+
+ case 18:
+ c[HASTESELF] += 7 + clev;
+ return; /* haste self */
+
+ case 19:
+ omnidirect(x, 30 + rnd(10), " The %s gasps for air"); /* cloud kill */
+ return;
+
+ case 20:
+ xh = min(playerx + 1, MAXX - 2);
+ yh = min(playery + 1, MAXY - 2);
+ for (i = max(playerx - 1, 1); i <= xh; i++) /* vaporize rock */
+ for (j = max(playery - 1, 1); j <= yh; j++) {
+ kn = &know[i][j];
+ pm = &mitem[i][j];
+ switch (*(p = &item[i][j])) {
+ case OWALL:
+ if (level < MAXLEVEL + MAXVLEVEL - 1)
+ *p = *kn = 0;
+ break;
+
+ case OSTATUE:
+ if (c[HARDGAME] < 3) {
+ *p = OBOOK;
+ iarg[i][j] = level;
+ *kn = 0;
+ }
+ break;
+
+ case OTHRONE:
+ *pm = GNOMEKING;
+ *kn = 0;
+ *p = OTHRONE2;
+ hitp[i][j] = monster[GNOMEKING].hitpoints;
+ break;
+
+ case OALTAR:
+ *pm = DEMONPRINCE;
+ *kn = 0;
+ hitp[i][j] = monster[DEMONPRINCE].hitpoints;
+ break;
+ };
+ switch (*pm) {
+ case XORN:
+ ifblind(i, j);
+ hitm(i, j, 200);
+ break; /* Xorn takes damage from vpr */
+ }
+ }
+ return;
+
+ /* ----- LEVEL 4 SPELLS ----- */
+
+ case 21:
+ direct(x, 100 + clev, " The %s shrivels up", 0); /* dehydration */
+ return;
+
+ case 22:
+ godirect(x, rnd(25) + 20 + (clev << 1), " A lightning bolt hits the %s", 1, '~'); /* lightning */
+ return;
+
+ case 23:
+ i = min(c[HP] - 1, c[HPMAX] / 2); /* drain life */
+ direct(x, i + i, "", 0);
+ c[HP] -= i;
+ return;
+
+ case 24:
+ if (c[GLOBE] == 0)
+ c[MOREDEFENSES] += 10;
+ c[GLOBE] += 200;
+ loseint(); /* globe of invulnerability */
+ return;
+
+ case 25:
+ omnidirect(x, 32 + clev, " The %s struggles for air in your flood!"); /* flood */
+ return;
+
+ case 26:
+ if (rnd(151) == 63) {
+ beep();
+ lprcat("\nYour heart stopped!\n");
+ nap(4000);
+ died(270);
+ return;
+ }
+ if (c[WISDOM] > rnd(10) + 10)
+ direct(x, 2000, " The %s's heart stopped", 0); /* finger of death */
+ else
+ lprcat(" It didn't work");
+ return;
+
+ /* ----- LEVEL 5 SPELLS ----- */
+
+ case 27:
+ c[SCAREMONST] += rnd(10) + clev;
+ return; /* scare monster */
+
+ case 28:
+ c[HOLDMONST] += rnd(10) + clev;
+ return; /* hold monster */
+
+ case 29:
+ c[TIMESTOP] += rnd(20) + (clev << 1);
+ return; /* time stop */
+
+ case 30:
+ tdirect(x);
+ return; /* teleport away */
+
+ case 31:
+ omnidirect(x, 35 + rnd(10) + clev, " The %s cringes from the flame"); /* magic fire */
+ return;
+
+ /* ----- LEVEL 6 SPELLS ----- */
+
+ case 32:
+ if ((rnd(23) == 5) && (wizard == 0)) { /* sphere of
+ * annihilation */
+ beep();
+ lprcat("\nYou have been enveloped by the zone of nothingness!\n");
+ nap(4000);
+ died(258);
+ return;
+ }
+ xl = playerx;
+ yl = playery;
+ loseint();
+ i = dirsub(&xl, &yl); /* get direction of sphere */
+ newsphere(xl, yl, i, rnd(20) + 11); /* make a sphere */
+ return;
+
+ case 33:
+ genmonst();
+ spelknow[33] = 0; /* genocide */
+ loseint();
+ return;
+
+ case 34: /* summon demon */
+ if (rnd(100) > 30) {
+ direct(x, 150, " The demon strikes at the %s", 0);
+ return;
+ }
+ if (rnd(100) > 15) {
+ lprcat(" Nothing seems to have happened");
+ return;
+ }
+ lprcat(" The demon turned on you and vanished!");
+ beep();
+ i = rnd(40) + 30;
+ lastnum = 277;
+ losehp(i); /* must say killed by a demon */
+ return;
+
+ case 35: /* walk through walls */
+ c[WTW] += rnd(10) + 5;
+ return;
+
+ case 36: /* alter reality */
+ {
+ struct isave *save; /* pointer to item save
+ * structure */
+ int sc;
+ sc = 0; /* # items saved */
+ save = (struct isave *) malloc(sizeof(struct isave) * MAXX * MAXY * 2);
+ for (j = 0; j < MAXY; j++)
+ for (i = 0; i < MAXX; i++) { /* save all items and
+ * monsters */
+ xl = item[i][j];
+ if (xl && xl != OWALL && xl != OANNIHILATION) {
+ save[sc].type = 0;
+ save[sc].id = item[i][j];
+ save[sc++].arg = iarg[i][j];
+ }
+ if (mitem[i][j]) {
+ save[sc].type = 1;
+ save[sc].id = mitem[i][j];
+ save[sc++].arg = hitp[i][j];
+ }
+ item[i][j] = OWALL;
+ mitem[i][j] = 0;
+ if (wizard)
+ know[i][j] = 1;
+ else
+ know[i][j] = 0;
+ }
+ eat(1, 1);
+ if (level == 1)
+ item[33][MAXY - 1] = 0;
+ for (j = rnd(MAXY - 2), i = 1; i < MAXX - 1; i++)
+ item[i][j] = 0;
+ while (sc > 0) { /* put objects back in level */
+ --sc;
+ if (save[sc].type == 0) {
+ int trys;
+ for (trys = 100, i = j = 1; --trys > 0 && item[i][j]; i = rnd(MAXX - 1), j = rnd(MAXY - 1));
+ if (trys) {
+ item[i][j] = save[sc].id;
+ iarg[i][j] = save[sc].arg;
+ }
+ } else { /* put monsters back in */
+ int trys;
+ for (trys = 100, i = j = 1; --trys > 0 && (item[i][j] == OWALL || mitem[i][j]); i = rnd(MAXX - 1), j = rnd(MAXY - 1));
+ if (trys) {
+ mitem[i][j] = save[sc].id;
+ hitp[i][j] = save[sc].arg;
+ }
+ }
+ }
+ loseint();
+ draws(0, MAXX, 0, MAXY);
+ if (wizard == 0)
+ spelknow[36] = 0;
+ free((char *) save);
+ positionplayer();
+ return;
+ }
+
+ case 37: /* permanence */
+ adjusttime(-99999L);
+ spelknow[37] = 0; /* forget */
+ loseint();
+ return;
+
+ default:
+ lprintf(" spell %ld not available!", (long) x);
+ beep();
+ return;
+ };
+}
+
+/*
+ * loseint() Routine to subtract 1 from your int (intelligence) if > 3
+ *
+ * No arguments and no return value
+ */
+static void
+loseint(void)
+{
+ if (--c[INTELLIGENCE] < 3)
+ c[INTELLIGENCE] = 3;
+}
+
+/*
+ * isconfuse() Routine to check to see if player is confused
+ *
+ * This routine prints out a message saying "You can't aim your magic!"
+ * returns 0 if not confused, non-zero (time remaining confused) if confused
+ */
+static int
+isconfuse(void)
+{
+ if (c[CONFUSE]) {
+ lprcat(" You can't aim your magic!");
+ beep();
+ }
+ return (c[CONFUSE]);
+}
+
+/*
+ * nospell(x,monst) Routine to return 1 if a spell doesn't affect a monster
+ * int x,monst;
+ *
+ * Subroutine to return 1 if the spell can't affect the monster
+ * otherwise returns 0
+ * Enter with the spell number in x, and the monster number in monst.
+ */
+static int
+nospell(int x, int monst)
+{
+ int tmp;
+ if (x >= SPNUM || monst >= MAXMONST + 8 || monst < 0 || x < 0)
+ return (0); /* bad spell or monst */
+ if ((tmp = spelweird[monst - 1][x]) == 0)
+ return (0);
+ cursors();
+ lprc('\n');
+ lprintf(spelmes[tmp], monster[monst].name);
+ return (1);
+}
+
+/*
+ * fullhit(xx) Function to return full damage against a monster (aka web)
+ * int xx;
+ *
+ * Function to return hp damage to monster due to a number of full hits
+ * Enter with the number of full hits being done
+ */
+static int
+fullhit(int xx)
+{
+ int i;
+ if (xx < 0 || xx > 20)
+ return (0); /* fullhits are out of range */
+ if (c[LANCEDEATH])
+ return (10000); /* lance of death */
+ i = xx * ((c[WCLASS] >> 1) + c[STRENGTH] + c[STREXTRA] - c[HARDGAME] - 12 + c[MOREDAM]);
+ return ((i >= 1) ? i : xx);
+}
+
+/*
+ * direct(spnum,dam,str,arg) Routine to direct spell damage 1 square in 1 dir
+ * int spnum,dam,arg;
+ * char *str;
+ *
+ * Routine to ask for a direction to a spell and then hit the monster
+ * Enter with the spell number in spnum, the damage to be done in dam,
+ * lprintf format string in str, and lprintf's argument in arg.
+ * Returns no value.
+ */
+static void
+direct(int spnum, int dam, const char *str, int arg)
+{
+ int x, y;
+ int m;
+ if (spnum < 0 || spnum >= SPNUM || str == 0)
+ return; /* bad arguments */
+ if (isconfuse())
+ return;
+ dirsub(&x, &y);
+ m = mitem[x][y];
+ if (item[x][y] == OMIRROR) {
+ if (spnum == 3) { /* sleep */
+ lprcat("You fall asleep! ");
+ beep();
+ fool:
+ arg += 2;
+ while (arg-- > 0) {
+ parse2();
+ nap(1000);
+ }
+ return;
+ } else if (spnum == 6) { /* web */
+ lprcat("You get stuck in your own web! ");
+ beep();
+ goto fool;
+ } else {
+ lastnum = 278;
+ lprintf(str, "spell caster (that's you)", (long) arg);
+ beep();
+ losehp(dam);
+ return;
+ }
+ }
+ if (m == 0) {
+ lprcat(" There wasn't anything there!");
+ return;
+ }
+ ifblind(x, y);
+ if (nospell(spnum, m)) {
+ lasthx = x;
+ lasthy = y;
+ return;
+ }
+ lprintf(str, lastmonst, (long) arg);
+ hitm(x, y, dam);
+}
+
+/*
+ * godirect(spnum,dam,str,delay,cshow) Function to perform missile attacks
+ * int spnum,dam,delay;
+ * char *str,cshow;
+ *
+ * Function to hit in a direction from a missile weapon and have it keep
+ * on going in that direction until its power is exhausted
+ * Enter with the spell number in spnum, the power of the weapon in hp,
+ * lprintf format string in str, the # of milliseconds to delay between
+ * locations in delay, and the character to represent the weapon in cshow.
+ * Returns no value.
+ */
+void
+godirect(int spnum, int dam, const char *str, int delay, int cshow_i)
+{
+ u_char *p;
+ int x, y, m;
+ int dx, dy;
+ char cshow;
+
+ /* truncate to char width in case it matters */
+ cshow = (char)cshow_i;
+
+ if (spnum < 0 || spnum >= SPNUM || str == 0 || delay < 0)
+ return; /* bad args */
+ if (isconfuse())
+ return;
+ dirsub(&dx, &dy);
+ x = dx;
+ y = dy;
+ dx = x - playerx;
+ dy = y - playery;
+ x = playerx;
+ y = playery;
+ while (dam > 0) {
+ x += dx;
+ y += dy;
+ if ((x > MAXX - 1) || (y > MAXY - 1) || (x < 0) || (y < 0)) {
+ dam = 0;
+ break; /* out of bounds */
+ }
+ if ((x == playerx) && (y == playery)) { /* if energy hits player */
+ cursors();
+ lprcat("\nYou are hit by your own magic!");
+ beep();
+ lastnum = 278;
+ losehp(dam);
+ return;
+ }
+ if (c[BLINDCOUNT] == 0) { /* if not blind show effect */
+ cursor(x + 1, y + 1);
+ lprc(cshow);
+ nap(delay);
+ show1cell(x, y);
+ }
+ if ((m = mitem[x][y])) { /* is there a monster there? */
+ ifblind(x, y);
+ if (nospell(spnum, m)) {
+ lasthx = x;
+ lasthy = y;
+ return;
+ }
+ cursors();
+ lprc('\n');
+ lprintf(str, lastmonst);
+ dam -= hitm(x, y, dam);
+ show1cell(x, y);
+ nap(1000);
+ x -= dx;
+ y -= dy;
+ } else
+ switch (*(p = &item[x][y])) {
+ case OWALL:
+ cursors();
+ lprc('\n');
+ lprintf(str, "wall");
+ if (dam >= 50 + c[HARDGAME]) /* enough damage? */
+ if (level < MAXLEVEL + MAXVLEVEL - 1) /* not on V3 */
+ if ((x < MAXX - 1) && (y < MAXY - 1) && (x) && (y)) {
+ lprcat(" The wall crumbles");
+ god3: *p = 0;
+ god: know[x][y] = 0;
+ show1cell(x, y);
+ }
+ god2: dam = 0;
+ break;
+
+ case OCLOSEDDOOR:
+ cursors();
+ lprc('\n');
+ lprintf(str, "door");
+ if (dam >= 40) {
+ lprcat(" The door is blasted apart");
+ goto god3;
+ }
+ goto god2;
+
+ case OSTATUE:
+ cursors();
+ lprc('\n');
+ lprintf(str, "statue");
+ if (c[HARDGAME] < 3)
+ if (dam > 44) {
+ lprcat(" The statue crumbles");
+ *p = OBOOK;
+ iarg[x][y] = level;
+ goto god;
+ }
+ goto god2;
+
+ case OTHRONE:
+ cursors();
+ lprc('\n');
+ lprintf(str, "throne");
+ if (dam > 39) {
+ mitem[x][y] = GNOMEKING;
+ hitp[x][y] = monster[GNOMEKING].hitpoints;
+ *p = OTHRONE2;
+ goto god;
+ }
+ goto god2;
+
+ case OMIRROR:
+ dx *= -1;
+ dy *= -1;
+ break;
+ };
+ dam -= 3 + (c[HARDGAME] >> 1);
+ }
+}
+
+/*
+ * ifblind(x,y) Routine to put "monster" or the monster name into lastmosnt
+ * int x,y;
+ *
+ * Subroutine to copy the word "monster" into lastmonst if the player is blind
+ * Enter with the coordinates (x,y) of the monster
+ * Returns no value.
+ */
+static void
+ifblind(int x, int y)
+{
+ const char *p;
+
+ vxy(&x, &y); /* verify correct x,y coordinates */
+ if (c[BLINDCOUNT]) {
+ lastnum = 279;
+ p = "monster";
+ } else {
+ lastnum = mitem[x][y];
+ p = monster[lastnum].name;
+ }
+ strcpy(lastmonst, p);
+}
+
+/*
+ * tdirect(spnum) Routine to teleport away a monster
+ * int spnum;
+ *
+ * Routine to ask for a direction to a spell and then teleport away monster
+ * Enter with the spell number that wants to teleport away
+ * Returns no value.
+ */
+static void
+tdirect(int spnum)
+{
+ int x, y;
+ int m;
+ if (spnum < 0 || spnum >= SPNUM)
+ return; /* bad args */
+ if (isconfuse())
+ return;
+ dirsub(&x, &y);
+ if ((m = mitem[x][y]) == 0) {
+ lprcat(" There wasn't anything there!");
+ return;
+ }
+ ifblind(x, y);
+ if (nospell(spnum, m)) {
+ lasthx = x;
+ lasthy = y;
+ return;
+ }
+ fillmonst(m);
+ mitem[x][y] = know[x][y] = 0;
+}
+
+/*
+ * omnidirect(sp,dam,str) Routine to damage all monsters 1 square from player
+ * int sp,dam;
+ * char *str;
+ *
+ * Routine to cast a spell and then hit the monster in all directions
+ * Enter with the spell number in sp, the damage done to wach square in dam,
+ * and the lprintf string to identify the spell in str.
+ * Returns no value.
+ */
+static void
+omnidirect(int spnum, int dam, const char *str)
+{
+ int x, y, m;
+
+ if (spnum < 0 || spnum >= SPNUM || str == 0)
+ return; /* bad args */
+ for (x = playerx - 1; x < playerx + 2; x++)
+ for (y = playery - 1; y < playery + 2; y++) {
+ if ((m = mitem[x][y]) != 0) {
+ if (nospell(spnum, m) == 0) {
+ ifblind(x, y);
+ cursors();
+ lprc('\n');
+ lprintf(str, lastmonst);
+ hitm(x, y, dam);
+ nap(800);
+ } else {
+ lasthx = x;
+ lasthy = y;
+ }
+ }
+ }
+}
+
+/*
+ * static dirsub(x,y) Routine to ask for direction, then modify x,y for it
+ * int *x,*y;
+ *
+ * Function to ask for a direction and modify an x,y for that direction
+ * Enter with the origination coordinates in (x,y).
+ * Returns index into diroffx[] (0-8).
+ */
+static int
+dirsub(int *x, int *y)
+{
+ int i;
+ lprcat("\nIn What Direction? ");
+ for (i = 0;;)
+ switch (ttgetch()) {
+ case 'b':
+ i++;
+ case 'n':
+ i++;
+ case 'y':
+ i++;
+ case 'u':
+ i++;
+ case 'h':
+ i++;
+ case 'k':
+ i++;
+ case 'l':
+ i++;
+ case 'j':
+ i++;
+ goto out;
+ };
+out:
+ *x = playerx + diroffx[i];
+ *y = playery + diroffy[i];
+ vxy(x, y);
+ return (i);
+}
+
+/*
+ * vxy(x,y) Routine to verify/fix coordinates for being within bounds
+ * int *x,*y;
+ *
+ * Function to verify x & y are within the bounds for a level
+ * If *x or *y is not within the absolute bounds for a level, fix them so that
+ * they are on the level.
+ * Returns TRUE if it was out of bounds, and the *x & *y in the calling
+ * routine are affected.
+ */
+int
+vxy(int *x, int *y)
+{
+ int flag = 0;
+ if (*x < 0) {
+ *x = 0;
+ flag++;
+ }
+ if (*y < 0) {
+ *y = 0;
+ flag++;
+ }
+ if (*x >= MAXX) {
+ *x = MAXX - 1;
+ flag++;
+ }
+ if (*y >= MAXY) {
+ *y = MAXY - 1;
+ flag++;
+ }
+ return (flag);
+}
+
+/*
+ * dirpoly(spnum) Routine to ask for a direction and polymorph a monst
+ * int spnum;
+ *
+ * Subroutine to polymorph a monster and ask for the direction its in
+ * Enter with the spell number in spmun.
+ * Returns no value.
+ */
+static void
+dirpoly(int spnum)
+{
+ int x, y, m;
+ if (spnum < 0 || spnum >= SPNUM)
+ return; /* bad args */
+ if (isconfuse())
+ return; /* if he is confused, he can't aim his magic */
+ dirsub(&x, &y);
+ if (mitem[x][y] == 0) {
+ lprcat(" There wasn't anything there!");
+ return;
+ }
+ ifblind(x, y);
+ if (nospell(spnum, mitem[x][y])) {
+ lasthx = x;
+ lasthy = y;
+ return;
+ }
+ while (monster[m = mitem[x][y] = rnd(MAXMONST + 7)].genocided);
+ hitp[x][y] = monster[m].hitpoints;
+ show1cell(x, y); /* show the new monster */
+}
+
+/*
+ * hitmonster(x,y) Function to hit a monster at the designated coordinates
+ * int x,y;
+ *
+ * This routine is used for a bash & slash type attack on a monster
+ * Enter with the coordinates of the monster in (x,y).
+ * Returns no value.
+ */
+void
+hitmonster(int x, int y)
+{
+ int tmp, monst, damag = 0, flag;
+ if (c[TIMESTOP])
+ return; /* not if time stopped */
+ vxy(&x, &y); /* verify coordinates are within range */
+ if ((monst = mitem[x][y]) == 0)
+ return;
+ hit3flag = 1;
+ ifblind(x, y);
+ tmp = monster[monst].armorclass + c[LEVEL] + c[DEXTERITY] +
+ c[WCLASS] / 4 - 12;
+ cursors();
+ /* need at least random chance to hit */
+ if ((rnd(20) < tmp - c[HARDGAME]) || (rnd(71) < 5)) {
+ lprcat("\nYou hit");
+ flag = 1;
+ damag = fullhit(1);
+ if (damag < 9999)
+ damag = rnd(damag) + 1;
+ } else {
+ lprcat("\nYou missed");
+ flag = 0;
+ }
+ lprcat(" the ");
+ lprcat(lastmonst);
+ if (flag) /* if the monster was hit */
+ if ((monst == RUSTMONSTER) || (monst == DISENCHANTRESS) || (monst == CUBE))
+ if (c[WIELD] > 0)
+ if (ivenarg[c[WIELD]] > -10) {
+ lprintf("\nYour weapon is dulled by the %s", lastmonst);
+ beep();
+ --ivenarg[c[WIELD]];
+ }
+ if (flag)
+ hitm(x, y, damag);
+ if (monst == VAMPIRE)
+ if (hitp[x][y] < 25) {
+ mitem[x][y] = BAT;
+ know[x][y] = 0;
+ }
+}
+
+/*
+ * hitm(x,y,amt) Function to just hit a monster at a given coordinates
+ * int x,y,amt;
+ *
+ * Returns the number of hitpoints the monster absorbed
+ * This routine is used to specifically damage a monster at a location (x,y)
+ * Called by hitmonster(x,y)
+ */
+static int
+hitm(int x, int y, int amt)
+{
+ int monst;
+ int hpoints, amt2;
+ vxy(&x, &y); /* verify coordinates are within range */
+ amt2 = amt; /* save initial damage so we can return it */
+ monst = mitem[x][y];
+ if (c[HALFDAM])
+ amt >>= 1; /* if half damage curse adjust damage points */
+ if (amt <= 0)
+ amt2 = amt = 1;
+ lasthx = x;
+ lasthy = y;
+ stealth[x][y] = 1; /* make sure hitting monst breaks stealth
+ * condition */
+ c[HOLDMONST] = 0; /* hit a monster breaks hold monster spell */
+ switch (monst) { /* if a dragon and orb(s) of dragon slaying */
+ case WHITEDRAGON:
+ case REDDRAGON:
+ case GREENDRAGON:
+ case BRONZEDRAGON:
+ case PLATINUMDRAGON:
+ case SILVERDRAGON:
+ amt *= 1 + (c[SLAYING] << 1);
+ break;
+ }
+ /* invincible monster fix is here */
+ if (hitp[x][y] > monster[monst].hitpoints)
+ hitp[x][y] = monster[monst].hitpoints;
+ if ((hpoints = hitp[x][y]) <= amt) {
+#ifdef EXTRA
+ c[MONSTKILLED]++;
+#endif
+ lprintf("\nThe %s died!", lastmonst);
+ raiseexperience((long) monster[monst].experience);
+ amt = monster[monst].gold;
+ if (amt > 0)
+ dropgold(rnd(amt) + amt);
+ dropsomething(monst);
+ disappear(x, y);
+ bottomline();
+ return (hpoints);
+ }
+ hitp[x][y] = hpoints - amt;
+ return (amt2);
+}
+
+/*
+ * hitplayer(x,y) Function for the monster to hit the player from (x,y)
+ * int x,y;
+ *
+ * Function for the monster to hit the player with monster at location x,y
+ * Returns nothing of value.
+ */
+void
+hitplayer(int x, int y)
+{
+ int dam, tmp, mster, bias;
+ vxy(&x, &y); /* verify coordinates are within range */
+ lastnum = mster = mitem[x][y];
+ /*
+ * spirit nagas and poltergeists do nothing if scarab of negate
+ * spirit
+ */
+ if (c[NEGATESPIRIT] || c[SPIRITPRO])
+ if ((mster == POLTERGEIST) || (mster == SPIRITNAGA))
+ return;
+ /* if undead and cube of undead control */
+ if (c[CUBEofUNDEAD] || c[UNDEADPRO])
+ if ((mster == VAMPIRE) || (mster == WRAITH) || (mster == ZOMBIE))
+ return;
+ if ((know[x][y] & 1) == 0) {
+ know[x][y] = 1;
+ show1cell(x, y);
+ }
+ bias = (c[HARDGAME]) + 1;
+ hitflag = hit2flag = hit3flag = 1;
+ yrepcount = 0;
+ cursors();
+ ifblind(x, y);
+ if (c[INVISIBILITY])
+ if (rnd(33) < 20) {
+ lprintf("\nThe %s misses wildly", lastmonst);
+ return;
+ }
+ if (c[CHARMCOUNT])
+ if (rnd(30) + 5 * monster[mster].level - c[CHARISMA] < 30) {
+ lprintf("\nThe %s is awestruck at your magnificence!", lastmonst);
+ return;
+ }
+ if (mster == BAT)
+ dam = 1;
+ else {
+ dam = monster[mster].damage;
+ dam += rnd((int) ((dam < 1) ? 1 : dam)) + monster[mster].level;
+ }
+ tmp = 0;
+ if (monster[mster].attack > 0)
+ if (((dam + bias + 8) > c[AC]) || (rnd((int) ((c[AC] > 0) ? c[AC] : 1)) == 1)) {
+ if (spattack(monster[mster].attack, x, y)) {
+ flushall();
+ return;
+ }
+ tmp = 1;
+ bias -= 2;
+ cursors();
+ }
+ if (((dam + bias) > c[AC]) || (rnd((int) ((c[AC] > 0) ? c[AC] : 1)) == 1)) {
+ lprintf("\n The %s hit you ", lastmonst);
+ tmp = 1;
+ if ((dam -= c[AC]) < 0)
+ dam = 0;
+ if (dam > 0) {
+ losehp(dam);
+ bottomhp();
+ flushall();
+ }
+ }
+ if (tmp == 0)
+ lprintf("\n The %s missed ", lastmonst);
+}
+
+/*
+ * dropsomething(monst) Function to create an object when a monster dies
+ * int monst;
+ *
+ * Function to create an object near the player when certain monsters are killed
+ * Enter with the monster number
+ * Returns nothing of value.
+ */
+static void
+dropsomething(int monst)
+{
+ switch (monst) {
+ case ORC:
+ case NYMPH:
+ case ELF:
+ case TROGLODYTE:
+ case TROLL:
+ case ROTHE:
+ case VIOLETFUNGI:
+ case PLATINUMDRAGON:
+ case GNOMEKING:
+ case REDDRAGON:
+ something(level);
+ return;
+
+ case LEPRECHAUN:
+ if (rnd(101) >= 75)
+ creategem();
+ if (rnd(5) == 1)
+ dropsomething(LEPRECHAUN);
+ return;
+ }
+}
+
+/*
+ * dropgold(amount) Function to drop some gold around player
+ * int amount;
+ *
+ * Enter with the number of gold pieces to drop
+ * Returns nothing of value.
+ */
+void
+dropgold(int amount)
+{
+ if (amount > 250)
+ createitem(OMAXGOLD, amount / 100);
+ else
+ createitem(OGOLDPILE, amount);
+}
+
+/*
+ * something(level) Function to create a random item around player
+ * int level;
+ *
+ * Function to create an item from a designed probability around player
+ * Enter with the cave level on which something is to be dropped
+ * Returns nothing of value.
+ */
+void
+something(int cavelevel)
+{
+ int j;
+ int i;
+ if (cavelevel < 0 || cavelevel > MAXLEVEL + MAXVLEVEL)
+ return; /* correct level? */
+ if (rnd(101) < 8)
+ something(cavelevel); /* possibly more than one item */
+ j = newobject(cavelevel, &i);
+ createitem(j, i);
+}
+
+/*
+ * newobject(lev,i) Routine to return a randomly selected new object
+ * int lev,*i;
+ *
+ * Routine to return a randomly selected object to be created
+ * Returns the object number created, and sets *i for its argument
+ * Enter with the cave level and a pointer to the items arg
+ */
+static char nobjtab[] = {
+ 0, OSCROLL, OSCROLL, OSCROLL, OSCROLL, OPOTION, OPOTION,
+ OPOTION, OPOTION, OGOLDPILE, OGOLDPILE, OGOLDPILE, OGOLDPILE,
+ OBOOK, OBOOK, OBOOK, OBOOK, ODAGGER, ODAGGER, ODAGGER,
+ OLEATHER, OLEATHER, OLEATHER, OREGENRING, OPROTRING,
+ OENERGYRING, ODEXRING, OSTRRING, OSPEAR, OBELT, ORING,
+ OSTUDLEATHER, OSHIELD, OFLAIL, OCHAIN, O2SWORD, OPLATE,
+ OLONGSWORD};
+
+int
+newobject(int lev, int *i)
+{
+ int tmp = 32, j;
+ if (level < 0 || level > MAXLEVEL + MAXVLEVEL)
+ return (0); /* correct level? */
+ if (lev > 6)
+ tmp = 37;
+ else if (lev > 4)
+ tmp = 35;
+ j = nobjtab[tmp = rnd(tmp)]; /* the object type */
+ switch (tmp) {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ *i = newscroll();
+ break;
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ *i = newpotion();
+ break;
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ *i = rnd((lev + 1) * 10) + lev * 10 + 10;
+ break;
+ case 13:
+ case 14:
+ case 15:
+ case 16:
+ *i = lev;
+ break;
+ case 17:
+ case 18:
+ case 19:
+ if (!(*i = newdagger()))
+ return (0);
+ break;
+ case 20:
+ case 21:
+ case 22:
+ if (!(*i = newleather()))
+ return (0);
+ break;
+ case 23:
+ case 32:
+ case 35:
+ *i = rund(lev / 3 + 1);
+ break;
+ case 24:
+ case 26:
+ *i = rnd(lev / 4 + 1);
+ break;
+ case 25:
+ *i = rund(lev / 4 + 1);
+ break;
+ case 27:
+ *i = rnd(lev / 2 + 1);
+ break;
+ case 30:
+ case 33:
+ *i = rund(lev / 2 + 1);
+ break;
+ case 28:
+ *i = rund(lev / 3 + 1);
+ if (*i == 0)
+ return (0);
+ break;
+ case 29:
+ case 31:
+ *i = rund(lev / 2 + 1);
+ if (*i == 0)
+ return (0);
+ break;
+ case 34:
+ *i = newchain();
+ break;
+ case 36:
+ *i = newplate();
+ break;
+ case 37:
+ *i = newsword();
+ break;
+ }
+ return (j);
+}
+
+/*
+ * spattack(atckno,xx,yy) Function to process special attacks from monsters
+ * int atckno,xx,yy;
+ *
+ * Enter with the special attack number, and the coordinates (xx,yy)
+ * of the monster that is special attacking
+ * Returns 1 if must do a show1cell(xx,yy) upon return, 0 otherwise
+ *
+ * atckno monster effect
+ * ---------------------------------------------------
+ * 0 none
+ * 1 rust monster eat armor
+ * 2 hell hound breathe light fire
+ * 3 dragon breathe fire
+ * 4 giant centipede weakening sing
+ * 5 white dragon cold breath
+ * 6 wraith drain level
+ * 7 waterlord water gusher
+ * 8 leprechaun steal gold
+ * 9 disenchantress disenchant weapon or armor
+ * 10 ice lizard hits with barbed tail
+ * 11 umber hulk confusion
+ * 12 spirit naga cast spells taken from special attacks
+ * 13 platinum dragon psionics
+ * 14 nymph steal objects
+ * 15 bugbear bite
+ * 16 osequip bite
+ *
+ * char rustarm[ARMORTYPES][2];
+ * special array for maximum rust damage to armor from rustmonster
+ * format is: { armor type , minimum attribute
+ */
+#define ARMORTYPES 6
+static char rustarm[ARMORTYPES][2] = {
+ { OSTUDLEATHER, -2 },
+ { ORING, -4 },
+ { OCHAIN, -5 },
+ { OSPLINT, -6 },
+ { OPLATE, -8 },
+ { OPLATEARMOR, -9}
+};
+static char spsel[] = {1, 2, 3, 5, 6, 8, 9, 11, 13, 14};
+static int
+spattack(int x, int xx, int yy)
+{
+ int i, j = 0, k, m;
+ const char *p = NULL;
+
+ if (c[CANCELLATION])
+ return (0);
+ vxy(&xx, &yy); /* verify x & y coordinates */
+ switch (x) {
+ case 1: /* rust your armor, j=1 when rusting has occurred */
+ m = k = c[WEAR];
+ if ((i = c[SHIELD]) != -1) {
+ if (--ivenarg[i] < -1)
+ ivenarg[i] = -1;
+ else
+ j = 1;
+ }
+ if ((j == 0) && (k != -1)) {
+ m = iven[k];
+ for (i = 0; i < ARMORTYPES; i++)
+ /* find his armor in table */
+ if (m == rustarm[i][0]) {
+ if (--ivenarg[k] < rustarm[i][1])
+ ivenarg[k] = rustarm[i][1];
+ else
+ j = 1;
+ break;
+ }
+ }
+ if (j == 0) /* if rusting did not occur */
+ switch (m) {
+ case OLEATHER:
+ p = "\nThe %s hit you -- You're lucky you have leather on";
+ break;
+ case OSSPLATE:
+ p = "\nThe %s hit you -- You're fortunate to have stainless steel armor!";
+ break;
+ }
+ else {
+ beep();
+ p = "\nThe %s hit you -- your armor feels weaker";
+ }
+ break;
+
+ case 2:
+ i = rnd(15) + 8 - c[AC];
+spout: p = "\nThe %s breathes fire at you!";
+ if (c[FIRERESISTANCE])
+ p = "\nThe %s's flame doesn't faze you!";
+ else
+spout2: if (p) {
+ lprintf(p, lastmonst);
+ beep();
+ }
+ checkloss(i);
+ return (0);
+
+ case 3:
+ i = rnd(20) + 25 - c[AC];
+ goto spout;
+
+ case 4:
+ if (c[STRENGTH] > 3) {
+ p = "\nThe %s stung you! You feel weaker";
+ beep();
+ --c[STRENGTH];
+ } else
+ p = "\nThe %s stung you!";
+ break;
+
+ case 5:
+ p = "\nThe %s blasts you with his cold breath";
+ i = rnd(15) + 18 - c[AC];
+ goto spout2;
+
+ case 6:
+ lprintf("\nThe %s drains you of your life energy!", lastmonst);
+ loselevel();
+ beep();
+ return (0);
+
+ case 7:
+ p = "\nThe %s got you with a gusher!";
+ i = rnd(15) + 25 - c[AC];
+ goto spout2;
+
+ case 8:
+ if (c[NOTHEFT])
+ return (0); /* he has a device of no theft */
+ if (c[GOLD]) {
+ p = "\nThe %s hit you -- Your purse feels lighter";
+ if (c[GOLD] > 32767)
+ c[GOLD] >>= 1;
+ else
+ c[GOLD] -= rnd((int) (1 + (c[GOLD] >> 1)));
+ if (c[GOLD] < 0)
+ c[GOLD] = 0;
+ } else
+ p = "\nThe %s couldn't find any gold to steal";
+ lprintf(p, lastmonst);
+ disappear(xx, yy);
+ beep();
+ bottomgold();
+ return (1);
+
+ case 9:
+ for (j = 50;;) {/* disenchant */
+ i = rund(26);
+ m = iven[i]; /* randomly select item */
+ if (m > 0 && ivenarg[i] > 0 && m != OSCROLL && m != OPOTION) {
+ if ((ivenarg[i] -= 3) < 0)
+ ivenarg[i] = 0;
+ lprintf("\nThe %s hits you -- you feel a sense of loss", lastmonst);
+ srcount = 0;
+ beep();
+ show3(i);
+ bottomline();
+ return (0);
+ }
+ if (--j <= 0) {
+ p = "\nThe %s nearly misses";
+ break;
+ }
+ break;
+ }
+ break;
+
+ case 10:
+ p = "\nThe %s hit you with his barbed tail";
+ i = rnd(25) - c[AC];
+ goto spout2;
+
+ case 11:
+ p = "\nThe %s has confused you";
+ beep();
+ c[CONFUSE] += 10 + rnd(10);
+ break;
+
+ case 12: /* performs any number of other special
+ * attacks */
+ return (spattack(spsel[rund(10)], xx, yy));
+
+ case 13:
+ p = "\nThe %s flattens you with his psionics!";
+ i = rnd(15) + 30 - c[AC];
+ goto spout2;
+
+ case 14:
+ if (c[NOTHEFT])
+ return (0); /* he has device of no theft */
+ if (emptyhanded() == 1) {
+ p = "\nThe %s couldn't find anything to steal";
+ break;
+ }
+ lprintf("\nThe %s picks your pocket and takes:", lastmonst);
+ beep();
+ if (stealsomething() == 0)
+ lprcat(" nothing");
+ disappear(xx, yy);
+ bottomline();
+ return (1);
+
+ case 15:
+ i = rnd(10) + 5 - c[AC];
+spout3: p = "\nThe %s bit you!";
+ goto spout2;
+
+ case 16:
+ i = rnd(15) + 10 - c[AC];
+ goto spout3;
+ };
+ if (p) {
+ lprintf(p, lastmonst);
+ bottomline();
+ }
+ return (0);
+}
+
+/*
+ * checkloss(x) Routine to subtract hp from user and flag bottomline display
+ * int x;
+ *
+ * Routine to subtract hitpoints from the user and flag the bottomline display
+ * Enter with the number of hit points to lose
+ * Note: if x > c[HP] this routine could kill the player!
+ */
+void
+checkloss(int x)
+{
+ if (x > 0) {
+ losehp(x);
+ bottomhp();
+ }
+}
+
+/*
+ * annihilate() Routine to annihilate all monsters around player (playerx,playery)
+ *
+ * Gives player experience, but no dropped objects
+ * Returns the experience gained from all monsters killed
+ */
+int
+annihilate(void)
+{
+ int i, j;
+ long k;
+ u_char *p;
+ for (k = 0, i = playerx - 1; i <= playerx + 1; i++)
+ for (j = playery - 1; j <= playery + 1; j++)
+ if (!vxy(&i, &j)) { /* if not out of bounds */
+ if (*(p = &mitem[i][j])) { /* if a monster there */
+ if (*p < DEMONLORD + 2) {
+ k += monster[*p].experience;
+ *p = know[i][j] = 0;
+ } else {
+ lprintf("\nThe %s barely escapes being annihilated!", monster[*p].name);
+ hitp[i][j] = (hitp[i][j] >> 1) + 1; /* lose half hit points */
+ }
+ }
+ }
+ if (k > 0) {
+ lprcat("\nYou hear loud screams of agony!");
+ raiseexperience((long) k);
+ }
+ return (k);
+}
+
+/*
+ * newsphere(x,y,dir,lifetime) Function to create a new sphere of annihilation
+ * int x,y,dir,lifetime;
+ *
+ * Enter with the coordinates of the sphere in x,y
+ * the direction (0-8 diroffx format) in dir, and the lifespan of the
+ * sphere in lifetime (in turns)
+ * Returns the number of spheres currently in existence
+ */
+int
+newsphere(int x, int y, int dir, int life)
+{
+ int m;
+ struct sphere *sp;
+ if (((sp = (struct sphere *) malloc(sizeof(struct sphere)))) == 0)
+ return (c[SPHCAST]); /* can't malloc, therefore failure */
+ if (dir >= 9)
+ dir = 0; /* no movement if direction not found */
+ if (level == 0)
+ vxy(&x, &y); /* don't go out of bounds */
+ else {
+ if (x < 1)
+ x = 1;
+ if (x >= MAXX - 1)
+ x = MAXX - 2;
+ if (y < 1)
+ y = 1;
+ if (y >= MAXY - 1)
+ y = MAXY - 2;
+ }
+ if ((m = mitem[x][y]) >= DEMONLORD + 4) { /* demons dispel spheres */
+ know[x][y] = 1;
+ show1cell(x, y);/* show the demon (ha ha) */
+ cursors();
+ lprintf("\nThe %s dispels the sphere!", monster[m].name);
+ beep();
+ rmsphere(x, y); /* remove any spheres that are here */
+ free(sp);
+ return (c[SPHCAST]);
+ }
+ if (m == DISENCHANTRESS) { /* disenchantress cancels spheres */
+ cursors();
+ lprintf("\nThe %s causes cancellation of the sphere!", monster[m].name);
+ beep();
+boom: sphboom(x, y); /* blow up stuff around sphere */
+ rmsphere(x, y); /* remove any spheres that are here */
+ free(sp);
+ return (c[SPHCAST]);
+ }
+ if (c[CANCELLATION]) { /* cancellation cancels spheres */
+ cursors();
+ lprcat("\nAs the cancellation takes effect, you hear a great earth shaking blast!");
+ beep();
+ goto boom;
+ }
+ if (item[x][y] == OANNIHILATION) { /* collision of spheres
+ * detonates spheres */
+ cursors();
+ lprcat("\nTwo spheres of annihilation collide! You hear a great earth shaking blast!");
+ beep();
+ rmsphere(x, y);
+ goto boom;
+ }
+ if (playerx == x && playery == y) { /* collision of sphere and
+ * player! */
+ cursors();
+ lprcat("\nYou have been enveloped by the zone of nothingness!\n");
+ beep();
+ rmsphere(x, y); /* remove any spheres that are here */
+ nap(4000);
+ died(258);
+ }
+ item[x][y] = OANNIHILATION;
+ mitem[x][y] = 0;
+ know[x][y] = 1;
+ show1cell(x, y); /* show the new sphere */
+ sp->x = x;
+ sp->y = y;
+ sp->lev = level;
+ sp->dir = dir;
+ sp->lifetime = life;
+ sp->p = 0;
+ if (spheres == 0)
+ spheres = sp; /* if first node in the sphere list */
+ else { /* add sphere to beginning of linked list */
+ sp->p = spheres;
+ spheres = sp;
+ }
+ return (++c[SPHCAST]); /* one more sphere in the world */
+}
+
+/*
+ * rmsphere(x,y) Function to delete a sphere of annihilation from list
+ * int x,y;
+ *
+ * Enter with the coordinates of the sphere (on current level)
+ * Returns the number of spheres currently in existence
+ */
+int
+rmsphere(int x, int y)
+{
+ struct sphere *sp, *sp2 = 0;
+ for (sp = spheres; sp; sp2 = sp, sp = sp->p)
+ if (level == sp->lev) /* is sphere on this level? */
+ if ((x == sp->x) && (y == sp->y)) { /* locate sphere at this
+ * location */
+ item[x][y] = mitem[x][y] = 0;
+ know[x][y] = 1;
+ show1cell(x, y); /* show the now missing
+ * sphere */
+ --c[SPHCAST];
+ if (sp == spheres) {
+ sp2 = sp;
+ spheres = sp->p;
+ free((char *) sp2);
+ } else {
+ if (sp2)
+ sp2->p = sp->p;
+ free((char *) sp);
+ }
+ break;
+ }
+ return (c[SPHCAST]); /* return number of spheres in the world */
+}
+
+/*
+ * sphboom(x,y) Function to perform the effects of a sphere detonation
+ * int x,y;
+ *
+ * Enter with the coordinates of the blast, Returns no value
+ */
+static void
+sphboom(int x, int y)
+{
+ int i, j;
+ if (c[HOLDMONST])
+ c[HOLDMONST] = 1;
+ if (c[CANCELLATION])
+ c[CANCELLATION] = 1;
+ for (j = max(1, x - 2); j < min(x + 3, MAXX - 1); j++)
+ for (i = max(1, y - 2); i < min(y + 3, MAXY - 1); i++) {
+ item[j][i] = mitem[j][i] = 0;
+ show1cell(j, i);
+ if (playerx == j && playery == i) {
+ cursors();
+ beep();
+ lprcat("\nYou were too close to the sphere!");
+ nap(3000);
+ died(283); /* player killed in explosion */
+ }
+ }
+}
+
+/*
+ * genmonst() Function to ask for monster and genocide from game
+ *
+ * This is done by setting a flag in the monster[] structure
+ */
+static void
+genmonst(void)
+{
+ int i, j;
+ cursors();
+ lprcat("\nGenocide what monster? ");
+ for (i = 0; (!isalpha(i)) && (i != ' '); i = ttgetch());
+ lprc(i);
+ for (j = 0; j < MAXMONST; j++) /* search for the monster type */
+ if (monstnamelist[j] == i) { /* have we found it? */
+ monster[j].genocided = 1; /* genocided from game */
+ lprintf(" There will be no more %s's", monster[j].name);
+ /* now wipe out monsters on this level */
+ newcavelevel(level);
+ draws(0, MAXX, 0, MAXY);
+ bot_linex();
+ return;
+ }
+ lprcat(" You sense failure!");
+}
diff --git a/larn/moreobj.c b/larn/moreobj.c
new file mode 100644
index 0000000..d36e6e1
--- /dev/null
+++ b/larn/moreobj.c
@@ -0,0 +1,296 @@
+/* $NetBSD: moreobj.c,v 1.12 2012/06/19 05:30:43 dholland Exp $ */
+
+/*
+ * moreobj.c Larn is copyrighted 1986 by Noah Morgan.
+ *
+ * Routines in this file:
+ *
+ * oaltar() othrone() ochest() ofountain()
+ */
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: moreobj.c,v 1.12 2012/06/19 05:30:43 dholland Exp $");
+#endif /* not lint */
+#include <stdlib.h>
+#include <unistd.h>
+#include "header.h"
+#include "extern.h"
+
+static void fch(int, long *);
+
+/*
+ * subroutine to process an altar object
+ */
+void
+oaltar(void)
+{
+
+ lprcat("\nDo you (p) pray (d) desecrate");
+ iopts();
+ while (1) {
+ while (1)
+ switch (ttgetch()) {
+ case 'p':
+ lprcat(" pray\nDo you (m) give money or (j) just pray? ");
+ while (1)
+ switch (ttgetch()) {
+ case 'j':
+ act_just_pray();
+ return;
+
+ case 'm':
+ act_donation_pray();
+ return;
+
+ case '\33':
+ return;
+ };
+
+ case 'd':
+ lprcat(" desecrate");
+ act_desecrate_altar();
+ return;
+
+ case 'i':
+ case '\33':
+ ignore();
+ act_ignore_altar();
+ return;
+ };
+ }
+}
+
+/*
+ subroutine to process a throne object
+ */
+void
+othrone(int arg)
+{
+
+ lprcat("\nDo you (p) pry off jewels, (s) sit down");
+ iopts();
+ while (1) {
+ while (1)
+ switch (ttgetch()) {
+ case 'p':
+ lprcat(" pry off");
+ act_remove_gems(arg);
+ return;
+
+ case 's':
+ lprcat(" sit down");
+ act_sit_throne(arg);
+ return;
+
+ case 'i':
+ case '\33':
+ ignore();
+ return;
+ };
+ }
+}
+
+void
+odeadthrone(void)
+{
+ int k;
+
+ lprcat("\nDo you (s) sit down");
+ iopts();
+ while (1) {
+ while (1)
+ switch (ttgetch()) {
+ case 's':
+ lprcat(" sit down");
+ k = rnd(101);
+ if (k < 35) {
+ lprcat("\nZaaaappp! You've been teleported!\n");
+ beep();
+ oteleport(0);
+ } else
+ lprcat("\nnothing happens");
+ return;
+
+ case 'i':
+ case '\33':
+ ignore();
+ return;
+ };
+ }
+}
+
+/*
+ subroutine to process a throne object
+ */
+void
+ochest(void)
+{
+
+ lprcat("\nDo you (t) take it, (o) try to open it");
+ iopts();
+ while (1) {
+ while (1)
+ switch (ttgetch()) {
+ case 'o':
+ lprcat(" open it");
+ act_open_chest(playerx, playery);
+ return;
+
+ case 't':
+ lprcat(" take");
+ if (take(OCHEST, iarg[playerx][playery]) == 0)
+ item[playerx][playery] = know[playerx][playery] = 0;
+ return;
+
+ case 'i':
+ case '\33':
+ ignore();
+ return;
+ };
+ }
+}
+
+/*
+ process a fountain object
+ */
+void
+ofountain(void)
+{
+
+ cursors();
+ lprcat("\nDo you (d) drink, (w) wash yourself");
+ iopts();
+ while (1)
+ switch (ttgetch()) {
+ case 'd':
+ lprcat("drink");
+ act_drink_fountain();
+ return;
+
+ case '\33':
+ case 'i':
+ ignore();
+ return;
+
+ case 'w':
+ lprcat("wash yourself");
+ act_wash_fountain();
+ return;
+ }
+}
+
+/*
+ ***
+ FCH
+ ***
+
+ subroutine to process an up/down of a character attribute for ofountain
+ */
+static void
+fch(int how, long *x)
+{
+ if (how < 0) {
+ lprcat(" went down by one!");
+ --(*x);
+ } else {
+ lprcat(" went up by one!");
+ (*x)++;
+ }
+ bottomline();
+}
+
+/*
+ a subroutine to raise or lower character levels
+ if x > 0 they are raised if x < 0 they are lowered
+ */
+void
+fntchange(int how)
+{
+ long j;
+ lprc('\n');
+ switch (rnd(9)) {
+ case 1:
+ lprcat("Your strength");
+ fch(how, &c[0]);
+ break;
+ case 2:
+ lprcat("Your intelligence");
+ fch(how, &c[1]);
+ break;
+ case 3:
+ lprcat("Your wisdom");
+ fch(how, &c[2]);
+ break;
+ case 4:
+ lprcat("Your constitution");
+ fch(how, &c[3]);
+ break;
+ case 5:
+ lprcat("Your dexterity");
+ fch(how, &c[4]);
+ break;
+ case 6:
+ lprcat("Your charm");
+ fch(how, &c[5]);
+ break;
+ case 7:
+ j = rnd(level + 1);
+ if (how < 0) {
+ lprintf("You lose %ld hit point", (long) j);
+ if (j > 1)
+ lprcat("s!");
+ else
+ lprc('!');
+ losemhp((int) j);
+ } else {
+ lprintf("You gain %ld hit point", (long) j);
+ if (j > 1)
+ lprcat("s!");
+ else
+ lprc('!');
+ raisemhp((int) j);
+ }
+ bottomline();
+ break;
+
+ case 8:
+ j = rnd(level + 1);
+ if (how > 0) {
+ lprintf("You just gained %ld spell", (long) j);
+ raisemspells((int) j);
+ if (j > 1)
+ lprcat("s!");
+ else
+ lprc('!');
+ } else {
+ lprintf("You just lost %ld spell", (long) j);
+ losemspells((int) j);
+ if (j > 1)
+ lprcat("s!");
+ else
+ lprc('!');
+ }
+ bottomline();
+ break;
+
+ case 9:
+ j = 5 * rnd((level + 1) * (level + 1));
+ if (how < 0) {
+ lprintf("You just lost %ld experience point", (long) j);
+ if (j > 1)
+ lprcat("s!");
+ else
+ lprc('!');
+ loseexperience((long) j);
+ } else {
+ lprintf("You just gained %ld experience point", (long) j);
+ if (j > 1)
+ lprcat("s!");
+ else
+ lprc('!');
+ raiseexperience((long) j);
+ }
+ break;
+ }
+ cursors();
+}
diff --git a/larn/movem.c b/larn/movem.c
new file mode 100644
index 0000000..b9745fc
--- /dev/null
+++ b/larn/movem.c
@@ -0,0 +1,447 @@
+/* $NetBSD: movem.c,v 1.9 2012/06/19 05:30:43 dholland Exp $ */
+
+/*
+ * movem.c (move monster) Larn is copyrighted 1986 by Noah Morgan.
+ *
+ * Here are the functions in this file:
+ *
+ * movemonst() Routine to move the monsters toward the player
+ * movemt(x,y) Function to move a monster at (x,y) -- must determine where
+ * mmove(x,y,xd,yd) Function to actually perform the monster movement
+ * movsphere() Function to look for and move spheres of annihilation
+ */
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: movem.c,v 1.9 2012/06/19 05:30:43 dholland Exp $");
+#endif /* not lint */
+
+#include "header.h"
+#include "extern.h"
+
+static void movemt(int, int);
+static void mmove(int, int, int, int);
+static void movsphere(void);
+
+/*
+ * movemonst() Routine to move the monsters toward the player
+ *
+ * This routine has the responsibility to determine which monsters are to
+ * move, and call movemt() to do the move.
+ * Returns no value.
+ */
+static short w1[9], w1x[9], w1y[9];
+static int tmp1, tmp2, tmp3, tmp4, distance;
+void
+movemonst(void)
+{
+ int i, j;
+ if (c[TIMESTOP])
+ return; /* no action if time is stopped */
+ if (c[HASTESELF])
+ if ((c[HASTESELF] & 1) == 0)
+ return;
+ if (spheres)
+ movsphere(); /* move the spheres of annihilation if any */
+ if (c[HOLDMONST])
+ return; /* no action if monsters are held */
+
+ if (c[AGGRAVATE]) { /* determine window of monsters to move */
+ tmp1 = playery - 5;
+ tmp2 = playery + 6;
+ tmp3 = playerx - 10;
+ tmp4 = playerx + 11;
+ distance = 40; /* depth of intelligent monster movement */
+ } else {
+ tmp1 = playery - 3;
+ tmp2 = playery + 4;
+ tmp3 = playerx - 5;
+ tmp4 = playerx + 6;
+ distance = 17; /* depth of intelligent monster movement */
+ }
+
+ if (level == 0) { /* if on outside level monsters can move in
+ * perimeter */
+ if (tmp1 < 0)
+ tmp1 = 0;
+ if (tmp2 > MAXY)
+ tmp2 = MAXY;
+ if (tmp3 < 0)
+ tmp3 = 0;
+ if (tmp4 > MAXX)
+ tmp4 = MAXX;
+ } else { /* if in a dungeon monsters can't be on the
+ * perimeter (wall there) */
+ if (tmp1 < 1)
+ tmp1 = 1;
+ if (tmp2 > MAXY - 1)
+ tmp2 = MAXY - 1;
+ if (tmp3 < 1)
+ tmp3 = 1;
+ if (tmp4 > MAXX - 1)
+ tmp4 = MAXX - 1;
+ }
+
+ for (j = tmp1; j < tmp2; j++) /* now reset monster moved flags */
+ for (i = tmp3; i < tmp4; i++)
+ moved[i][j] = 0;
+ moved[lasthx][lasthy] = 0;
+
+ if (c[AGGRAVATE] || !c[STEALTH]) { /* who gets moved? split for
+ * efficiency */
+ for (j = tmp1; j < tmp2; j++) /* look thru all locations in
+ * window */
+ for (i = tmp3; i < tmp4; i++)
+ if (mitem[i][j]) /* if there is a monster
+ * to move */
+ if (moved[i][j] == 0) /* if it has not already
+ * been moved */
+ movemt(i, j); /* go and move the
+ * monster */
+ } else { /* not aggravated and not stealth */
+ for (j = tmp1; j < tmp2; j++) /* look thru all locations in
+ * window */
+ for (i = tmp3; i < tmp4; i++)
+ if (mitem[i][j]) /* if there is a monster
+ * to move */
+ if (moved[i][j] == 0) /* if it has not already
+ * been moved */
+ if (stealth[i][j]) /* if it is asleep due
+ * to stealth */
+ movemt(i, j); /* go and move the
+ * monster */
+ }
+
+ if (mitem[lasthx][lasthy]) { /* now move monster last hit by
+ * player if not already moved */
+ if (moved[lasthx][lasthy] == 0) { /* if it has not already
+ * been moved */
+ movemt(lasthx, lasthy);
+ lasthx = w1x[0];
+ lasthy = w1y[0];
+ }
+ }
+}
+
+/*
+ * movemt(x,y) Function to move a monster at (x,y) -- must determine where
+ * int x,y;
+ *
+ * This routine is responsible for determining where one monster at (x,y) will
+ * move to. Enter with the monsters coordinates in (x,y).
+ * Returns no value.
+ */
+static int tmpitem, xl, xh, yl, yh;
+static void
+movemt(int i, int j)
+{
+ int k, m, z, tmp, xtmp, ytmp, monst;
+ switch (monst = mitem[i][j]) { /* for half speed monsters */
+ case TROGLODYTE:
+ case HOBGOBLIN:
+ case METAMORPH:
+ case XVART:
+ case INVISIBLESTALKER:
+ case ICELIZARD:
+ if ((gltime & 1) == 1)
+ return;
+ };
+
+ if (c[SCAREMONST]) { /* choose destination randomly if scared */
+ if ((xl = i + rnd(3) - 2) < 0)
+ xl = 0;
+ if (xl >= MAXX)
+ xl = MAXX - 1;
+ if ((yl = j + rnd(3) - 2) < 0)
+ yl = 0;
+ if (yl >= MAXY)
+ yl = MAXY - 1;
+ if ((tmp = item[xl][yl]) != OWALL)
+ if (mitem[xl][yl] == 0)
+ if ((mitem[i][j] != VAMPIRE) || (tmpitem != OMIRROR))
+ if (tmp != OCLOSEDDOOR)
+ mmove(i, j, xl, yl);
+ return;
+ }
+ if (monster[monst].intelligence > 10 - c[HARDGAME]) { /* if smart monster */
+ /* intelligent movement here -- first setup screen array */
+ xl = tmp3 - 2;
+ yl = tmp1 - 2;
+ xh = tmp4 + 2;
+ yh = tmp2 + 2;
+ vxy(&xl, &yl);
+ vxy(&xh, &yh);
+ for (k = yl; k < yh; k++)
+ for (m = xl; m < xh; m++) {
+ switch (item[m][k]) {
+ case OWALL:
+ case OPIT:
+ case OTRAPARROW:
+ case ODARTRAP:
+ case OCLOSEDDOOR:
+ case OTRAPDOOR:
+ case OTELEPORTER:
+ smm: screen[m][k] = 127;
+ break;
+ case OMIRROR:
+ if (mitem[m][k] == VAMPIRE)
+ goto smm;
+ default:
+ screen[m][k] = 0;
+ break;
+ };
+ }
+ screen[playerx][playery] = 1;
+
+ /*
+ * now perform proximity ripple from playerx,playery to
+ * monster
+ */
+ xl = tmp3 - 1;
+ yl = tmp1 - 1;
+ xh = tmp4 + 1;
+ yh = tmp2 + 1;
+ vxy(&xl, &yl);
+ vxy(&xh, &yh);
+ for (tmp = 1; tmp < distance; tmp++) /* only up to 20 squares
+ * away */
+ for (k = yl; k < yh; k++)
+ for (m = xl; m < xh; m++)
+ if (screen[m][k] == tmp) /* if find proximity n
+ * advance it */
+ for (z = 1; z < 9; z++) { /* go around in a circle */
+ if (screen[xtmp = m + diroffx[z]][ytmp = k + diroffy[z]] == 0)
+ screen[xtmp][ytmp] = tmp + 1;
+ if (xtmp == i && ytmp == j)
+ goto out;
+ }
+
+out: if (tmp < distance) /* did find connectivity */
+ /* now select lowest value around playerx,playery */
+ for (z = 1; z < 9; z++) /* go around in a circle */
+ if (screen[xl = i + diroffx[z]][yl = j + diroffy[z]] == tmp)
+ if (!mitem[xl][yl]) {
+ mmove(i, j, w1x[0] = xl, w1y[0] = yl);
+ return;
+ }
+ }
+ /* dumb monsters move here */
+ xl = i - 1;
+ yl = j - 1;
+ xh = i + 2;
+ yh = j + 2;
+ if (i < playerx)
+ xl++;
+ else if (i > playerx)
+ --xh;
+ if (j < playery)
+ yl++;
+ else if (j > playery)
+ --yh;
+ for (k = 0; k < 9; k++)
+ w1[k] = 10000;
+
+ for (k = xl; k < xh; k++)
+ for (m = yl; m < yh; m++) { /* for each square compute
+ * distance to player */
+ tmp = k - i + 4 + 3 * (m - j);
+ tmpitem = item[k][m];
+ if (tmpitem != OWALL || (k == playerx && m == playery))
+ if (mitem[k][m] == 0)
+ if ((mitem[i][j] != VAMPIRE) || (tmpitem != OMIRROR))
+ if (tmpitem != OCLOSEDDOOR) {
+ w1[tmp] = (playerx - k) * (playerx - k) + (playery - m) * (playery - m);
+ w1x[tmp] = k;
+ w1y[tmp] = m;
+ }
+ }
+
+ tmp = 0;
+ for (k = 1; k < 9; k++)
+ if (w1[tmp] > w1[k])
+ tmp = k;
+
+ if (w1[tmp] < 10000)
+ if ((i != w1x[tmp]) || (j != w1y[tmp]))
+ mmove(i, j, w1x[tmp], w1y[tmp]);
+}
+
+/*
+ * mmove(x,y,xd,yd) Function to actually perform the monster movement
+ * int x,y,xd,yd;
+ *
+ * Enter with the from coordinates in (x,y) and the destination coordinates
+ * in (xd,yd).
+ */
+static void
+mmove(int aa, int bb, int cc, int dd)
+{
+ int tmp, i, flag;
+ const char *who = NULL;
+
+ flag = 0; /* set to 1 if monster hit by arrow trap */
+ if ((cc == playerx) && (dd == playery)) {
+ hitplayer(aa, bb);
+ moved[aa][bb] = 1;
+ return;
+ }
+ i = item[cc][dd];
+ if ((i == OPIT) || (i == OTRAPDOOR))
+ switch (mitem[aa][bb]) {
+ case SPIRITNAGA:
+ case PLATINUMDRAGON:
+ case WRAITH:
+ case VAMPIRE:
+ case SILVERDRAGON:
+ case POLTERGEIST:
+ case DEMONLORD:
+ case DEMONLORD + 1:
+ case DEMONLORD + 2:
+ case DEMONLORD + 3:
+ case DEMONLORD + 4:
+ case DEMONLORD + 5:
+ case DEMONLORD + 6:
+ case DEMONPRINCE:
+ break;
+
+ default:
+ mitem[aa][bb] = 0; /* fell in a pit or trapdoor */
+ };
+ tmp = mitem[cc][dd] = mitem[aa][bb];
+ if (i == OANNIHILATION) {
+ if (tmp >= DEMONLORD + 3) { /* demons dispel spheres */
+ cursors();
+ lprintf("\nThe %s dispels the sphere!", monster[tmp].name);
+ rmsphere(cc, dd); /* delete the sphere */
+ } else
+ i = tmp = mitem[cc][dd] = 0;
+ }
+ stealth[cc][dd] = 1;
+ if ((hitp[cc][dd] = hitp[aa][bb]) < 0)
+ hitp[cc][dd] = 1;
+ mitem[aa][bb] = 0;
+ moved[cc][dd] = 1;
+ if (tmp == LEPRECHAUN)
+ switch (i) {
+ case OGOLDPILE:
+ case OMAXGOLD:
+ case OKGOLD:
+ case ODGOLD:
+ case ODIAMOND:
+ case ORUBY:
+ case OEMERALD:
+ case OSAPPHIRE:
+ item[cc][dd] = 0; /* leprechaun takes gold */
+ };
+
+ if (tmp == TROLL) /* if a troll regenerate him */
+ if ((gltime & 1) == 0)
+ if (monster[tmp].hitpoints > hitp[cc][dd])
+ hitp[cc][dd]++;
+
+ if (i == OTRAPARROW) { /* arrow hits monster */
+ who = "An arrow";
+ if ((hitp[cc][dd] -= rnd(10) + level) <= 0) {
+ mitem[cc][dd] = 0;
+ flag = 2;
+ } else
+ flag = 1;
+ }
+ if (i == ODARTRAP) { /* dart hits monster */
+ who = "A dart";
+ if ((hitp[cc][dd] -= rnd(6)) <= 0) {
+ mitem[cc][dd] = 0;
+ flag = 2;
+ } else
+ flag = 1;
+ }
+ if (i == OTELEPORTER) { /* monster hits teleport trap */
+ flag = 3;
+ fillmonst(mitem[cc][dd]);
+ mitem[cc][dd] = 0;
+ }
+ if (c[BLINDCOUNT])
+ return; /* if blind don't show where monsters are */
+ if (know[cc][dd] & 1) {
+ if (flag)
+ cursors();
+ switch (flag) {
+ case 1:
+ lprintf("\n%s hits the %s", who, monster[tmp].name);
+ beep();
+ break;
+ case 2:
+ lprintf("\n%s hits and kills the %s",
+ who, monster[tmp].name);
+ beep();
+ break;
+ case 3:
+ lprintf("\nThe %s gets teleported", monster[tmp].name);
+ beep();
+ break;
+ }
+ }
+ /*
+ * if (yrepcount>1) { know[aa][bb] &= 2; know[cc][dd] &= 2; return;
+ * }
+ */
+ if (know[aa][bb] & 1)
+ show1cell(aa, bb);
+ if (know[cc][dd] & 1)
+ show1cell(cc, dd);
+}
+
+/*
+ * movsphere() Function to look for and move spheres of annihilation
+ *
+ * This function works on the sphere linked list, first duplicating the list
+ * (the act of moving changes the list), then processing each sphere in order
+ * to move it. They eat anything in their way, including stairs, volcanic
+ * shafts, potions, etc, except for upper level demons, who can dispel
+ * spheres.
+ * No value is returned.
+ */
+#define SPHMAX 20 /* maximum number of spheres movsphere can
+ * handle */
+static void
+movsphere(void)
+{
+ int x, y, dir, len;
+ struct sphere *sp, *sp2;
+ struct sphere sph[SPHMAX];
+
+ /* first duplicate sphere list */
+ for (sp = 0, x = 0, sp2 = spheres; sp2; sp2 = sp2->p) /* look through sphere
+ * list */
+ if (sp2->lev == level) { /* only if this level */
+ sph[x] = *sp2;
+ sph[x++].p = 0; /* copy the struct */
+ if (x > 1)
+ sph[x - 2].p = &sph[x - 1]; /* link pointers */
+ }
+ if (x)
+ sp = sph; /* if any spheres, point to them */
+ else
+ return; /* no spheres */
+
+ for (sp = sph; sp; sp = sp->p) { /* look through sphere list */
+ x = sp->x;
+ y = sp->y;
+ if (item[x][y] != OANNIHILATION)
+ continue; /* not really there */
+ if (--(sp->lifetime) < 0) { /* has sphere run out of gas? */
+ rmsphere(x, y); /* delete sphere */
+ continue;
+ }
+ switch (rnd((int) max(7, c[INTELLIGENCE] >> 1))) { /* time to move the
+ * sphere */
+ case 1:
+ case 2: /* change direction to a random one */
+ sp->dir = rnd(8);
+ default: /* move in normal direction */
+ dir = sp->dir;
+ len = sp->lifetime;
+ rmsphere(x, y);
+ newsphere(x + diroffx[dir], y + diroffy[dir], dir, len);
+ };
+ }
+}
diff --git a/larn/nap.c b/larn/nap.c
new file mode 100644
index 0000000..1bd11ea
--- /dev/null
+++ b/larn/nap.c
@@ -0,0 +1,23 @@
+/* $NetBSD: nap.c,v 1.6 2012/06/19 05:30:43 dholland Exp $ */
+
+/* nap.c Larn is copyrighted 1986 by Noah Morgan. */
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: nap.c,v 1.6 2012/06/19 05:30:43 dholland Exp $");
+#endif /* not lint */
+
+#include <unistd.h>
+#include "header.h"
+#include "extern.h"
+
+/*
+ * routine to take a nap for n milliseconds
+ */
+void
+nap(int x)
+{
+ if (x <= 0)
+ return; /* eliminate chance for infinite loop */
+ lflush();
+ usleep(x * 1000);
+}
diff --git a/larn/object.c b/larn/object.c
new file mode 100644
index 0000000..a8e1449
--- /dev/null
+++ b/larn/object.c
@@ -0,0 +1,1336 @@
+/* $NetBSD: object.c,v 1.16 2012/06/19 05:30:43 dholland Exp $ */
+
+/* object.c Larn is copyrighted 1986 by Noah Morgan. */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: object.c,v 1.16 2012/06/19 05:30:43 dholland Exp $");
+#endif /* not lint */
+#include "header.h"
+#include "extern.h"
+
+static void finditem(int);
+static void ostairs(int);
+static void opotion(int);
+static void oscroll(int);
+static void oorb(void);
+static void opit(void);
+static void obottomless(void);
+static void oelevator(int);
+static void ostatue(void);
+static void omirror(void);
+static void obook(void);
+static void ocookie(void);
+static void ogold(int);
+static void ohome(void);
+
+/*
+ lookforobject
+
+ subroutine to look for an object and give the player his options
+ if an object was found.
+ */
+void
+lookforobject(void)
+{
+ int i, j;
+ if (c[TIMESTOP])
+ return; /* can't find objects if time is stopped */
+ i = item[playerx][playery];
+ if (i == 0)
+ return;
+ showcell(playerx, playery);
+ cursors();
+ yrepcount = 0;
+ switch (i) {
+ case OGOLDPILE:
+ case OMAXGOLD:
+ case OKGOLD:
+ case ODGOLD:
+ lprcat("\n\nYou have found some gold!");
+ ogold(i);
+ break;
+
+ case OPOTION:
+ lprcat("\n\nYou have found a magic potion");
+ i = iarg[playerx][playery];
+ if (potionname[i][0] != 0)
+ lprintf(" of%s", potionname[i]);
+ opotion(i);
+ break;
+
+ case OSCROLL:
+ lprcat("\n\nYou have found a magic scroll");
+ i = iarg[playerx][playery];
+ if (scrollname[i][0] != 0)
+ lprintf(" of%s", scrollname[i]);
+ oscroll(i);
+ break;
+
+ case OALTAR:
+ if (nearbymonst())
+ return;
+ lprcat("\n\nThere is a Holy Altar here!");
+ oaltar();
+ break;
+
+ case OBOOK:
+ lprcat("\n\nYou have found a book.");
+ obook();
+ break;
+
+ case OCOOKIE:
+ lprcat("\n\nYou have found a fortune cookie.");
+ ocookie();
+ break;
+
+ case OTHRONE:
+ if (nearbymonst())
+ return;
+ lprintf("\n\nThere is %s here!", objectname[i]);
+ othrone(0);
+ break;
+
+ case OTHRONE2:
+ if (nearbymonst())
+ return;
+ lprintf("\n\nThere is %s here!", objectname[i]);
+ othrone(1);
+ break;
+
+ case ODEADTHRONE:
+ lprintf("\n\nThere is %s here!", objectname[i]);
+ odeadthrone();
+ break;
+
+ case OORB:
+ lprcat("\n\nYou have found the Orb!!!!!");
+ oorb();
+ break;
+
+ case OPIT:
+ lprcat("\n\nYou're standing at the top of a pit.");
+ opit();
+ break;
+
+ case OSTAIRSUP:
+ lprcat("\n\nThere is a circular staircase here");
+ ostairs(1); /* up */
+ break;
+
+ case OELEVATORUP:
+ lprcat("\n\nYou feel heavy for a moment, but the feeling disappears");
+ oelevator(1); /* up */
+ break;
+
+ case OFOUNTAIN:
+ if (nearbymonst())
+ return;
+ lprcat("\n\nThere is a fountain here");
+ ofountain();
+ break;
+
+ case OSTATUE:
+ if (nearbymonst())
+ return;
+ lprcat("\n\nYou are standing in front of a statue");
+ ostatue();
+ break;
+
+ case OCHEST:
+ lprcat("\n\nThere is a chest here");
+ ochest();
+ break;
+
+ case OIVTELETRAP:
+ if (rnd(11) < 6)
+ return;
+ item[playerx][playery] = OTELEPORTER;
+ know[playerx][playery] = 1;
+
+ case OTELEPORTER:
+ lprcat("\nZaaaappp! You've been teleported!\n");
+ beep();
+ nap(3000);
+ oteleport(0);
+ break;
+
+ case OSCHOOL:
+ if (nearbymonst())
+ return;
+ lprcat("\n\nYou have found the College of Larn.");
+ lprcat("\nDo you (g) go inside, or (i) stay here? ");
+ i = 0;
+ while ((i != 'g') && (i != 'i') && (i != '\33'))
+ i = ttgetch();
+ if (i == 'g') {
+ oschool(); /* the college of larn */
+ } else
+ lprcat(" stay here");
+ break;
+
+ case OMIRROR:
+ if (nearbymonst())
+ return;
+ lprcat("\n\nThere is a mirror here");
+ omirror();
+ break;
+
+ case OBANK2:
+ case OBANK:
+ if (nearbymonst())
+ return;
+ if (i == OBANK)
+ lprcat("\n\nYou have found the bank of Larn.");
+ else
+ lprcat("\n\nYou have found a branch office of the bank of Larn.");
+ lprcat("\nDo you (g) go inside, or (i) stay here? ");
+ j = 0;
+ while ((j != 'g') && (j != 'i') && (j != '\33'))
+ j = ttgetch();
+ if (j == 'g') {
+ if (i == OBANK)
+ obank();
+ else
+ obank2(); /* the bank of larn */
+ } else
+ lprcat(" stay here");
+ break;
+
+ case ODEADFOUNTAIN:
+ if (nearbymonst())
+ return;
+ lprcat("\n\nThere is a dead fountain here");
+ break;
+
+ case ODNDSTORE:
+ if (nearbymonst())
+ return;
+ lprcat("\n\nThere is a DND store here.");
+ lprcat("\nDo you (g) go inside, or (i) stay here? ");
+ i = 0;
+ while ((i != 'g') && (i != 'i') && (i != '\33'))
+ i = ttgetch();
+ if (i == 'g')
+ dndstore(); /* the dnd adventurers store */
+ else
+ lprcat(" stay here");
+ break;
+
+ case OSTAIRSDOWN:
+ lprcat("\n\nThere is a circular staircase here");
+ ostairs(-1); /* down */
+ break;
+
+ case OELEVATORDOWN:
+ lprcat("\n\nYou feel light for a moment, but the feeling disappears");
+ oelevator(-1); /* down */
+ break;
+
+ case OOPENDOOR:
+ lprintf("\n\nYou have found %s", objectname[i]);
+ lprcat("\nDo you (c) close it");
+ iopts();
+ i = 0;
+ while ((i != 'c') && (i != 'i') && (i != '\33'))
+ i = ttgetch();
+ if ((i == '\33') || (i == 'i')) {
+ ignore();
+ break;
+ }
+ lprcat("close");
+ forget();
+ item[playerx][playery] = OCLOSEDDOOR;
+ iarg[playerx][playery] = 0;
+ playerx = lastpx;
+ playery = lastpy;
+ break;
+
+ case OCLOSEDDOOR:
+ lprintf("\n\nYou have found %s", objectname[i]);
+ lprcat("\nDo you (o) try to open it");
+ iopts();
+ i = 0;
+ while ((i != 'o') && (i != 'i') && (i != '\33'))
+ i = ttgetch();
+ if ((i == '\33') || (i == 'i')) {
+ ignore();
+ playerx = lastpx;
+ playery = lastpy;
+ break;
+ } else {
+ lprcat("open");
+ if (rnd(11) < 7) {
+ switch (iarg[playerx][playery]) {
+ case 6:
+ c[AGGRAVATE] += rnd(400);
+ break;
+
+ case 7:
+ lprcat("\nYou are jolted by an electric shock ");
+ lastnum = 274;
+ losehp(rnd(20));
+ bottomline();
+ break;
+
+ case 8:
+ loselevel();
+ break;
+
+ case 9:
+ lprcat("\nYou suddenly feel weaker ");
+ if (c[STRENGTH] > 3)
+ c[STRENGTH]--;
+ bottomline();
+ break;
+
+ default:
+ break;
+ }
+ playerx = lastpx;
+ playery = lastpy;
+ } else {
+ forget();
+ item[playerx][playery] = OOPENDOOR;
+ }
+ }
+ break;
+
+ case OENTRANCE:
+ lprcat("\nYou have found ");
+ lprcat(objectname[OENTRANCE]);
+ lprcat("\nDo you (g) go inside");
+ iopts();
+ i = 0;
+ while ((i != 'g') && (i != 'i') && (i != '\33'))
+ i = ttgetch();
+ if (i == 'g') {
+ newcavelevel(1);
+ playerx = 33;
+ playery = MAXY - 2;
+ item[33][MAXY - 1] = know[33][MAXY - 1] = mitem[33][MAXY - 1] = 0;
+ draws(0, MAXX, 0, MAXY);
+ bot_linex();
+ return;
+ } else
+ ignore();
+ break;
+
+ case OVOLDOWN:
+ lprcat("\nYou have found ");
+ lprcat(objectname[OVOLDOWN]);
+ lprcat("\nDo you (c) climb down");
+ iopts();
+ i = 0;
+ while ((i != 'c') && (i != 'i') && (i != '\33'))
+ i = ttgetch();
+ if ((i == '\33') || (i == 'i')) {
+ ignore();
+ break;
+ }
+ if (level != 0) {
+ lprcat("\nThe shaft only extends 5 feet downward!");
+ return;
+ }
+ if (packweight() > 45 + 3 * (c[STRENGTH] + c[STREXTRA])) {
+ lprcat("\nYou slip and fall down the shaft");
+ beep();
+ lastnum = 275;
+ losehp(30 + rnd(20));
+ bottomhp();
+ } else
+ lprcat("climb down");
+ nap(3000);
+ newcavelevel(MAXLEVEL);
+ for (i = 0; i < MAXY; i++)
+ for (j = 0; j < MAXX; j++) /* put player near
+ * volcano shaft */
+ if (item[j][i] == OVOLUP) {
+ playerx = j;
+ playery = i;
+ j = MAXX;
+ i = MAXY;
+ positionplayer();
+ }
+ draws(0, MAXX, 0, MAXY);
+ bot_linex();
+ return;
+
+ case OVOLUP:
+ lprcat("\nYou have found ");
+ lprcat(objectname[OVOLUP]);
+ lprcat("\nDo you (c) climb up");
+ iopts();
+ i = 0;
+ while ((i != 'c') && (i != 'i') && (i != '\33'))
+ i = ttgetch();
+ if ((i == '\33') || (i == 'i')) {
+ ignore();
+ break;
+ }
+ if (level != 11) {
+ lprcat("\nThe shaft only extends 8 feet upwards before you find a blockage!");
+ return;
+ }
+ if (packweight() > 45 + 5 * (c[STRENGTH] + c[STREXTRA])) {
+ lprcat("\nYou slip and fall down the shaft");
+ beep();
+ lastnum = 275;
+ losehp(15 + rnd(20));
+ bottomhp();
+ return;
+ }
+ lprcat("climb up");
+ lflush();
+ nap(3000);
+ newcavelevel(0);
+ for (i = 0; i < MAXY; i++)
+ for (j = 0; j < MAXX; j++) /* put player near
+ * volcano shaft */
+ if (item[j][i] == OVOLDOWN) {
+ playerx = j;
+ playery = i;
+ j = MAXX;
+ i = MAXY;
+ positionplayer();
+ }
+ draws(0, MAXX, 0, MAXY);
+ bot_linex();
+ return;
+
+ case OTRAPARROWIV:
+ if (rnd(17) < 13)
+ return; /* for an arrow trap */
+ item[playerx][playery] = OTRAPARROW;
+ know[playerx][playery] = 0;
+
+ case OTRAPARROW:
+ lprcat("\nYou are hit by an arrow");
+ beep(); /* for an arrow trap */
+ lastnum = 259;
+ losehp(rnd(10) + level);
+ bottomhp();
+ return;
+
+ case OIVDARTRAP:
+ if (rnd(17) < 13)
+ return; /* for a dart trap */
+ item[playerx][playery] = ODARTRAP;
+ know[playerx][playery] = 0;
+
+ case ODARTRAP:
+ lprcat("\nYou are hit by a dart");
+ beep(); /* for a dart trap */
+ lastnum = 260;
+ losehp(rnd(5));
+ if ((--c[STRENGTH]) < 3)
+ c[STRENGTH] = 3;
+ bottomline();
+ return;
+
+ case OIVTRAPDOOR:
+ if (rnd(17) < 13)
+ return; /* for a trap door */
+ item[playerx][playery] = OTRAPDOOR;
+ know[playerx][playery] = 1;
+
+ case OTRAPDOOR:
+ lastnum = 272; /* a trap door */
+ if ((level == MAXLEVEL - 1) || (level == MAXLEVEL + MAXVLEVEL - 1)) {
+ lprcat("\nYou fell through a bottomless trap door!");
+ beep();
+ nap(3000);
+ died(271);
+ }
+ lprcat("\nYou fall through a trap door!");
+ beep(); /* for a trap door */
+ losehp(rnd(5 + level));
+ nap(2000);
+ newcavelevel(level + 1);
+ draws(0, MAXX, 0, MAXY);
+ bot_linex();
+ return;
+
+
+ case OTRADEPOST:
+ if (nearbymonst())
+ return;
+ lprcat("\nYou have found the Larn trading Post.");
+ lprcat("\nDo you (g) go inside, or (i) stay here? ");
+ i = 0;
+ while ((i != 'g') && (i != 'i') && (i != '\33'))
+ i = ttgetch();
+ if (i == 'g')
+ otradepost();
+ else
+ lprcat("stay here");
+ return;
+
+ case OHOME:
+ if (nearbymonst())
+ return;
+ lprcat("\nYou have found your way home.");
+ lprcat("\nDo you (g) go inside, or (i) stay here? ");
+ i = 0;
+ while ((i != 'g') && (i != 'i') && (i != '\33'))
+ i = ttgetch();
+ if (i == 'g')
+ ohome();
+ else
+ lprcat("stay here");
+ return;
+
+ case OWALL:
+ break;
+
+ case OANNIHILATION:
+ died(283);
+ return; /* annihilated by sphere of annihilation */
+
+ case OLRS:
+ if (nearbymonst())
+ return;
+ lprcat("\n\nThere is an LRS office here.");
+ lprcat("\nDo you (g) go inside, or (i) stay here? ");
+ i = 0;
+ while ((i != 'g') && (i != 'i') && (i != '\33'))
+ i = ttgetch();
+ if (i == 'g')
+ olrs(); /* the larn revenue service */
+ else
+ lprcat(" stay here");
+ break;
+
+ default:
+ finditem(i);
+ break;
+ };
+}
+
+/*
+ function to say what object we found and ask if player wants to take it
+ */
+static void
+finditem(int theitem)
+{
+ int tmp, i;
+ lprintf("\n\nYou have found %s ", objectname[theitem]);
+ tmp = iarg[playerx][playery];
+ switch (theitem) {
+ case ODIAMOND:
+ case ORUBY:
+ case OEMERALD:
+ case OSAPPHIRE:
+ case OSPIRITSCARAB:
+ case OORBOFDRAGON:
+ case OCUBEofUNDEAD:
+ case ONOTHEFT:
+ break;
+
+ default:
+ if (tmp > 0)
+ lprintf("+ %ld", (long) tmp);
+ else if (tmp < 0)
+ lprintf(" %ld", (long) tmp);
+ }
+ lprcat("\nDo you want to (t) take it");
+ iopts();
+ i = 0;
+ while (i != 't' && i != 'i' && i != '\33')
+ i = ttgetch();
+ if (i == 't') {
+ lprcat("take");
+ if (take(theitem, tmp) == 0)
+ forget();
+ return;
+ }
+ ignore();
+}
+
+
+
+/*
+ subroutine to process the stair cases
+ if dir > 0 the up else down
+ */
+static void
+ostairs(int dir)
+{
+ int k;
+ lprcat("\nDo you (s) stay here ");
+ if (dir > 0)
+ lprcat("(u) go up ");
+ else
+ lprcat("(d) go down ");
+ lprcat("or (f) kick stairs? ");
+
+ while (1)
+ switch (ttgetch()) {
+ case '\33':
+ case 's':
+ case 'i':
+ lprcat("stay here");
+ return;
+
+ case 'f':
+ lprcat("kick stairs");
+ if (rnd(2) == 1)
+ lprcat("\nI hope you feel better. Showing anger rids you of frustration.");
+ else {
+ k = rnd((level + 1) << 1);
+ lprintf("\nYou hurt your foot dumb dumb! You suffer %ld hit points", (long) k);
+ lastnum = 276;
+ losehp(k);
+ bottomline();
+ }
+ return;
+
+ case 'u':
+ lprcat("go up");
+ if (dir < 0)
+ lprcat("\nThe stairs don't go up!");
+ else if (level >= 2 && level != 11) {
+ k = level;
+ newcavelevel(level - 1);
+ draws(0, MAXX, 0, MAXY);
+ bot_linex();
+ } else
+ lprcat("\nThe stairs lead to a dead end!");
+ return;
+
+ case 'd':
+ lprcat("go down");
+ if (dir > 0)
+ lprcat("\nThe stairs don't go down!");
+ else if (level != 0 && level != 10 && level != 13) {
+ k = level;
+ newcavelevel(level + 1);
+ draws(0, MAXX, 0, MAXY);
+ bot_linex();
+ } else
+ lprcat("\nThe stairs lead to a dead end!");
+ return;
+ };
+}
+
+
+
+/*
+ subroutine to handle a teleport trap +/- 1 level maximum
+ */
+void
+oteleport(int err)
+{
+ int tmp;
+ if (err)
+ if (rnd(151) < 3)
+ died(264); /* stuck in a rock */
+ c[TELEFLAG] = 1; /* show ?? on bottomline if been teleported */
+ if (level == 0)
+ tmp = 0;
+ else if (level < MAXLEVEL) {
+ tmp = rnd(5) + level - 3;
+ if (tmp >= MAXLEVEL)
+ tmp = MAXLEVEL - 1;
+ if (tmp < 1)
+ tmp = 1;
+ } else {
+ tmp = rnd(3) + level - 2;
+ if (tmp >= MAXLEVEL + MAXVLEVEL)
+ tmp = MAXLEVEL + MAXVLEVEL - 1;
+ if (tmp < MAXLEVEL)
+ tmp = MAXLEVEL;
+ }
+ playerx = rnd(MAXX - 2);
+ playery = rnd(MAXY - 2);
+ if (level != tmp)
+ newcavelevel(tmp);
+ positionplayer();
+ draws(0, MAXX, 0, MAXY);
+ bot_linex();
+}
+
+
+/*
+ function to process a potion
+ */
+static void
+opotion(int pot)
+{
+ lprcat("\nDo you (d) drink it, (t) take it");
+ iopts();
+ while (1)
+ switch (ttgetch()) {
+ case '\33':
+ case 'i':
+ ignore();
+ return;
+
+ case 'd':
+ lprcat("drink\n");
+ forget(); /* destroy potion */
+ quaffpotion(pot);
+ return;
+
+ case 't':
+ lprcat("take\n");
+ if (take(OPOTION, pot) == 0)
+ forget();
+ return;
+ };
+}
+
+/*
+ function to drink a potion
+ */
+void
+quaffpotion(int pot)
+{
+ int i, j, k;
+ if (pot < 0 || pot >= MAXPOTION)
+ return; /* check for within bounds */
+ potionname[pot] = potionhide[pot];
+ switch (pot) {
+ case 9:
+ lprcat("\nYou feel greedy . . .");
+ nap(2000);
+ for (i = 0; i < MAXY; i++)
+ for (j = 0; j < MAXX; j++)
+ if ((item[j][i] == OGOLDPILE) || (item[j][i] == OMAXGOLD)) {
+ know[j][i] = 1;
+ show1cell(j, i);
+ }
+ showplayer();
+ return;
+
+ case 19:
+ lprcat("\nYou feel greedy . . .");
+ nap(2000);
+ for (i = 0; i < MAXY; i++)
+ for (j = 0; j < MAXX; j++) {
+ k = item[j][i];
+ if ((k == ODIAMOND) || (k == ORUBY) || (k == OEMERALD) || (k == OMAXGOLD)
+ || (k == OSAPPHIRE) || (k == OLARNEYE) || (k == OGOLDPILE)) {
+ know[j][i] = 1;
+ show1cell(j, i);
+ }
+ }
+ showplayer();
+ return;
+
+ case 20:
+ c[HP] = c[HPMAX];
+ break; /* instant healing */
+
+ case 1:
+ lprcat("\nYou feel better");
+ if (c[HP] == c[HPMAX])
+ raisemhp(1);
+ else if ((c[HP] += rnd(20) + 20 + c[LEVEL]) > c[HPMAX])
+ c[HP] = c[HPMAX];
+ break;
+
+ case 2:
+ lprcat("\nSuddenly, you feel much more skillful!");
+ raiselevel();
+ raisemhp(1);
+ return;
+
+ case 3:
+ lprcat("\nYou feel strange for a moment");
+ c[rund(6)]++;
+ break;
+
+ case 4:
+ lprcat("\nYou feel more self confident!");
+ c[WISDOM] += rnd(2);
+ break;
+
+ case 5:
+ lprcat("\nWow! You feel great!");
+ if (c[STRENGTH] < 12)
+ c[STRENGTH] = 12;
+ else
+ c[STRENGTH]++;
+ break;
+
+ case 6:
+ lprcat("\nYour charm went up by one!");
+ c[CHARISMA]++;
+ break;
+
+ case 8:
+ lprcat("\nYour intelligence went up by one!");
+ c[INTELLIGENCE]++;
+ break;
+
+ case 10:
+ for (i = 0; i < MAXY; i++)
+ for (j = 0; j < MAXX; j++)
+ if (mitem[j][i]) {
+ know[j][i] = 1;
+ show1cell(j, i);
+ }
+ /* monster detection */ return;
+
+ case 12:
+ lprcat("\nThis potion has no taste to it");
+ return;
+
+ case 15:
+ lprcat("\nWOW!!! You feel Super-fantastic!!!");
+ if (c[HERO] == 0)
+ for (i = 0; i < 6; i++)
+ c[i] += 11;
+ c[HERO] += 250;
+ break;
+
+ case 16:
+ lprcat("\nYou have a greater intestinal constitude!");
+ c[CONSTITUTION]++;
+ break;
+
+ case 17:
+ lprcat("\nYou now have incredibly bulging muscles!!!");
+ if (c[GIANTSTR] == 0)
+ c[STREXTRA] += 21;
+ c[GIANTSTR] += 700;
+ break;
+
+ case 18:
+ lprcat("\nYou feel a chill run up your spine!");
+ c[FIRERESISTANCE] += 1000;
+ break;
+
+ case 0:
+ lprcat("\nYou fall asleep. . .");
+ i = rnd(11) - (c[CONSTITUTION] >> 2) + 2;
+ while (--i > 0) {
+ parse2();
+ nap(1000);
+ }
+ cursors();
+ lprcat("\nYou woke up!");
+ return;
+
+ case 7:
+ lprcat("\nYou become dizzy!");
+ if (--c[STRENGTH] < 3)
+ c[STRENGTH] = 3;
+ break;
+
+ case 11:
+ lprcat("\nYou stagger for a moment . .");
+ for (i = 0; i < MAXY; i++)
+ for (j = 0; j < MAXX; j++)
+ know[j][i] = 0;
+ nap(2000);
+ draws(0, MAXX, 0, MAXY); /* potion of forgetfulness */
+ return;
+
+ case 13:
+ lprcat("\nYou can't see anything!"); /* blindness */
+ c[BLINDCOUNT] += 500;
+ return;
+
+ case 14:
+ lprcat("\nYou feel confused");
+ c[CONFUSE] += 20 + rnd(9);
+ return;
+
+ case 21:
+ lprcat("\nYou don't seem to be affected");
+ return; /* cure dianthroritis */
+
+ case 22:
+ lprcat("\nYou feel a sickness engulf you"); /* poison */
+ c[HALFDAM] += 200 + rnd(200);
+ return;
+
+ case 23:
+ lprcat("\nYou feel your vision sharpen"); /* see invisible */
+ c[SEEINVISIBLE] += rnd(1000) + 400;
+ monstnamelist[INVISIBLESTALKER] = 'I';
+ return;
+ };
+ bottomline(); /* show new stats */
+ return;
+}
+
+
+/*
+ function to process a magic scroll
+ */
+static void
+oscroll(int typ)
+{
+ lprcat("\nDo you ");
+ if (c[BLINDCOUNT] == 0)
+ lprcat("(r) read it, ");
+ lprcat("(t) take it");
+ iopts();
+ while (1)
+ switch (ttgetch()) {
+ case '\33':
+ case 'i':
+ ignore();
+ return;
+
+ case 'r':
+ if (c[BLINDCOUNT])
+ break;
+ lprcat("read");
+ forget();
+ if (typ == 2 || typ == 15) {
+ show1cell(playerx, playery);
+ cursors();
+ }
+ /* destroy it */ read_scroll(typ);
+ return;
+
+ case 't':
+ lprcat("take");
+ if (take(OSCROLL, typ) == 0)
+ forget(); /* destroy it */
+ return;
+ };
+}
+
+/*
+ data for the function to read a scroll
+ */
+static int xh, yh, yl, xl;
+static u_char curse[] = {
+ BLINDCOUNT, CONFUSE, AGGRAVATE, HASTEMONST, ITCHING,
+ LAUGHING, DRAINSTRENGTH, CLUMSINESS, INFEEBLEMENT, HALFDAM
+};
+
+static u_char exten[] = {
+ PROTECTIONTIME, DEXCOUNT, STRCOUNT, CHARMCOUNT, INVISIBILITY,
+ CANCELLATION, HASTESELF, GLOBE, SCAREMONST, HOLDMONST, TIMESTOP
+};
+
+static u_char time_change[] = {
+ HASTESELF, HERO, ALTPRO, PROTECTIONTIME, DEXCOUNT, STRCOUNT,
+ GIANTSTR, CHARMCOUNT, INVISIBILITY, CANCELLATION, HASTESELF,
+ AGGRAVATE, SCAREMONST, STEALTH, AWARENESS, HOLDMONST,
+ HASTEMONST, FIRERESISTANCE, GLOBE, SPIRITPRO, UNDEADPRO,
+ HALFDAM, SEEINVISIBLE, ITCHING, CLUMSINESS, WTW
+};
+
+/*
+ * function to adjust time when time warping and taking courses in school
+ */
+void
+adjusttime(long tim)
+{
+ int j;
+ for (j = 0; j < 26; j++)/* adjust time related parameters */
+ if (c[time_change[j]])
+ if ((c[time_change[j]] -= tim) < 1)
+ c[time_change[j]] = 1;
+ regen();
+}
+
+/*
+ function to read a scroll
+ */
+void
+read_scroll(int typ)
+{
+ int i, j;
+ if (typ < 0 || typ >= MAXSCROLL)
+ return; /* be sure we are within bounds */
+ scrollname[typ] = scrollhide[typ];
+ switch (typ) {
+ case 0:
+ lprcat("\nYour armor glows for a moment");
+ enchantarmor();
+ return;
+
+ case 1:
+ lprcat("\nYour weapon glows for a moment");
+ enchweapon();
+ return; /* enchant weapon */
+
+ case 2:
+ lprcat("\nYou have been granted enlightenment!");
+ yh = min(playery + 7, MAXY);
+ xh = min(playerx + 25, MAXX);
+ yl = max(playery - 7, 0);
+ xl = max(playerx - 25, 0);
+ for (i = yl; i < yh; i++)
+ for (j = xl; j < xh; j++)
+ know[j][i] = 1;
+ nap(2000);
+ draws(xl, xh, yl, yh);
+ return;
+
+ case 3:
+ lprcat("\nThis scroll seems to be blank");
+ return;
+
+ case 4:
+ createmonster(makemonst(level + 1));
+ return; /* this one creates a monster */
+
+ case 5:
+ something(level); /* create artifact */
+ return;
+
+ case 6:
+ c[AGGRAVATE] += 800;
+ return; /* aggravate monsters */
+
+ case 7:
+ gltime += (i = rnd(1000) - 850); /* time warp */
+ if (i >= 0)
+ lprintf("\nYou went forward in time by %ld mobuls", (long) ((i + 99) / 100));
+ else
+ lprintf("\nYou went backward in time by %ld mobuls", (long) (-(i + 99) / 100));
+ adjusttime((long) i); /* adjust time for time warping */
+ return;
+
+ case 8:
+ oteleport(0);
+ return; /* teleportation */
+
+ case 9:
+ c[AWARENESS] += 1800;
+ return; /* expanded awareness */
+
+ case 10:
+ c[HASTEMONST] += rnd(55) + 12;
+ return; /* haste monster */
+
+ case 11:
+ for (i = 0; i < MAXY; i++)
+ for (j = 0; j < MAXX; j++)
+ if (mitem[j][i])
+ hitp[j][i] = monster[mitem[j][i]].hitpoints;
+ return; /* monster healing */
+ case 12:
+ c[SPIRITPRO] += 300 + rnd(200);
+ bottomline();
+ return; /* spirit protection */
+
+ case 13:
+ c[UNDEADPRO] += 300 + rnd(200);
+ bottomline();
+ return; /* undead protection */
+
+ case 14:
+ c[STEALTH] += 250 + rnd(250);
+ bottomline();
+ return; /* stealth */
+
+ case 15:
+ lprcat("\nYou have been granted enlightenment!"); /* magic mapping */
+ for (i = 0; i < MAXY; i++)
+ for (j = 0; j < MAXX; j++)
+ know[j][i] = 1;
+ nap(2000);
+ draws(0, MAXX, 0, MAXY);
+ return;
+
+ case 16:
+ c[HOLDMONST] += 30;
+ bottomline();
+ return; /* hold monster */
+
+ case 17:
+ for (i = 0; i < 26; i++) /* gem perfection */
+ switch (iven[i]) {
+ case ODIAMOND:
+ case ORUBY:
+ case OEMERALD:
+ case OSAPPHIRE:
+ j = ivenarg[i];
+ j &= 255;
+ j <<= 1;
+ if (j > 255)
+ j = 255; /* double value */
+ ivenarg[i] = j;
+ break;
+ }
+ break;
+
+ case 18:
+ for (i = 0; i < 11; i++)
+ c[exten[i]] <<= 1; /* spell extension */
+ break;
+
+ case 19:
+ for (i = 0; i < 26; i++) { /* identify */
+ if (iven[i] == OPOTION)
+ potionname[ivenarg[i]] = potionhide[ivenarg[i]];
+ if (iven[i] == OSCROLL)
+ scrollname[ivenarg[i]] = scrollhide[ivenarg[i]];
+ }
+ break;
+
+ case 20:
+ for (i = 0; i < 10; i++) /* remove curse */
+ if (c[curse[i]])
+ c[curse[i]] = 1;
+ break;
+
+ case 21:
+ annihilate();
+ break; /* scroll of annihilation */
+
+ case 22:
+ godirect(22, 150, "The ray hits the %s", 0, ' '); /* pulverization */
+ break;
+ case 23:
+ c[LIFEPROT]++;
+ break; /* life protection */
+ };
+}
+
+
+
+static void
+oorb(void)
+{
+}
+
+static void
+opit(void)
+{
+ int i;
+ if (rnd(101) < 81) {
+ if (rnd(70) > 9 * c[DEXTERITY] - packweight() || rnd(101) < 5) {
+ if (level == MAXLEVEL - 1)
+ obottomless();
+ else if (level == MAXLEVEL + MAXVLEVEL - 1)
+ obottomless();
+ else {
+ if (rnd(101) < 20) {
+ i = 0;
+ lprcat("\nYou fell into a pit! Your fall is cushioned by an unknown force\n");
+ } else {
+ i = rnd(level * 3 + 3);
+ lprintf("\nYou fell into a pit! You suffer %ld hit points damage", (long) i);
+ lastnum = 261; /* if he dies scoreboard
+ * will say so */
+ }
+ losehp(i);
+ nap(2000);
+ newcavelevel(level + 1);
+ draws(0, MAXX, 0, MAXY);
+ }
+ }
+ }
+}
+
+static void
+obottomless(void)
+{
+ lprcat("\nYou fell into a bottomless pit!");
+ beep();
+ nap(3000);
+ died(262);
+}
+
+static void
+oelevator(int dir)
+{
+#ifdef lint
+ int x;
+ x = dir;
+ dir = x;
+#endif /* lint */
+}
+
+static void
+ostatue(void)
+{
+}
+
+static void
+omirror(void)
+{
+}
+
+static void
+obook(void)
+{
+ lprcat("\nDo you ");
+ if (c[BLINDCOUNT] == 0)
+ lprcat("(r) read it, ");
+ lprcat("(t) take it");
+ iopts();
+ while (1)
+ switch (ttgetch()) {
+ case '\33':
+ case 'i':
+ ignore();
+ return;
+
+ case 'r':
+ if (c[BLINDCOUNT])
+ break;
+ lprcat("read");
+ /* no more book */ readbook(iarg[playerx][playery]);
+ forget();
+ return;
+
+ case 't':
+ lprcat("take");
+ if (take(OBOOK, iarg[playerx][playery]) == 0)
+ forget(); /* no more book */
+ return;
+ };
+}
+
+/*
+ function to read a book
+ */
+void
+readbook(int lev)
+{
+ int i, tmp;
+ if (lev <= 3)
+ i = rund((tmp = splev[lev]) ? tmp : 1);
+ else
+ i = rnd((tmp = splev[lev] - 9) ? tmp : 1) + 9;
+ spelknow[i] = 1;
+ lprintf("\nSpell \"%s\": %s\n%s", spelcode[i], spelname[i], speldescript[i]);
+ if (rnd(10) == 4) {
+ lprcat("\nYour int went up by one!");
+ c[INTELLIGENCE]++;
+ bottomline();
+ }
+}
+
+static void
+ocookie(void)
+{
+ const char *p;
+
+ lprcat("\nDo you (e) eat it, (t) take it");
+ iopts();
+ while (1)
+ switch (ttgetch()) {
+ case '\33':
+ case 'i':
+ ignore();
+ return;
+
+ case 'e':
+ lprcat("eat\nThe cookie tasted good.");
+ forget(); /* no more cookie */
+ if (c[BLINDCOUNT])
+ return;
+ if (!(p = fortune()))
+ return;
+ lprcat(" A message inside the cookie reads:\n");
+ lprcat(p);
+ return;
+
+ case 't':
+ lprcat("take");
+ if (take(OCOOKIE, 0) == 0)
+ forget(); /* no more book */
+ return;
+ };
+}
+
+
+/*
+ * routine to pick up some gold -- if arg==OMAXGOLD then the pile is worth
+ * 100* the argument
+ */
+static void
+ogold(int arg)
+{
+ long i;
+ i = iarg[playerx][playery];
+ if (arg == OMAXGOLD)
+ i *= 100;
+ else if (arg == OKGOLD)
+ i *= 1000;
+ else if (arg == ODGOLD)
+ i *= 10;
+ lprintf("\nIt is worth %ld!", (long) i);
+ c[GOLD] += i;
+ bottomgold();
+ item[playerx][playery] = know[playerx][playery] = 0; /* destroy gold */
+}
+
+static void
+ohome(void)
+{
+ int i;
+ nosignal = 1; /* disable signals */
+ for (i = 0; i < 26; i++)
+ if (iven[i] == OPOTION)
+ if (ivenarg[i] == 21) {
+ iven[i] = 0; /* remove the potion of cure
+ * dianthroritis from
+ * inventory */
+ clear();
+ lprcat("Congratulations. You found a potion of cure dianthroritis.\n");
+ lprcat("\nFrankly, No one thought you could do it. Boy! Did you surprise them!\n");
+ if (gltime > TIMELIMIT) {
+ lprcat("\nThe doctor has the sad duty to inform you that your daughter died!\n");
+ lprcat("You didn't make it in time. In your agony, you kill the doctor,\nyour wife, and yourself! Too bad!\n");
+ nap(5000);
+ died(269);
+ } else {
+ lprcat("\nThe doctor is now administering the potion, and in a few moments\n");
+ lprcat("Your daughter should be well on her way to recovery.\n");
+ nap(6000);
+ lprcat("\nThe potion is");
+ nap(3000);
+ lprcat(" working! The doctor thinks that\n");
+ lprcat("your daughter will recover in a few days. Congratulations!\n");
+ beep();
+ nap(5000);
+ died(263);
+ }
+ }
+ while (1) {
+ clear();
+ lprintf("Welcome home %s. Latest word from the doctor is not good.\n", logname);
+
+ if (gltime > TIMELIMIT) {
+ lprcat("\nThe doctor has the sad duty to inform you that your daughter died!\n");
+ lprcat("You didn't make it in time. In your agony, you kill the doctor,\nyour wife, and yourself! Too bad!\n");
+ nap(5000);
+ died(269);
+ }
+ lprcat("\nThe diagnosis is confirmed as dianthroritis. He guesses that\n");
+ lprintf("your daughter has only %ld mobuls left in this world. It's up to you,\n", (long) ((TIMELIMIT - gltime + 99) / 100));
+ lprintf("%s, to find the only hope for your daughter, the very rare\n", logname);
+ lprcat("potion of cure dianthroritis. It is rumored that only deep in the\n");
+ lprcat("depths of the caves can this potion be found.\n\n\n");
+ lprcat("\n ----- press ");
+ standout("return");
+ lprcat(" to continue, ");
+ standout("escape");
+ lprcat(" to leave ----- ");
+ i = ttgetch();
+ while (i != '\33' && i != '\n')
+ i = ttgetch();
+ if (i == '\33') {
+ drawscreen();
+ nosignal = 0; /* enable signals */
+ return;
+ }
+ }
+}
+
+/* routine to save program space */
+void
+iopts(void)
+{
+ lprcat(", or (i) ignore it? ");
+}
+
+void
+ignore(void)
+{
+ lprcat("ignore\n");
+}
diff --git a/larn/pathnames.h b/larn/pathnames.h
new file mode 100644
index 0000000..0790dfc
--- /dev/null
+++ b/larn/pathnames.h
@@ -0,0 +1,38 @@
+/* $NetBSD: pathnames.h,v 1.8 2003/08/07 09:37:23 agc Exp $ */
+
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 5.2 (Berkeley) 4/27/95
+ */
+
+#define _PATH_LOG "/var/games/larn/llog12.0"
+#define _PATH_SCORE "/var/games/larn/lscore12.0"
+#define _PATH_HELP "/usr/share/games/larn/larn.help"
+#define _PATH_LEVELS "/usr/share/games/larn/larnmaze"
+#define _PATH_PLAYERIDS "/var/games/larn/playerids"
diff --git a/larn/regen.c b/larn/regen.c
new file mode 100644
index 0000000..2c8537f
--- /dev/null
+++ b/larn/regen.c
@@ -0,0 +1,187 @@
+/* $NetBSD: regen.c,v 1.6 2012/06/19 05:30:44 dholland Exp $ */
+
+/* regen.c Larn is copyrighted 1986 by Noah Morgan. */
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: regen.c,v 1.6 2012/06/19 05:30:44 dholland Exp $");
+#endif /* not lint */
+
+#include "header.h"
+#include "extern.h"
+/*
+ *******
+ REGEN()
+ *******
+ regen()
+
+ subroutine to regenerate player hp and spells
+ */
+void
+regen(void)
+{
+ int i, flag;
+ long *d;
+ d = c;
+#ifdef EXTRA
+ d[MOVESMADE]++;
+#endif
+ if (d[TIMESTOP]) {
+ if (--d[TIMESTOP] <= 0)
+ bottomline();
+ return;
+ } /* for stop time spell */
+ flag = 0;
+
+ if (d[STRENGTH] < 3) {
+ d[STRENGTH] = 3;
+ flag = 1;
+ }
+ if ((d[HASTESELF] == 0) || ((d[HASTESELF] & 1) == 0))
+ gltime++;
+
+ if (d[HP] != d[HPMAX])
+ if (d[REGENCOUNTER]-- <= 0) { /* regenerate hit points */
+ d[REGENCOUNTER] = 22 + (d[HARDGAME] << 1) - d[LEVEL];
+ if ((d[HP] += d[REGEN]) > d[HPMAX])
+ d[HP] = d[HPMAX];
+ bottomhp();
+ }
+ if (d[SPELLS] < d[SPELLMAX]) /* regenerate spells */
+ if (d[ECOUNTER]-- <= 0) {
+ d[ECOUNTER] = 100 + 4 * (d[HARDGAME] - d[LEVEL] - d[ENERGY]);
+ d[SPELLS]++;
+ bottomspell();
+ }
+ if (d[HERO])
+ if (--d[HERO] <= 0) {
+ for (i = 0; i < 6; i++)
+ d[i] -= 10;
+ flag = 1;
+ }
+ if (d[ALTPRO])
+ if (--d[ALTPRO] <= 0) {
+ d[MOREDEFENSES] -= 3;
+ flag = 1;
+ }
+ if (d[PROTECTIONTIME])
+ if (--d[PROTECTIONTIME] <= 0) {
+ d[MOREDEFENSES] -= 2;
+ flag = 1;
+ }
+ if (d[DEXCOUNT])
+ if (--d[DEXCOUNT] <= 0) {
+ d[DEXTERITY] -= 3;
+ flag = 1;
+ }
+ if (d[STRCOUNT])
+ if (--d[STRCOUNT] <= 0) {
+ d[STREXTRA] -= 3;
+ flag = 1;
+ }
+ if (d[BLINDCOUNT])
+ if (--d[BLINDCOUNT] <= 0) {
+ cursors();
+ lprcat("\nThe blindness lifts ");
+ beep();
+ }
+ if (d[CONFUSE])
+ if (--d[CONFUSE] <= 0) {
+ cursors();
+ lprcat("\nYou regain your senses");
+ beep();
+ }
+ if (d[GIANTSTR])
+ if (--d[GIANTSTR] <= 0) {
+ d[STREXTRA] -= 20;
+ flag = 1;
+ }
+ if (d[CHARMCOUNT])
+ if ((--d[CHARMCOUNT]) <= 0)
+ flag = 1;
+ if (d[INVISIBILITY])
+ if ((--d[INVISIBILITY]) <= 0)
+ flag = 1;
+ if (d[CANCELLATION])
+ if ((--d[CANCELLATION]) <= 0)
+ flag = 1;
+ if (d[WTW])
+ if ((--d[WTW]) <= 0)
+ flag = 1;
+ if (d[HASTESELF])
+ if ((--d[HASTESELF]) <= 0)
+ flag = 1;
+ if (d[AGGRAVATE])
+ --d[AGGRAVATE];
+ if (d[SCAREMONST])
+ if ((--d[SCAREMONST]) <= 0)
+ flag = 1;
+ if (d[STEALTH])
+ if ((--d[STEALTH]) <= 0)
+ flag = 1;
+ if (d[AWARENESS])
+ --d[AWARENESS];
+ if (d[HOLDMONST])
+ if ((--d[HOLDMONST]) <= 0)
+ flag = 1;
+ if (d[HASTEMONST])
+ --d[HASTEMONST];
+ if (d[FIRERESISTANCE])
+ if ((--d[FIRERESISTANCE]) <= 0)
+ flag = 1;
+ if (d[GLOBE])
+ if (--d[GLOBE] <= 0) {
+ d[MOREDEFENSES] -= 10;
+ flag = 1;
+ }
+ if (d[SPIRITPRO])
+ if (--d[SPIRITPRO] <= 0)
+ flag = 1;
+ if (d[UNDEADPRO])
+ if (--d[UNDEADPRO] <= 0)
+ flag = 1;
+ if (d[HALFDAM])
+ if (--d[HALFDAM] <= 0) {
+ cursors();
+ lprcat("\nYou now feel better ");
+ beep();
+ }
+ if (d[SEEINVISIBLE])
+ if (--d[SEEINVISIBLE] <= 0) {
+ monstnamelist[INVISIBLESTALKER] = ' ';
+ cursors();
+ lprcat("\nYou feel your vision return to normal");
+ beep();
+ }
+ if (d[ITCHING]) {
+ if (d[ITCHING] > 1)
+ if ((d[WEAR] != -1) || (d[SHIELD] != -1))
+ if (rnd(100) < 50) {
+ d[WEAR] = d[SHIELD] = -1;
+ cursors();
+ lprcat("\nThe hysteria of itching forces you to remove your armor!");
+ beep();
+ recalc();
+ bottomline();
+ }
+ if (--d[ITCHING] <= 0) {
+ cursors();
+ lprcat("\nYou now feel the irritation subside!");
+ beep();
+ }
+ }
+ if (d[CLUMSINESS]) {
+ if (d[WIELD] != -1)
+ if (d[CLUMSINESS] > 1)
+ if (item[playerx][playery] == 0) /* only if nothing there */
+ if (rnd(100) < 33) /* drop your weapon due
+ * to clumsiness */
+ drop_object((int) d[WIELD]);
+ if (--d[CLUMSINESS] <= 0) {
+ cursors();
+ lprcat("\nYou now feel less awkward!");
+ beep();
+ }
+ }
+ if (flag)
+ bottomline();
+}
diff --git a/larn/savelev.c b/larn/savelev.c
new file mode 100644
index 0000000..9954ff0
--- /dev/null
+++ b/larn/savelev.c
@@ -0,0 +1,67 @@
+/* $NetBSD: savelev.c,v 1.7 2012/06/19 05:30:44 dholland Exp $ */
+
+/* savelev.c Larn is copyrighted 1986 by Noah Morgan. */
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: savelev.c,v 1.7 2012/06/19 05:30:44 dholland Exp $");
+#endif /* not lint */
+#include "header.h"
+#include "extern.h"
+
+/*
+ * routine to save the present level into storage
+ */
+void
+savelevel(void)
+{
+ struct cel *pcel;
+ u_char *pitem, *pknow, *pmitem;
+ short *phitp, *piarg;
+ struct cel *pecel;
+ pcel = &cell[level * MAXX * MAXY]; /* pointer to this level's
+ * cells */
+ pecel = pcel + MAXX * MAXY; /* pointer to past end of this
+ * level's cells */
+ pitem = item[0];
+ piarg = iarg[0];
+ pknow = know[0];
+ pmitem = mitem[0];
+ phitp = hitp[0];
+ while (pcel < pecel) {
+ pcel->mitem = *pmitem++;
+ pcel->hitp = *phitp++;
+ pcel->item = *pitem++;
+ pcel->know = *pknow++;
+ pcel->iarg = *piarg++;
+ pcel++;
+ }
+}
+
+/*
+ * routine to restore a level from storage
+ */
+void
+getlevel(void)
+{
+ struct cel *pcel;
+ u_char *pitem, *pknow, *pmitem;
+ short *phitp, *piarg;
+ struct cel *pecel;
+ pcel = &cell[level * MAXX * MAXY]; /* pointer to this level's
+ * cells */
+ pecel = pcel + MAXX * MAXY; /* pointer to past end of this
+ * level's cells */
+ pitem = item[0];
+ piarg = iarg[0];
+ pknow = know[0];
+ pmitem = mitem[0];
+ phitp = hitp[0];
+ while (pcel < pecel) {
+ *pmitem++ = pcel->mitem;
+ *phitp++ = pcel->hitp;
+ *pitem++ = pcel->item;
+ *pknow++ = pcel->know;
+ *piarg++ = pcel->iarg;
+ pcel++;
+ }
+}
diff --git a/larn/scores.c b/larn/scores.c
new file mode 100644
index 0000000..49a6f87
--- /dev/null
+++ b/larn/scores.c
@@ -0,0 +1,855 @@
+/* $NetBSD: scores.c,v 1.21 2012/06/19 05:30:44 dholland Exp $ */
+
+/*
+ * scores.c Larn is copyrighted 1986 by Noah Morgan.
+ *
+ * Functions in this file are:
+ *
+ * readboard() Function to read in the scoreboard into a static buffer
+ * writeboard() Function to write the scoreboard from readboard()'s buffer
+ * makeboard() Function to create a new scoreboard (wipe out old one)
+ * hashewon() Function to return 1 if player has won a game before, else 0
+ * long paytaxes(x) Function to pay taxes if any are due winshou()
+ * ubroutine to print out the winning scoreboard shou(x)
+ * ubroutine to print out the non-winners scoreboard showscores()
+ * unction to show the scoreboard on the terminal showallscores()
+ * Function to show scores and the iven lists that go with them sortboard()
+ * unction to sort the scoreboard newscore(score, whoo, whyded, winner)
+ * Function to add entry to scoreboard new1sub(score,i,whoo,taxes)
+ * Subroutine to put player into a new2sub(score,i,whoo,whyded)
+ * Subroutine to put player into a died(x) Subroutine to record who
+ * played larn, and what the score was diedsub(x) Subroutine to print out a
+ * line showing player when he is killed diedlog() Subroutine to read a
+ * log file and print it out in ascii format getplid(name)
+ * on to get players id # from id file
+ *
+ */
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: scores.c,v 1.21 2012/06/19 05:30:44 dholland Exp $");
+#endif /* not lint */
+#include <sys/types.h>
+#include <sys/times.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include "header.h"
+#include "extern.h"
+
+struct scofmt { /* This is the structure for the scoreboard */
+ long score; /* the score of the player */
+ long suid; /* the user id number of the player */
+ short what; /* the number of the monster that killed
+ * player */
+ short level; /* the level player was on when he died */
+ short hardlev;/* the level of difficulty player played at */
+ short order; /* the relative ordering place of this entry */
+ char who[40];/* the name of the character */
+ char sciv[26][2]; /* this is the inventory list of the
+ * character */
+};
+struct wscofmt { /* This is the structure for the winning
+ * scoreboard */
+ long score; /* the score of the player */
+ long timeused; /* the time used in mobuls to win the
+ * game */
+ long taxes; /* taxes he owes to LRS */
+ long suid; /* the user id number of the player */
+ short hardlev;/* the level of difficulty player played at */
+ short order; /* the relative ordering place of this entry */
+ char who[40];/* the name of the character */
+};
+
+struct log_fmt { /* 102 bytes struct for the log file */
+ long score; /* the players score */
+ int32_t diedtime; /* time when game was over */
+ short cavelev;/* level in caves */
+ short diff; /* difficulty player played at */
+#ifdef EXTRA
+ long elapsedtime; /* real time of game in seconds */
+ long bytout; /* bytes input and output */
+ long bytin;
+ long moves; /* number of moves made by player */
+ short ac; /* armor class of player */
+ short hp, hpmax; /* players hitpoints */
+ short cputime;/* CPU time needed in seconds */
+ short killed, spused; /* monsters killed and spells cast */
+ short usage; /* usage of the CPU in % */
+ short lev; /* player level */
+#endif
+ char who[12];/* player name */
+ char what[46]; /* what happened to player */
+};
+
+static struct scofmt sco[SCORESIZE]; /* the structure for the scoreboard */
+static struct wscofmt winr[SCORESIZE]; /* struct for the winning scoreboard */
+static struct log_fmt logg; /* structure for the log file */
+static const char *whydead[] = {
+ "quit", "suspended", "self - annihilated", "shot by an arrow",
+ "hit by a dart", "fell into a pit", "fell into a bottomless pit",
+ "a winner", "trapped in solid rock", "killed by a missing save file",
+ "killed by an old save file", "caught by the greedy cheater checker trap",
+ "killed by a protected save file", "killed his family and committed suicide",
+ "erased by a wayward finger", "fell through a bottomless trap door",
+ "fell through a trap door", "drank some poisonous water",
+ "fried by an electric shock", "slipped on a volcano shaft",
+ "killed by a stupid act of frustration", "attacked by a revolting demon",
+ "hit by his own magic", "demolished by an unseen attacker",
+ "fell into the dreadful sleep", "killed by an exploding chest",
+ /* 26 */ "killed by a missing maze data file", "annihilated in a sphere",
+ "died a post mortem death", "wasted by a malloc() failure"
+};
+
+static int readboard(void);
+static int writeboard(void);
+static int winshou(void);
+static int shou(int);
+static int sortboard(void);
+static void newscore(long, char *, int, int);
+static void new1sub(long, int, char *, long);
+static void new2sub(long, int, char *, int);
+static void diedsub(int);
+
+/*
+ * readboard() Function to read in the scoreboard into a static buffer
+ *
+ * returns -1 if unable to read in the scoreboard, returns 0 if all is OK
+ */
+static int
+readboard(void)
+{
+ int i;
+
+ if (gid != egid)
+ setegid(egid);
+ i = lopen(scorefile);
+ if (gid != egid)
+ setegid(gid);
+ if (i < 0) {
+ lprcat("Can't read scoreboard\n");
+ lflush();
+ return (-1);
+ }
+ lrfill((char *) sco, sizeof(sco));
+ lrfill((char *) winr, sizeof(winr));
+ lrclose();
+ lcreat((char *) 0);
+ return (0);
+}
+
+/*
+ * writeboard() Function to write the scoreboard from readboard()'s buffer
+ *
+ * returns -1 if unable to write the scoreboard, returns 0 if all is OK
+ */
+static int
+writeboard(void)
+{
+ int i;
+
+ set_score_output();
+ if (gid != egid)
+ setegid(egid);
+ i = lcreat(scorefile);
+ if (gid != egid)
+ setegid(gid);
+ if (i < 0) {
+ lprcat("Can't write scoreboard\n");
+ lflush();
+ return (-1);
+ }
+ lwrite((char *) sco, sizeof(sco));
+ lwrite((char *) winr, sizeof(winr));
+ lwclose();
+ lcreat((char *) 0);
+ return (0);
+}
+
+/*
+ * makeboard() Function to create a new scoreboard (wipe out old one)
+ *
+ * returns -1 if unable to write the scoreboard, returns 0 if all is OK
+ */
+int
+makeboard(void)
+{
+ int i;
+ set_score_output();
+ for (i = 0; i < SCORESIZE; i++) {
+ winr[i].taxes = winr[i].score = sco[i].score = 0;
+ winr[i].order = sco[i].order = i;
+ }
+ if (writeboard())
+ return (-1);
+ if (gid != egid)
+ setegid(egid);
+ chmod(scorefile, 0660);
+ if (gid != egid)
+ setegid(gid);
+ return (0);
+}
+
+/*
+ * hashewon() Function to return 1 if player has won a game before, else 0
+ *
+ * This function also sets c[HARDGAME] to appropriate value -- 0 if not a
+ * winner, otherwise the next level of difficulty listed in the winners
+ * scoreboard. This function also sets outstanding_taxes to the value in
+ * the winners scoreboard.
+ */
+int
+hashewon(void)
+{
+ int i;
+ c[HARDGAME] = 0;
+ if (readboard() < 0)
+ return (0); /* can't find scoreboard */
+ for (i = 0; i < SCORESIZE; i++) /* search through winners scoreboard */
+ if (winr[i].suid == userid)
+ if (winr[i].score > 0) {
+ c[HARDGAME] = winr[i].hardlev + 1;
+ outstanding_taxes = winr[i].taxes;
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * long paytaxes(x) Function to pay taxes if any are due
+ *
+ * Enter with the amount (in gp) to pay on the taxes.
+ * Returns amount actually paid.
+ */
+long
+paytaxes(long x)
+{
+ int i;
+ long amt;
+ if (x < 0)
+ return (0L);
+ if (readboard() < 0)
+ return (0L);
+ for (i = 0; i < SCORESIZE; i++)
+ if (winr[i].suid == userid) /* look for players winning
+ * entry */
+ if (winr[i].score > 0) { /* search for a winning
+ * entry for the player */
+ amt = winr[i].taxes;
+ if (x < amt)
+ amt = x; /* don't overpay taxes
+ * (Ughhhhh) */
+ winr[i].taxes -= amt;
+ outstanding_taxes -= amt;
+ set_score_output();
+ if (writeboard() < 0)
+ return (0);
+ return (amt);
+ }
+ return (0L); /* couldn't find user on winning scoreboard */
+}
+
+/*
+ * winshou() Subroutine to print out the winning scoreboard
+ *
+ * Returns the number of players on scoreboard that were shown
+ */
+static int
+winshou(void)
+{
+ struct wscofmt *p;
+ int i, j, count;
+ for (count = j = i = 0; i < SCORESIZE; i++) /* is there anyone on
+ * the scoreboard? */
+ if (winr[i].score != 0) {
+ j++;
+ break;
+ }
+ if (j) {
+ lprcat("\n Score Difficulty Time Needed Larn Winners List\n");
+
+ for (i = 0; i < SCORESIZE; i++) /* this loop is needed to
+ * print out the */
+ for (j = 0; j < SCORESIZE; j++) { /* winners in order */
+ p = &winr[j]; /* pointer to the scoreboard
+ * entry */
+ if (p->order == i) {
+ if (p->score) {
+ count++;
+ lprintf("%10ld %2ld %5ld Mobuls %s \n",
+ (long) p->score, (long) p->hardlev, (long) p->timeused, p->who);
+ }
+ break;
+ }
+ }
+ }
+ return (count); /* return number of people on scoreboard */
+}
+
+/*
+ * shou(x) Subroutine to print out the non-winners scoreboard
+ * int x;
+ *
+ * Enter with 0 to list the scores, enter with 1 to list inventories too
+ * Returns the number of players on scoreboard that were shown
+ */
+static int
+shou(int x)
+{
+ int i, j, n, k;
+ int count;
+ for (count = j = i = 0; i < SCORESIZE; i++) /* is the scoreboard
+ * empty? */
+ if (sco[i].score != 0) {
+ j++;
+ break;
+ }
+ if (j) {
+ lprcat("\n Score Difficulty Larn Visitor Log\n");
+ for (i = 0; i < SCORESIZE; i++) /* be sure to print them out
+ * in order */
+ for (j = 0; j < SCORESIZE; j++)
+ if (sco[j].order == i) {
+ if (sco[j].score) {
+ count++;
+ lprintf("%10ld %2ld %s ",
+ (long) sco[j].score, (long) sco[j].hardlev, sco[j].who);
+ if (sco[j].what < 256)
+ lprintf("killed by a %s", monster[sco[j].what].name);
+ else
+ lprintf("%s", whydead[sco[j].what - 256]);
+ if (x != 263)
+ lprintf(" on %s", levelname[sco[j].level]);
+ if (x) {
+ for (n = 0; n < 26; n++) {
+ iven[n] = sco[j].sciv[n][0];
+ ivenarg[n] = sco[j].sciv[n][1];
+ }
+ for (k = 1; k < 99; k++)
+ for (n = 0; n < 26; n++)
+ if (k == iven[n]) {
+ srcount = 0;
+ show3(n);
+ }
+ lprcat("\n\n");
+ } else
+ lprc('\n');
+ }
+ j = SCORESIZE;
+ }
+ }
+ return (count); /* return the number of players just shown */
+}
+
+/*
+ * showscores() Function to show the scoreboard on the terminal
+ *
+ * Returns nothing of value
+ */
+static char esb[] = "The scoreboard is empty.\n";
+void
+showscores(void)
+{
+ int i, j;
+ lflush();
+ lcreat((char *) 0);
+ if (readboard() < 0)
+ return;
+ i = winshou();
+ j = shou(0);
+ if (i + j == 0)
+ lprcat(esb);
+ else
+ lprc('\n');
+ lflush();
+}
+
+/*
+ * showallscores() Function to show scores and the iven lists that go with them
+ *
+ * Returns nothing of value
+ */
+void
+showallscores(void)
+{
+ int i, j;
+ lflush();
+ lcreat((char *) 0);
+ if (readboard() < 0)
+ return;
+ c[WEAR] = c[WIELD] = c[SHIELD] = -1; /* not wielding or wearing
+ * anything */
+ for (i = 0; i < MAXPOTION; i++)
+ potionname[i] = potionhide[i];
+ for (i = 0; i < MAXSCROLL; i++)
+ scrollname[i] = scrollhide[i];
+ i = winshou();
+ j = shou(1);
+ if (i + j == 0)
+ lprcat(esb);
+ else
+ lprc('\n');
+ lflush();
+}
+
+/*
+ * sortboard() Function to sort the scoreboard
+ *
+ * Returns 0 if no sorting done, else returns 1
+ */
+static int
+sortboard(void)
+{
+ int i, j = 0, pos;
+ long jdat;
+ for (i = 0; i < SCORESIZE; i++)
+ sco[i].order = winr[i].order = -1;
+ pos = 0;
+ while (pos < SCORESIZE) {
+ jdat = 0;
+ for (i = 0; i < SCORESIZE; i++)
+ if ((sco[i].order < 0) && (sco[i].score >= jdat)) {
+ j = i;
+ jdat = sco[i].score;
+ }
+ sco[j].order = pos++;
+ }
+ pos = 0;
+ while (pos < SCORESIZE) {
+ jdat = 0;
+ for (i = 0; i < SCORESIZE; i++)
+ if ((winr[i].order < 0) && (winr[i].score >= jdat)) {
+ j = i;
+ jdat = winr[i].score;
+ }
+ winr[j].order = pos++;
+ }
+ return (1);
+}
+
+/*
+ * newscore(score, whoo, whyded, winner) Function to add entry to scoreboard
+ * int score, winner, whyded;
+ * char *whoo;
+ *
+ * Enter with the total score in gp in score, players name in whoo,
+ * died() reason # in whyded, and TRUE/FALSE in winner if a winner
+ * ex. newscore(1000, "player 1", 32, 0);
+ */
+static void
+newscore(long score, char *whoo, int whyded, int winner)
+{
+ int i;
+ long taxes;
+ if (readboard() < 0)
+ return; /* do the scoreboard */
+ /* if a winner then delete all non-winning scores */
+ if (cheat)
+ winner = 0; /* if he cheated, don't let him win */
+ if (winner) {
+ for (i = 0; i < SCORESIZE; i++)
+ if (sco[i].suid == userid)
+ sco[i].score = 0;
+ taxes = score * TAXRATE;
+ score += 100000 * c[HARDGAME]; /* bonus for winning */
+ /*
+ * if he has a slot on the winning scoreboard update it if
+ * greater score
+ */
+ for (i = 0; i < SCORESIZE; i++)
+ if (winr[i].suid == userid) {
+ new1sub(score, i, whoo, taxes);
+ return;
+ }
+ /*
+ * he had no entry. look for last entry and see if he has a
+ * greater score
+ */
+ for (i = 0; i < SCORESIZE; i++)
+ if (winr[i].order == SCORESIZE - 1) {
+ new1sub(score, i, whoo, taxes);
+ return;
+ }
+ } else if (!cheat) { /* for not winning scoreboard */
+ /*
+ * if he has a slot on the scoreboard update it if greater
+ * score
+ */
+ for (i = 0; i < SCORESIZE; i++)
+ if (sco[i].suid == userid) {
+ new2sub(score, i, whoo, whyded);
+ return;
+ }
+ /*
+ * he had no entry. look for last entry and see if he has a
+ * greater score
+ */
+ for (i = 0; i < SCORESIZE; i++)
+ if (sco[i].order == SCORESIZE - 1) {
+ new2sub(score, i, whoo, whyded);
+ return;
+ }
+ }
+}
+
+/*
+ * new1sub(score,i,whoo,taxes) Subroutine to put player into a
+ * int score,i,whyded,taxes; winning scoreboard entry if his score
+ * char *whoo; is high enough
+ *
+ * Enter with the total score in gp in score, players name in whoo,
+ * died() reason # in whyded, and TRUE/FALSE in winner if a winner
+ * slot in scoreboard in i, and the tax bill in taxes.
+ * Returns nothing of value
+ */
+static void
+new1sub(long score, int i, char *whoo, long taxes)
+{
+ struct wscofmt *p;
+ p = &winr[i];
+ p->taxes += taxes;
+ if ((score >= p->score) || (c[HARDGAME] > p->hardlev)) {
+ strcpy(p->who, whoo);
+ p->score = score;
+ p->hardlev = c[HARDGAME];
+ p->suid = userid;
+ p->timeused = gltime / 100;
+ }
+}
+
+/*
+ * new2sub(score,i,whoo,whyded) Subroutine to put player into a
+ * int score,i,whyded,taxes; non-winning scoreboard entry if his
+ * char *whoo; score is high enough
+ *
+ * Enter with the total score in gp in score, players name in whoo,
+ * died() reason # in whyded, and slot in scoreboard in i.
+ * Returns nothing of value
+ */
+static void
+new2sub(long score, int i, char *whoo, int whyded)
+{
+ int j;
+ struct scofmt *p;
+ p = &sco[i];
+ if ((score >= p->score) || (c[HARDGAME] > p->hardlev)) {
+ strcpy(p->who, whoo);
+ p->score = score;
+ p->what = whyded;
+ p->hardlev = c[HARDGAME];
+ p->suid = userid;
+ p->level = level;
+ for (j = 0; j < 26; j++) {
+ p->sciv[j][0] = iven[j];
+ p->sciv[j][1] = ivenarg[j];
+ }
+ }
+}
+
+/*
+ * died(x) Subroutine to record who played larn, and what the score was
+ * int x;
+ *
+ * if x < 0 then don't show scores
+ * died() never returns! (unless c[LIFEPROT] and a reincarnatable death!)
+ *
+ * < 256 killed by the monster number
+ * 256 quit
+ * 257 suspended
+ * 258 self - annihilated
+ * 259 shot by an arrow
+ * 260 hit by a dart
+ * 261 fell into a pit
+ * 262 fell into a bottomless pit
+ * 263 a winner
+ * 264 trapped in solid rock
+ * 265 killed by a missing save file
+ * 266 killed by an old save file
+ * 267 caught by the greedy cheater checker trap
+ * 268 killed by a protected save file
+ * 269 killed his family and killed himself
+ * 270 erased by a wayward finger
+ * 271 fell through a bottomless trap door
+ * 272 fell through a trap door
+ * 273 drank some poisonous water
+ * 274 fried by an electric shock
+ * 275 slipped on a volcano shaft
+ * 276 killed by a stupid act of frustration
+ * 277 attacked by a revolting demon
+ * 278 hit by his own magic
+ * 279 demolished by an unseen attacker
+ * 280 fell into the dreadful sleep
+ * 281 killed by an exploding chest
+ * 282 killed by a missing maze data file
+ * 283 killed by a sphere of annihilation
+ * 284 died a post mortem death
+ * 285 malloc() failure
+ * 300 quick quit -- don't put on scoreboard
+ */
+
+static int scorerror;
+void
+died(int x)
+{
+ int f, win;
+ char ch;
+ const char *mod;
+ time_t zzz;
+ if (c[LIFEPROT] > 0) { /* if life protection */
+ switch ((x > 0) ? x : -x) {
+ case 256:
+ case 257:
+ case 262:
+ case 263:
+ case 265:
+ case 266:
+ case 267:
+ case 268:
+ case 269:
+ case 271:
+ case 282:
+ case 284:
+ case 285:
+ case 300:
+ goto invalid; /* can't be saved */
+ };
+ --c[LIFEPROT];
+ c[HP] = 1;
+ --c[CONSTITUTION];
+ cursors();
+ lprcat("\nYou feel wiiieeeeerrrrrd all over! ");
+ beep();
+ lflush();
+ sleep(4);
+ return; /* only case where died() returns */
+ }
+invalid:
+ clearvt100();
+ lflush();
+ f = 0;
+ if (ckpflag)
+ unlink(ckpfile);/* remove checkpoint file if used */
+ if (x < 0) {
+ f++;
+ x = -x;
+ } /* if we are not to display the scores */
+ if ((x == 300) || (x == 257))
+ exit(0); /* for quick exit or saved game */
+ if (x == 263)
+ win = 1;
+ else
+ win = 0;
+ c[GOLD] += c[BANKACCOUNT];
+ c[BANKACCOUNT] = 0;
+ /* now enter the player at the end of the scoreboard */
+ newscore(c[GOLD], logname, x, win);
+ diedsub(x); /* print out the score line */
+ lflush();
+
+ set_score_output();
+ if ((wizard == 0) && (c[GOLD] > 0)) { /* wizards can't score */
+#ifndef NOLOG
+ if (gid != egid)
+ setegid(egid);
+ if (lappend(logfile) < 0) { /* append to file */
+ if (lcreat(logfile) < 0) { /* and can't create new
+ * log file */
+ lcreat((char *) 0);
+ lprcat("\nCan't open record file: I can't post your score.\n");
+ sncbr();
+ resetscroll();
+ lflush();
+ exit(0);
+ }
+ if (gid != egid)
+ setegid(egid);
+ chmod(logfile, 0660);
+ if (gid != egid)
+ setegid(gid);
+ }
+ if (gid != egid)
+ setegid(gid);
+ strcpy(logg.who, loginname);
+ logg.score = c[GOLD];
+ logg.diff = c[HARDGAME];
+ if (x < 256) {
+ ch = *monster[x].name;
+ if (ch == 'a' || ch == 'e' || ch == 'i' || ch == 'o' || ch == 'u')
+ mod = "an";
+ else
+ mod = "a";
+ snprintf(logg.what, sizeof(logg.what),
+ "killed by %s %s", mod, monster[x].name);
+ } else
+ snprintf(logg.what, sizeof(logg.what),
+ "%s", whydead[x - 256]);
+ logg.cavelev = level;
+ time(&zzz); /* get CPU time -- write out score info */
+ logg.diedtime = zzz;
+#ifdef EXTRA
+ times(&cputime);/* get CPU time -- write out score info */
+ logg.cputime = i = (cputime.tms_utime + cputime.tms_stime) / 60 + c[CPUTIME];
+ logg.lev = c[LEVEL];
+ logg.ac = c[AC];
+ logg.hpmax = c[HPMAX];
+ logg.hp = c[HP];
+ logg.elapsedtime = (zzz - initialtime + 59) / 60;
+ logg.usage = (10000 * i) / (zzz - initialtime);
+ logg.bytin = c[BYTESIN];
+ logg.bytout = c[BYTESOUT];
+ logg.moves = c[MOVESMADE];
+ logg.spused = c[SPELLSCAST];
+ logg.killed = c[MONSTKILLED];
+#endif
+ lwrite((char *) &logg, sizeof(struct log_fmt));
+ lwclose();
+#endif /* NOLOG */
+
+ /*
+ * now for the scoreboard maintenance -- not for a suspended
+ * game
+ */
+ if (x != 257) {
+ if (sortboard()) {
+ set_score_output();
+ scorerror = writeboard();
+ }
+ }
+ }
+ if ((x == 256) || (x == 257) || (f != 0))
+ exit(0);
+ if (scorerror == 0)
+ showscores(); /* if we updated the scoreboard */
+ if (x == 263)
+ mailbill();
+ exit(0);
+}
+
+/*
+ * diedsub(x) Subroutine to print out the line showing the player when he is killed
+ * int x;
+ */
+static void
+diedsub(int x)
+{
+ char ch;
+ const char *mod;
+
+ lprintf("Score: %ld, Diff: %ld, %s ", (long) c[GOLD], (long) c[HARDGAME], logname);
+ if (x < 256) {
+ ch = *monster[x].name;
+ if (ch == 'a' || ch == 'e' || ch == 'i' || ch == 'o' || ch == 'u')
+ mod = "an";
+ else
+ mod = "a";
+ lprintf("killed by %s %s", mod, monster[x].name);
+ } else
+ lprintf("%s", whydead[x - 256]);
+ if (x != 263)
+ lprintf(" on %s\n", levelname[level]);
+ else
+ lprc('\n');
+}
+
+/*
+ * diedlog() Subroutine to read a log file and print it out in ascii format
+ */
+void
+diedlog(void)
+{
+ int n;
+ char *p;
+ static char q[] = "?";
+ struct stat stbuf;
+ time_t t;
+
+ lcreat((char *) 0);
+ if (lopen(logfile) < 0) {
+ lprintf("Can't locate log file <%s>\n", logfile);
+ return;
+ }
+ if (fstat(io_infd, &stbuf) < 0) {
+ lprintf("Can't stat log file <%s>\n", logfile);
+ return;
+ }
+ for (n = stbuf.st_size / sizeof(struct log_fmt); n > 0; --n) {
+ lrfill((char *) &logg, sizeof(struct log_fmt));
+ t = logg.diedtime;
+ if ((p = ctime(&t)) == NULL)
+ p = q;
+ else {
+ p[16] = '\n';
+ p[17] = 0;
+ }
+ lprintf("Score: %ld, Diff: %ld, %s %s on %ld at %s", (long) (logg.score), (long) (logg.diff), logg.who, logg.what, (long) (logg.cavelev), p + 4);
+#ifdef EXTRA
+ if (logg.moves <= 0)
+ logg.moves = 1;
+ lprintf(" Experience Level: %ld, AC: %ld, HP: %ld/%ld, Elapsed Time: %ld minutes\n", (long) (logg.lev), (long) (logg.ac), (long) (logg.hp), (long) (logg.hpmax), (long) (logg.elapsedtime));
+ lprintf(" CPU time used: %ld seconds, Machine usage: %ld.%02ld%%\n", (long) (logg.cputime), (long) (logg.usage / 100), (long) (logg.usage % 100));
+ lprintf(" BYTES in: %ld, out: %ld, moves: %ld, deaths: %ld, spells cast: %ld\n", (long) (logg.bytin), (long) (logg.bytout), (long) (logg.moves), (long) (logg.killed), (long) (logg.spused));
+ lprintf(" out bytes per move: %ld, time per move: %ld ms\n", (long) (logg.bytout / logg.moves), (long) ((logg.cputime * 1000) / logg.moves));
+#endif
+ }
+ lflush();
+ lrclose();
+ return;
+}
+
+#ifndef UIDSCORE
+/*
+ * getplid(name) Function to get players id # from id file
+ *
+ * Enter with the name of the players character in name.
+ * Returns the id # of the players character, or -1 if failure.
+ * This routine will try to find the name in the id file, if its not there,
+ * it will try to make a new entry in the file. Only returns -1 if can't
+ * find him in the file, and can't make a new entry in the file.
+ * Format of playerids file:
+ * Id # in ascii \n character name \n
+ */
+static int havepid = -1; /* playerid # if previously done */
+int
+getplid(nam)
+ char *nam;
+{
+ int fd7, high = 999, no;
+ char *p, *p2;
+ char name[80];
+ if (havepid != -1)
+ return (havepid); /* already did it */
+ lflush(); /* flush any pending I/O */
+ snprintf(name, sizeof(name), "%s\n", nam);/* append a \n to name */
+ if (lopen(playerids) < 0) { /* no file, make it */
+ if ((fd7 = creat(playerids, 0664)) < 0)
+ return (-1); /* can't make it */
+ close(fd7);
+ goto addone; /* now append new playerid record to file */
+ }
+ for (;;) { /* now search for the name in the player id
+ * file */
+ p = lgetl();
+ if (p == NULL)
+ break; /* EOF? */
+ no = atoi(p); /* the id # */
+ p2 = lgetl();
+ if (p2 == NULL)
+ break; /* EOF? */
+ if (no > high)
+ high = no; /* accumulate highest id # */
+ if (strcmp(p2, name) == 0) { /* we found him */
+ return (no); /* his id number */
+ }
+ }
+ lrclose();
+ /* if we get here, we didn't find him in the file -- put him there */
+addone:
+ if (lappend(playerids) < 0)
+ return (-1); /* can't open file for append */
+ lprintf("%ld\n%s", (long) ++high, name); /* new id # and name */
+ lwclose();
+ lcreat((char *) 0); /* re-open terminal channel */
+ return (high);
+}
+#endif /* UIDSCORE */
diff --git a/larn/signal.c b/larn/signal.c
new file mode 100644
index 0000000..807579a
--- /dev/null
+++ b/larn/signal.c
@@ -0,0 +1,138 @@
+/* $NetBSD: signal.c,v 1.9 2012/06/19 05:30:44 dholland Exp $ */
+
+/* "Larn is copyrighted 1986 by Noah Morgan.\n" */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: signal.c,v 1.9 2012/06/19 05:30:44 dholland Exp $");
+#endif /* not lint */
+
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include "header.h"
+#include "extern.h"
+
+static void s2choose(void);
+static void cntlc(int);
+static void sgam(int);
+static void tstop(int);
+static void sigpanic(int);
+
+#define BIT(a) (1<<((a)-1))
+
+static void
+s2choose(void)
+{ /* text to be displayed if ^C during intro
+ * screen */
+ cursor(1, 24);
+ lprcat("Press ");
+ setbold();
+ lprcat("return");
+ resetbold();
+ lprcat(" to continue: ");
+ lflush();
+}
+
+static void
+cntlc(int n)
+{ /* what to do for a ^C */
+ if (nosignal)
+ return; /* don't do anything if inhibited */
+ signal(SIGQUIT, SIG_IGN);
+ signal(SIGINT, SIG_IGN);
+ quit();
+ if (predostuff == 1)
+ s2choose();
+ else
+ showplayer();
+ lflush();
+ signal(SIGQUIT, cntlc);
+ signal(SIGINT, cntlc);
+}
+
+/*
+ * subroutine to save the game if a hangup signal
+ */
+static void
+sgam(int n)
+{
+ savegame(savefilename);
+ wizard = 1;
+ died(-257); /* hangup signal */
+}
+
+#ifdef SIGTSTP
+static void
+tstop(int n)
+{ /* control Y */
+ if (nosignal)
+ return; /* nothing if inhibited */
+ lcreat((char *) 0);
+ clearvt100();
+ lflush();
+ signal(SIGTSTP, SIG_DFL);
+#ifdef SIGVTALRM
+ /*
+ * looks like BSD4.2 or higher - must clr mask for signal to take
+ * effect
+ */
+ sigsetmask(sigblock(0) & ~BIT(SIGTSTP));
+#endif
+ kill(getpid(), SIGTSTP);
+
+ setupvt100();
+ signal(SIGTSTP, tstop);
+ if (predostuff == 1)
+ s2choose();
+ else
+ drawscreen();
+ showplayer();
+ lflush();
+}
+#endif /* SIGTSTP */
+
+/*
+ * subroutine to issue the needed signal traps called from main()
+ */
+void
+sigsetup(void)
+{
+ signal(SIGQUIT, cntlc);
+ signal(SIGINT, cntlc);
+ signal(SIGKILL, SIG_IGN);
+ signal(SIGHUP, sgam);
+ signal(SIGILL, sigpanic);
+ signal(SIGTRAP, sigpanic);
+ signal(SIGIOT, sigpanic);
+ /* signal(SIGEMT, sigpanic); 20150209 bkw: not on linux */
+ signal(SIGFPE, sigpanic);
+ signal(SIGBUS, sigpanic);
+ signal(SIGSEGV, sigpanic);
+ signal(SIGSYS, sigpanic);
+ signal(SIGPIPE, sigpanic);
+ signal(SIGTERM, sigpanic);
+#ifdef SIGTSTP
+ signal(SIGTSTP, tstop);
+ signal(SIGSTOP, tstop);
+#endif /* SIGTSTP */
+}
+
+/*
+ * routine to process a fatal error signal
+ */
+static void
+sigpanic(int sig)
+{
+ char buf[128];
+ signal(sig, SIG_DFL);
+ snprintf(buf, sizeof(buf),
+ "\nLarn - Panic! Signal %d received [%s]", sig,
+ sys_siglist[sig]); /* 20150209 bkw: no sys_signame on linux */
+ write(2, buf, strlen(buf));
+ sleep(2);
+ sncbr();
+ savegame(savefilename);
+ kill(getpid(), sig); /* this will terminate us */
+}
diff --git a/larn/store.c b/larn/store.c
new file mode 100644
index 0000000..2d33538
--- /dev/null
+++ b/larn/store.c
@@ -0,0 +1,910 @@
+/* $NetBSD: store.c,v 1.16 2012/06/19 05:30:44 dholland Exp $ */
+
+/*-
+ * Copyright (c) 1988 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)store.c 5.4 (Berkeley) 5/13/91";
+#else
+__RCSID("$NetBSD: store.c,v 1.16 2012/06/19 05:30:44 dholland Exp $");
+#endif
+#endif /* not lint */
+
+/* store.c Larn is copyrighted 1986 by Noah Morgan. */
+#include "header.h"
+#include "extern.h"
+
+static void handsfull(void);
+static void outofstock(void);
+static void nogold(void);
+static void dnditem(int);
+static void banktitle(const char *);
+static void obanksub(void);
+static void otradhead(void);
+static void cnsitm(void);
+
+static int dndcount = 0, dnditm = 0;
+
+/* number of items in the dnd inventory table */
+#define MAXITM 83
+
+/* this is the data for the stuff in the dnd store */
+struct _itm itm[90] = {
+ /*
+ * cost iven name iven arg how gp
+ * iven[] ivenarg[] many
+ */
+
+ {2, OLEATHER, 0, 3},
+ {10, OSTUDLEATHER, 0, 2},
+ {40, ORING, 0, 2},
+ {85, OCHAIN, 0, 2},
+ {220, OSPLINT, 0, 1},
+ {400, OPLATE, 0, 1},
+ {900, OPLATEARMOR, 0, 1},
+ {2600, OSSPLATE, 0, 1},
+ {150, OSHIELD, 0, 1},
+
+ /*
+ * cost iven name iven arg how gp
+ * iven[] ivenarg[] many
+ */
+
+ {2, ODAGGER, 0, 3},
+ {20, OSPEAR, 0, 3},
+ {80, OFLAIL, 0, 2},
+ {150, OBATTLEAXE, 0, 2},
+ {450, OLONGSWORD, 0, 2},
+ {1000, O2SWORD, 0, 2},
+ {5000, OSWORD, 0, 1},
+ {16500, OLANCE, 0, 1},
+ {6000, OSWORDofSLASHING, 0, 0},
+ {10000, OHAMMER, 0, 0},
+
+ /*
+ * cost iven name iven arg how gp
+ * iven[] ivenarg[] many
+ */
+
+ {150, OPROTRING, 1, 1},
+ {85, OSTRRING, 1, 1},
+ {120, ODEXRING, 1, 1},
+ {120, OCLEVERRING, 1, 1},
+ {180, OENERGYRING, 0, 1},
+ {125, ODAMRING, 0, 1},
+ {220, OREGENRING, 0, 1},
+ {1000, ORINGOFEXTRA, 0, 1},
+
+ {280, OBELT, 0, 1},
+
+ {400, OAMULET, 0, 1},
+
+ {6500, OORBOFDRAGON, 0, 0},
+ {5500, OSPIRITSCARAB, 0, 0},
+ {5000, OCUBEofUNDEAD, 0, 0},
+ {6000, ONOTHEFT, 0, 0},
+
+ {590, OCHEST, 6, 1},
+ {200, OBOOK, 8, 1},
+ {10, OCOOKIE, 0, 3},
+
+ /*
+ * cost iven name iven arg how gp
+ * iven[] ivenarg[] many
+ */
+
+ {20, OPOTION, 0, 6},
+ {90, OPOTION, 1, 5},
+ {520, OPOTION, 2, 1},
+ {100, OPOTION, 3, 2},
+ {50, OPOTION, 4, 2},
+ {150, OPOTION, 5, 2},
+ {70, OPOTION, 6, 1},
+ {30, OPOTION, 7, 7},
+ {200, OPOTION, 8, 1},
+ {50, OPOTION, 9, 1},
+ {80, OPOTION, 10, 1},
+
+ /*
+ * cost iven name iven arg how gp
+ * iven[] ivenarg[] many
+ */
+
+ {30, OPOTION, 11, 3},
+ {20, OPOTION, 12, 5},
+ {40, OPOTION, 13, 3},
+ {35, OPOTION, 14, 2},
+ {520, OPOTION, 15, 1},
+ {90, OPOTION, 16, 2},
+ {200, OPOTION, 17, 2},
+ {220, OPOTION, 18, 4},
+ {80, OPOTION, 19, 6},
+ {370, OPOTION, 20, 3},
+ {50, OPOTION, 22, 1},
+ {150, OPOTION, 23, 3},
+
+ /*
+ * cost iven name iven arg how gp
+ * iven[] ivenarg[] many
+ */
+
+ {100, OSCROLL, 0, 2},
+ {125, OSCROLL, 1, 2},
+ {60, OSCROLL, 2, 4},
+ {10, OSCROLL, 3, 4},
+ {100, OSCROLL, 4, 3},
+ {200, OSCROLL, 5, 2},
+ {110, OSCROLL, 6, 1},
+ {500, OSCROLL, 7, 2},
+ {200, OSCROLL, 8, 2},
+ {250, OSCROLL, 9, 4},
+ {20, OSCROLL, 10, 5},
+ {30, OSCROLL, 11, 3},
+
+ /*
+ * cost iven name iven arg how gp
+ * iven[] ivenarg[] many
+ */
+
+ {340, OSCROLL, 12, 1},
+ {340, OSCROLL, 13, 1},
+ {300, OSCROLL, 14, 2},
+ {400, OSCROLL, 15, 2},
+ {500, OSCROLL, 16, 2},
+ {1000, OSCROLL, 17, 1},
+ {500, OSCROLL, 18, 1},
+ {340, OSCROLL, 19, 2},
+ {220, OSCROLL, 20, 3},
+ {3900, OSCROLL, 21, 0},
+ {610, OSCROLL, 22, 1},
+ {3000, OSCROLL, 23, 0}
+};
+
+/*
+ function for the dnd store
+ */
+static void
+dnd_2hed(void)
+{
+ lprcat("Welcome to the Larn Thrift Shoppe. We stock many items explorers find useful\n");
+ lprcat(" in their adventures. Feel free to browse to your hearts content.\n");
+ lprcat("Also be advised, if you break 'em, you pay for 'em.");
+}
+
+static void
+dnd_hed(void)
+{
+ int i;
+ for (i = dnditm; i < 26 + dnditm; i++)
+ dnditem(i);
+ cursor(50, 18);
+ lprcat("You have ");
+}
+
+static void
+handsfull(void)
+{
+ lprcat("\nYou can't carry anything more!");
+ lflush();
+ nap(2200);
+}
+
+static void
+outofstock(void)
+{
+ lprcat("\nSorry, but we are out of that item.");
+ lflush();
+ nap(2200);
+}
+
+static void
+nogold(void)
+{
+ lprcat("\nYou don't have enough gold to pay for that!");
+ lflush();
+ nap(2200);
+}
+
+void
+dndstore(void)
+{
+ int i;
+ dnditm = 0;
+ nosignal = 1; /* disable signals */
+ clear();
+ dnd_2hed();
+ if (outstanding_taxes > 0) {
+ lprcat("\n\nThe Larn Revenue Service has ordered us to not do business with tax evaders.\n");
+ beep();
+ lprintf("They have also told us that you owe %ld gp in back taxes, and as we must\n", (long) outstanding_taxes);
+ lprcat("comply with the law, we cannot serve you at this time. Soo Sorry.\n");
+ cursors();
+ lprcat("\nPress ");
+ standout("escape");
+ lprcat(" to leave: ");
+ lflush();
+ i = 0;
+ while (i != '\33')
+ i = ttgetch();
+ drawscreen();
+ nosignal = 0; /* enable signals */
+ return;
+ }
+ dnd_hed();
+ while (1) {
+ cursor(59, 18);
+ lprintf("%ld gold pieces", (long) c[GOLD]);
+ cltoeoln();
+ cl_dn(1, 20); /* erase to eod */
+ lprcat("\nEnter your transaction [");
+ standout("space");
+ lprcat(" for more, ");
+ standout("escape");
+ lprcat(" to leave]? ");
+ i = 0;
+ while ((i < 'a' || i > 'z') && (i != ' ') && (i != '\33') && (i != 12))
+ i = ttgetch();
+ if (i == 12) {
+ clear();
+ dnd_2hed();
+ dnd_hed();
+ } else if (i == '\33') {
+ drawscreen();
+ nosignal = 0; /* enable signals */
+ return;
+ } else if (i == ' ') {
+ cl_dn(1, 4);
+ if ((dnditm += 26) >= MAXITM)
+ dnditm = 0;
+ dnd_hed();
+ } else { /* buy something */
+ lprc(i);/* echo the byte */
+ i += dnditm - 'a';
+ if (i >= MAXITM)
+ outofstock();
+ else if (itm[i].qty <= 0)
+ outofstock();
+ else if (pocketfull())
+ handsfull();
+ else if (c[GOLD] < itm[i].price * 10)
+ nogold();
+ else {
+ if (itm[i].obj == OPOTION) {
+ potionname[itm[i].arg] = potionhide[itm[i].arg];
+ } else if (itm[i].obj == OSCROLL) {
+ scrollname[itm[i].arg] = scrollhide[itm[i].arg];
+ }
+ c[GOLD] -= itm[i].price * 10;
+ itm[i].qty--;
+ take(itm[i].obj, itm[i].arg);
+ if (itm[i].qty == 0)
+ dnditem(i);
+ nap(1001);
+ }
+ }
+
+ }
+}
+
+/*
+ dnditem(index)
+
+ to print the item list; used in dndstore() enter with the index into itm
+ */
+static void
+dnditem(int i)
+{
+ int j, k;
+ if (i >= MAXITM)
+ return;
+ cursor((j = (i & 1) * 40 + 1), (k = ((i % 26) >> 1) + 5));
+ if (itm[i].qty == 0) {
+ lprintf("%39s", "");
+ return;
+ }
+ lprintf("%c) ", (i % 26) + 'a');
+ if (itm[i].obj == OPOTION) {
+ lprintf("potion of%s", potionhide[itm[i].arg]);
+ } else if (itm[i].obj == OSCROLL) {
+ lprintf("scroll of%s", scrollhide[itm[i].arg]);
+ } else
+ lprintf("%s", objectname[itm[i].obj]);
+ cursor(j + 31, k);
+ lprintf("%6ld", (long) (itm[i].price * 10));
+}
+
+
+
+/*
+ for the college of larn
+ */
+u_char course[26] = {0}; /* the list of courses taken */
+static char coursetime[] = {10, 15, 10, 20, 10, 10, 10, 5};
+/*
+ function to display the header info for the school
+ */
+static void
+sch_hed(void)
+{
+ clear();
+ lprcat("The College of Larn offers the exciting opportunity of higher education to\n");
+ lprcat("all inhabitants of the caves. Here is a list of the class schedule:\n\n\n");
+ lprcat("\t\t Course Name \t Time Needed\n\n");
+
+ if (course[0] == 0)
+ lprcat("\t\ta) Fighters Training I 10 mobuls"); /* line 7 of crt */
+ lprc('\n');
+ if (course[1] == 0)
+ lprcat("\t\tb) Fighters Training II 15 mobuls");
+ lprc('\n');
+ if (course[2] == 0)
+ lprcat("\t\tc) Introduction to Wizardry 10 mobuls");
+ lprc('\n');
+ if (course[3] == 0)
+ lprcat("\t\td) Applied Wizardry 20 mobuls");
+ lprc('\n');
+ if (course[4] == 0)
+ lprcat("\t\te) Behavioral Psychology 10 mobuls");
+ lprc('\n');
+ if (course[5] == 0)
+ lprcat("\t\tf) Faith for Today 10 mobuls");
+ lprc('\n');
+ if (course[6] == 0)
+ lprcat("\t\tg) Contemporary Dance 10 mobuls");
+ lprc('\n');
+ if (course[7] == 0)
+ lprcat("\t\th) History of Larn 5 mobuls");
+
+ lprcat("\n\n\t\tAll courses cost 250 gold pieces.");
+ cursor(30, 18);
+ lprcat("You are presently carrying ");
+}
+
+void
+oschool(void)
+{
+ int i;
+ long time_used;
+ nosignal = 1; /* disable signals */
+ sch_hed();
+ while (1) {
+ cursor(57, 18);
+ lprintf("%ld gold pieces. ", (long) c[GOLD]);
+ cursors();
+ lprcat("\nWhat is your choice [");
+ standout("escape");
+ lprcat(" to leave] ? ");
+ yrepcount = 0;
+ i = 0;
+ while ((i < 'a' || i > 'h') && (i != '\33') && (i != 12))
+ i = ttgetch();
+ if (i == 12) {
+ sch_hed();
+ continue;
+ } else if (i == '\33') {
+ nosignal = 0;
+ drawscreen(); /* enable signals */
+ return;
+ }
+ lprc(i);
+ if (c[GOLD] < 250)
+ nogold();
+ else if (course[i - 'a']) {
+ lprcat("\nSorry, but that class is filled.");
+ nap(1000);
+ } else if (i <= 'h') {
+ c[GOLD] -= 250;
+ time_used = 0;
+ switch (i) {
+ case 'a':
+ c[STRENGTH] += 2;
+ c[CONSTITUTION]++;
+ lprcat("\nYou feel stronger!");
+ cl_line(16, 7);
+ break;
+
+ case 'b':
+ if (course[0] == 0) {
+ lprcat("\nSorry, but this class has a prerequisite of Fighters Training I");
+ c[GOLD] += 250;
+ time_used = -10000;
+ break;
+ }
+ lprcat("\nYou feel much stronger!");
+ cl_line(16, 8);
+ c[STRENGTH] += 2;
+ c[CONSTITUTION] += 2;
+ break;
+
+ case 'c':
+ c[INTELLIGENCE] += 2;
+ lprcat("\nThe task before you now seems more attainable!");
+ cl_line(16, 9);
+ break;
+
+ case 'd':
+ if (course[2] == 0) {
+ lprcat("\nSorry, but this class has a prerequisite of Introduction to Wizardry");
+ c[GOLD] += 250;
+ time_used = -10000;
+ break;
+ }
+ lprcat("\nThe task before you now seems very attainable!");
+ cl_line(16, 10);
+ c[INTELLIGENCE] += 2;
+ break;
+
+ case 'e':
+ c[CHARISMA] += 3;
+ lprcat("\nYou now feel like a born leader!");
+ cl_line(16, 11);
+ break;
+
+ case 'f':
+ c[WISDOM] += 2;
+ lprcat("\nYou now feel more confident that you can find the potion in time!");
+ cl_line(16, 12);
+ break;
+
+ case 'g':
+ c[DEXTERITY] += 3;
+ lprcat("\nYou feel like dancing!");
+ cl_line(16, 13);
+ break;
+
+ case 'h':
+ c[INTELLIGENCE]++;
+ lprcat("\nYour instructor told you that the Eye of Larn is rumored to be guarded\n");
+ lprcat("by a platinum dragon who possesses psionic abilities. ");
+ cl_line(16, 14);
+ break;
+ }
+ time_used += coursetime[i - 'a'] * 100;
+ if (time_used > 0) {
+ gltime += time_used;
+ course[i - 'a']++; /* remember that he has
+ * taken that course */
+ c[HP] = c[HPMAX];
+ c[SPELLS] = c[SPELLMAX]; /* he regenerated */
+
+ if (c[BLINDCOUNT])
+ c[BLINDCOUNT] = 1; /* cure blindness too! */
+ if (c[CONFUSE])
+ c[CONFUSE] = 1; /* end confusion */
+ adjusttime((long) time_used); /* adjust parameters for
+ * time change */
+ }
+ nap(1000);
+ }
+ }
+}
+
+
+/*
+ * for the first national bank of Larn
+ */
+int lasttime = 0; /* last time he was in bank */
+
+void
+obank(void)
+{
+ banktitle(" Welcome to the First National Bank of Larn.");
+}
+void
+obank2(void)
+{
+ banktitle("Welcome to the 5th level branch office of the First National Bank of Larn.");
+}
+
+static void
+banktitle(const char *str)
+{
+ nosignal = 1; /* disable signals */
+ clear();
+ lprcat(str);
+ if (outstanding_taxes > 0) {
+ int i;
+ lprcat("\n\nThe Larn Revenue Service has ordered that your account be frozen until all\n");
+ beep();
+ lprintf("levied taxes have been paid. They have also told us that you owe %ld gp in\n", (long) outstanding_taxes);
+ lprcat("taxes, and we must comply with them. We cannot serve you at this time. Sorry.\n");
+ lprcat("We suggest you go to the LRS office and pay your taxes.\n");
+ cursors();
+ lprcat("\nPress ");
+ standout("escape");
+ lprcat(" to leave: ");
+ lflush();
+ i = 0;
+ while (i != '\33')
+ i = ttgetch();
+ drawscreen();
+ nosignal = 0; /* enable signals */
+ return;
+ }
+ lprcat("\n\n\tGemstone\t Appraisal\t\tGemstone\t Appraisal");
+ obanksub();
+ nosignal = 0; /* enable signals */
+ drawscreen();
+}
+
+/*
+ * function to put interest on your bank account
+ */
+void
+ointerest(void)
+{
+ int i;
+ if (c[BANKACCOUNT] < 0)
+ c[BANKACCOUNT] = 0;
+ else if ((c[BANKACCOUNT] > 0) && (c[BANKACCOUNT] < 500000)) {
+ i = (gltime - lasttime) / 100; /* # mobuls elapsed */
+ while ((i-- > 0) && (c[BANKACCOUNT] < 500000))
+ c[BANKACCOUNT] += c[BANKACCOUNT] / 250;
+ if (c[BANKACCOUNT] > 500000)
+ c[BANKACCOUNT] = 500000; /* interest limit */
+ }
+ lasttime = (gltime / 100) * 100;
+}
+
+static short gemorder[26] = {0}; /* the reference to screen location
+ * for each */
+static long gemvalue[26] = {0}; /* the appraisal of the gems */
+void
+obanksub(void)
+{
+ long amt;
+ int i, k;
+ ointerest(); /* credit any needed interest */
+
+ for (k = i = 0; i < 26; i++)
+ switch (iven[i]) {
+ case OLARNEYE:
+ case ODIAMOND:
+ case OEMERALD:
+ case ORUBY:
+ case OSAPPHIRE:
+
+ if (iven[i] == OLARNEYE) {
+ gemvalue[i] = 250000 - ((gltime * 7) / 100) * 100;
+ if (gemvalue[i] < 50000)
+ gemvalue[i] = 50000;
+ } else
+ gemvalue[i] = (255 & ivenarg[i]) * 100;
+ gemorder[i] = k;
+ cursor((k % 2) * 40 + 1, (k >> 1) + 4);
+ lprintf("%c) %s", i + 'a', objectname[iven[i]]);
+ cursor((k % 2) * 40 + 33, (k >> 1) + 4);
+ lprintf("%5ld", (long) gemvalue[i]);
+ k++;
+ };
+ cursor(31, 17);
+ lprintf("You have %8ld gold pieces in the bank.", (long) c[BANKACCOUNT]);
+ cursor(40, 18);
+ lprintf("You have %8ld gold pieces", (long) c[GOLD]);
+ if (c[BANKACCOUNT] + c[GOLD] >= 500000)
+ lprcat("\nNote: Larndom law states that only deposits under 500,000gp can earn interest.");
+ while (1) {
+ cl_dn(1, 20);
+ lprcat("\nYour wish? [(");
+ standout("d");
+ lprcat(") deposit, (");
+ standout("w");
+ lprcat(") withdraw, (");
+ standout("s");
+ lprcat(") sell a stone, or ");
+ standout("escape");
+ lprcat("] ");
+ yrepcount = 0;
+ i = 0;
+ while (i != 'd' && i != 'w' && i != 's' && i != '\33')
+ i = ttgetch();
+ switch (i) {
+ case 'd':
+ lprcat("deposit\nHow much? ");
+ amt = readnum((long) c[GOLD]);
+ if (amt < 0) {
+ lprcat("\nSorry, but we can't take negative gold!");
+ nap(2000);
+ amt = 0;
+ } else if (amt > c[GOLD]) {
+ lprcat(" You don't have that much.");
+ nap(2000);
+ } else {
+ c[GOLD] -= amt;
+ c[BANKACCOUNT] += amt;
+ }
+ break;
+
+ case 'w':
+ lprcat("withdraw\nHow much? ");
+ amt = readnum((long) c[BANKACCOUNT]);
+ if (amt < 0) {
+ lprcat("\nSorry, but we don't have any negative gold!");
+ nap(2000);
+ amt = 0;
+ } else if (amt > c[BANKACCOUNT]) {
+ lprcat("\nYou don't have that much in the bank!");
+ nap(2000);
+ } else {
+ c[GOLD] += amt;
+ c[BANKACCOUNT] -= amt;
+ }
+ break;
+
+ case 's':
+ lprcat("\nWhich stone would you like to sell? ");
+ i = 0;
+ while ((i < 'a' || i > 'z') && i != '*')
+ i = ttgetch();
+ if (i == '*')
+ for (i = 0; i < 26; i++) {
+ if (gemvalue[i]) {
+ c[GOLD] += gemvalue[i];
+ iven[i] = 0;
+ gemvalue[i] = 0;
+ k = gemorder[i];
+ cursor((k % 2) * 40 + 1, (k >> 1) + 4);
+ lprintf("%39s", "");
+ }
+ }
+ else {
+ if (gemvalue[i = i - 'a'] == 0) {
+ lprintf("\nItem %c is not a gemstone!", i + 'a');
+ nap(2000);
+ break;
+ }
+ c[GOLD] += gemvalue[i];
+ iven[i] = 0;
+ gemvalue[i] = 0;
+ k = gemorder[i];
+ cursor((k % 2) * 40 + 1, (k >> 1) + 4);
+ lprintf("%39s", "");
+ }
+ break;
+
+ case '\33':
+ return;
+ };
+ cursor(40, 17);
+ lprintf("%8ld", (long) c[BANKACCOUNT]);
+ cursor(49, 18);
+ lprintf("%8ld", (long) c[GOLD]);
+ }
+}
+
+#if 0 /* XXX: apparently unused */
+/*
+ subroutine to appraise any stone for the bank
+ */
+static void
+appraise(int gemstone)
+{
+ int j, amt;
+ for (j = 0; j < 26; j++)
+ if (iven[j] == gemstone) {
+ lprintf("\nI see you have %s", objectname[gemstone]);
+ if (gemstone == OLARNEYE)
+ lprcat(" I must commend you. I didn't think\nyou could get it.");
+ lprcat(" Shall I appraise it for you? ");
+ yrepcount = 0;
+ if (getyn() == 'y') {
+ lprcat("yes.\n Just one moment please \n");
+ nap(1000);
+ if (gemstone == OLARNEYE) {
+ amt = 250000 - ((gltime * 7) / 100) * 100;
+ if (amt < 50000)
+ amt = 50000;
+ } else
+ amt = (255 & ivenarg[j]) * 100;
+ lprintf("\nI can see this is an excellent stone, It is worth %ld", (long) amt);
+ lprcat("\nWould you like to sell it to us? ");
+ yrepcount = 0;
+ if (getyn() == 'y') {
+ lprcat("yes\n");
+ c[GOLD] += amt;
+ iven[j] = 0;
+ } else
+ lprcat("no thank you.\n");
+ if (gemstone == OLARNEYE)
+ lprcat("It is, of course, your privilege to keep the stone\n");
+ } else
+ lprcat("no\nO. K.\n");
+ }
+}
+#endif /* 0 - unused */
+
+/*
+ function for the trading post
+ */
+static void
+otradhead(void)
+{
+ clear();
+ lprcat("Welcome to the Larn Trading Post. We buy items that explorers no longer find\n");
+ lprcat("useful. Since the condition of the items you bring in is not certain,\n");
+ lprcat("and we incur great expense in reconditioning the items, we usually pay\n");
+ lprcat("only 20% of their value were they to be new. If the items are badly\n");
+ lprcat("damaged, we will pay only 10% of their new value.\n\n");
+}
+
+void
+otradepost(void)
+{
+ int i, j, value, isub, izarg;
+ dnditm = dndcount = 0;
+ nosignal = 1; /* disable signals */
+ resetscroll();
+ otradhead();
+ while (1) {
+ lprcat("\nWhat item do you want to sell to us [");
+ standout("*");
+ lprcat(" for list, or ");
+ standout("escape");
+ lprcat("] ? ");
+ i = 0;
+ while (i > 'z' || (i < 'a' && i != '*' && i != '\33' && i != '.'))
+ i = ttgetch();
+ if (i == '\33') {
+ setscroll();
+ recalc();
+ drawscreen();
+ nosignal = 0; /* enable signals */
+ return;
+ }
+ isub = i - 'a';
+ j = 0;
+ if (iven[isub] == OSCROLL)
+ if (scrollname[ivenarg[isub]][0] == 0) {
+ j = 1;
+ cnsitm();
+ } /* can't sell unidentified item */
+ if (iven[isub] == OPOTION)
+ if (potionname[ivenarg[isub]][0] == 0) {
+ j = 1;
+ cnsitm();
+ } /* can't sell unidentified item */
+ if (!j) {
+ if (i == '*') {
+ clear();
+ qshowstr();
+ otradhead();
+ } else if (iven[isub] == 0)
+ lprintf("\nYou don't have item %c!", isub + 'a');
+ else {
+ for (j = 0; j < MAXITM; j++)
+ if ((itm[j].obj == iven[isub]) || (iven[isub] == ODIAMOND) || (iven[isub] == ORUBY) || (iven[isub] == OEMERALD) || (iven[isub] == OSAPPHIRE)) {
+ srcount = 0;
+ show3(isub); /* show what the item
+ * was */
+ if ((iven[isub] == ODIAMOND) || (iven[isub] == ORUBY)
+ || (iven[isub] == OEMERALD) || (iven[isub] == OSAPPHIRE))
+ value = 20 * ivenarg[isub];
+ else if ((itm[j].obj == OSCROLL) || (itm[j].obj == OPOTION))
+ value = 2 * itm[j + ivenarg[isub]].price;
+ else {
+ izarg = ivenarg[isub];
+ value = itm[j].price; /* appreciate if a +n
+ * object */
+ if (izarg >= 0)
+ value *= 2;
+ while ((izarg-- > 0) && ((value = 14 * (67 + value) / 10) < 500000));
+ }
+ lprintf("\nItem (%c) is worth %ld gold pieces to us. Do you want to sell it? ", i, (long) value);
+ yrepcount = 0;
+ if (getyn() == 'y') {
+ lprcat("yes\n");
+ c[GOLD] += value;
+ if (c[WEAR] == isub)
+ c[WEAR] = -1;
+ if (c[WIELD] == isub)
+ c[WIELD] = -1;
+ if (c[SHIELD] == isub)
+ c[SHIELD] = -1;
+ adjustcvalues(iven[isub], ivenarg[isub]);
+ iven[isub] = 0;
+ } else
+ lprcat("no thanks.\n");
+ j = MAXITM + 100; /* get out of the inner
+ * loop */
+ }
+ if (j <= MAXITM + 2)
+ lprcat("\nSo sorry, but we are not authorized to accept that item.");
+ }
+ }
+ }
+}
+
+static void
+cnsitm(void)
+{
+ lprcat("\nSorry, we can't accept unidentified objects.");
+}
+
+/*
+ * for the Larn Revenue Service
+ */
+void
+olrs(void)
+{
+ int i, first;
+ long amt;
+ first = nosignal = 1; /* disable signals */
+ clear();
+ resetscroll();
+ cursor(1, 4);
+ lprcat("Welcome to the Larn Revenue Service district office. How can we help you?");
+ while (1) {
+ if (first) {
+ first = 0;
+ goto nxt;
+ }
+ cursors();
+ lprcat("\n\nYour wish? [(");
+ standout("p");
+ lprcat(") pay taxes, or ");
+ standout("escape");
+ lprcat("] ");
+ yrepcount = 0;
+ i = 0;
+ while (i != 'p' && i != '\33')
+ i = ttgetch();
+ switch (i) {
+ case 'p':
+ lprcat("pay taxes\nHow much? ");
+ amt = readnum((long) c[GOLD]);
+ if (amt < 0) {
+ lprcat("\nSorry, but we can't take negative gold\n");
+ amt = 0;
+ } else if (amt > c[GOLD])
+ lprcat(" You don't have that much.\n");
+ else
+ c[GOLD] -= paytaxes(amt);
+ break;
+
+ case '\33':
+ nosignal = 0; /* enable signals */
+ setscroll();
+ drawscreen();
+ return;
+ };
+
+nxt: cursor(1, 6);
+ if (outstanding_taxes > 0)
+ lprintf("You presently owe %ld gp in taxes. ", (long) outstanding_taxes);
+ else
+ lprcat("You do not owe us any taxes. ");
+ cursor(1, 8);
+ if (c[GOLD] > 0)
+ lprintf("You have %6ld gp. ", (long) c[GOLD]);
+ else
+ lprcat("You have no gold pieces. ");
+ }
+}
diff --git a/larn/tok.c b/larn/tok.c
new file mode 100644
index 0000000..6bab595
--- /dev/null
+++ b/larn/tok.c
@@ -0,0 +1,235 @@
+/* $NetBSD: tok.c,v 1.11 2012/06/19 05:30:44 dholland Exp $ */
+
+/* tok.c Larn is copyrighted 1986 by Noah Morgan. */
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: tok.c,v 1.11 2012/06/19 05:30:44 dholland Exp $");
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <ctype.h>
+#include "header.h"
+#include "extern.h"
+
+/* Keystrokes (roughly) between checkpoints */
+#define CHECKPOINT_INTERVAL 400
+
+static char lastok = 0;
+int yrepcount = 0;
+#ifndef FLUSHNO
+#define FLUSHNO 5
+#endif /* FLUSHNO */
+static int flushno = FLUSHNO; /* input queue flushing threshold */
+#define MAXUM 52 /* maximum number of user re-named monsters */
+#define MAXMNAME 40 /* max length of a monster re-name */
+static char usermonster[MAXUM][MAXMNAME]; /* the user named monster
+ * name goes here */
+static u_char usermpoint = 0; /* the user monster pointer */
+
+/*
+ lexical analyzer for larn
+ */
+int
+yylex(void)
+{
+ char cc;
+ int ic;
+ if (hit2flag) {
+ hit2flag = 0;
+ yrepcount = 0;
+ return (' ');
+ }
+ if (yrepcount > 0) {
+ --yrepcount;
+ return (lastok);
+ } else
+ yrepcount = 0;
+ if (yrepcount == 0) {
+ bottomdo();
+ showplayer();
+ } /* show where the player is */
+ lflush();
+ while (1) {
+ c[BYTESIN]++;
+ /* check for periodic checkpointing */
+ if (ckpflag)
+ if ((c[BYTESIN] % CHECKPOINT_INTERVAL) == 0) {
+#ifndef DOCHECKPOINTS
+ savegame(ckpfile);
+#else
+ wait(0); /* wait for other forks to
+ * finish */
+ if (fork() == 0) {
+ savegame(ckpfile);
+ exit();
+ }
+#endif
+ }
+ do { /* if keyboard input buffer is too big, flush
+ * some of it */
+ ioctl(0, FIONREAD, &ic);
+ if (ic > flushno)
+ read(0, &cc, 1);
+ }
+ while (ic > flushno);
+
+ if (read(0, &cc, 1) != 1)
+ return (lastok = -1);
+
+ if (cc == 'Y' - 64) { /* control Y -- shell escape */
+ resetscroll();
+ clear();/* scrolling region, home, clear, no
+ * attributes */
+ if ((ic = fork()) == 0) { /* child */
+ execl("/bin/csh", "/bin/csh", NULL);
+ exit(1);
+ }
+ wait(0);
+ if (ic < 0) { /* error */
+ write(2, "Can't fork off a shell!\n", 25);
+ sleep(2);
+ }
+ setscroll();
+ return (lastok = 'L' - 64); /* redisplay screen */
+ }
+ if ((cc <= '9') && (cc >= '0')) {
+ yrepcount = yrepcount * 10 + cc - '0';
+ } else {
+ if (yrepcount > 0)
+ --yrepcount;
+ return (lastok = cc);
+ }
+ }
+}
+
+/*
+ * flushall() Function to flush all type-ahead in the input buffer
+ */
+void
+flushall(void)
+{
+ char cc;
+ int ic;
+ for (;;) { /* if keyboard input buffer is too big, flush
+ * some of it */
+ ioctl(0, FIONREAD, &ic);
+ if (ic <= 0)
+ return;
+ while (ic > 0) {
+ read(0, &cc, 1);
+ --ic;
+ } /* gobble up the byte */
+ }
+}
+
+/*
+ function to set the desired hardness
+ enter with hard= -1 for default hardness, else any desired hardness
+ */
+void
+sethard(int hard)
+{
+ int j, k, i;
+ struct monst *mp;
+
+ j = c[HARDGAME];
+ hashewon();
+ if (restorflag == 0) { /* don't set c[HARDGAME] if restoring game */
+ if (hard >= 0)
+ c[HARDGAME] = hard;
+ } else
+ c[HARDGAME] = j;/* set c[HARDGAME] to proper value if
+ * restoring game */
+
+ if ((k = c[HARDGAME]) != 0)
+ for (j = 0; j <= MAXMONST + 8; j++) {
+ mp = &monster[j];
+ i = ((6 + k) * mp->hitpoints + 1) / 6;
+ mp->hitpoints = (i < 0) ? 32767 : i;
+ i = ((6 + k) * mp->damage + 1) / 5;
+ mp->damage = (i > 127) ? 127 : i;
+ i = (10 * mp->gold) / (10 + k);
+ mp->gold = (i > 32767) ? 32767 : i;
+ i = mp->armorclass - k;
+ mp->armorclass = (i < -127) ? -127 : i;
+ i = (7 * mp->experience) / (7 + k) + 1;
+ mp->experience = (i <= 0) ? 1 : i;
+ }
+}
+
+/*
+ function to read and process the larn options file
+ */
+void
+readopts(void)
+{
+ const char *i;
+ int j, k;
+ int flag;
+
+ flag = 1; /* set to 0 if a name is specified */
+
+ if (lopen(optsfile) < 0) {
+ strcpy(logname, loginname);
+ return; /* user name if no character name */
+ }
+ i = " ";
+ while (*i) {
+ if ((i = lgetw()) == NULL)
+ break; /* check for EOF */
+ while ((*i == ' ') || (*i == '\t'))
+ i++; /* eat leading whitespace */
+
+ if (strcmp(i, "bold-objects") == 0)
+ boldon = 1;
+ else if (strcmp(i, "enable-checkpointing") == 0)
+ ckpflag = 1;
+ else if (strcmp(i, "inverse-objects") == 0)
+ boldon = 0;
+ else if (strcmp(i, "female") == 0)
+ sex = 0; /* male or female */
+ else if (strcmp(i, "monster:") == 0) { /* name favorite monster */
+ if ((i = lgetw()) == 0)
+ break;
+ strlcpy(usermonster[usermpoint], i, MAXMNAME);
+ if (usermpoint >= MAXUM)
+ continue; /* defined all of em */
+ if (isalpha(j = usermonster[usermpoint][0])) {
+ for (k = 1; k < MAXMONST + 8; k++) /* find monster */
+ if (monstnamelist[k] == j) {
+ monster[k].name = &usermonster[usermpoint++][0];
+ break;
+ }
+ }
+ } else if (strcmp(i, "male") == 0)
+ sex = 1;
+ else if (strcmp(i, "name:") == 0) { /* defining players name */
+ if ((i = lgetw()) == 0)
+ break;
+ strlcpy(logname, i, LOGNAMESIZE);
+ flag = 0;
+ } else if (strcmp(i, "no-introduction") == 0)
+ nowelcome = 1;
+ else if (strcmp(i, "no-beep") == 0)
+ nobeep = 1;
+ else if (strcmp(i, "process-name:") == 0) {
+ if ((i = lgetw()) == 0)
+ break;
+ strlcpy(psname, i, PSNAMESIZE);
+ } else if (strcmp(i, "play-day-play") == 0) {
+ /* bypass time restrictions: ignored */
+ } else if (strcmp(i, "savefile:") == 0) { /* defining savefilename */
+ if ((i = lgetw()) == 0)
+ break;
+ strcpy(savefilename, i);
+ flag = 0;
+ }
+ }
+ if (flag)
+ strcpy(logname, loginname);
+}