[comp.sources.games] v12i037: mdg - multiuser dungeon game, Part01/06

billr@saab.CNA.TEK.COM (Bill Randle) (02/20/91)

Submitted-by: jcg@tree.uucp (Chris Gonnerman)
Posting-number: Volume 12, Issue 37
Archive-name: mdg/Part01
Environment: System V, SunOS 4.1, curses

	[I was able to compile this sucessfully on a Sun SS-2 running
	 SunOS 4.1.1 using the System V environment. I've added a diff
	 to patch the Makefile for compiling under SunOS. If you're
	 running straight SysV, it should as is (with maybe the
	 exception of the 'tinfo' lib.  -br]

[From the author:]
[[Following is the source to MDG, the Multiuser Dungeon Game.  As stated
in the README it is only suitable for use on System V Release 3 and 4,
and SunOS 4.1.  It is a Hack/Larn/Rogue style game, but with a
difference... it allows players true concurrent play.  Up to 9 players
at a time can simultaneously be in the game.  They can ignore each
other, or do battle, or team up to fight the monsters... anything that
they want to do.]]

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 1 (of 6)."
# Contents:  README MANIFEST gmain.c mdg_dir mdg_dir/note.start
#   msghandler.c
# Wrapped by billr@saab on Tue Feb 19 14:49:40 1991
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(19787 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
X@(#) MDG Multi-user Dungeon Game, Copyright 1990 John C. Gonnerman.
X@(#) README Version 1.6, 1/11/91
X
X
XMDG is a multi-user Hack/Larn/Rogue type of game.  It uses System V IPC 
Xcalls (message queues and shared memory) so it is only available for 
Xthose systems.  It is not a "MUD" or any sort of a network game.  
XRather, it is intended to be played on a single Unix-based system, 
Xperhaps a BBS (such as the Tree in Sacramento... there's your plug Alan).
X
XNo reason why it could not be played over Telnet, though... on a LAN it
Xcould really scream!
X
X
XPORTING NOTES:
X
XAs stated, MDG is a SysV game.  Getting it to run on any other system may
Xbe impossible at this time.  It was developed on a Xenix System V host,
Xbut I only used the standard AT&T SysV Release 3.2 manual as a reference.  
XTherefore, I expect it to be fairly portable over most SysV systems.  
XRecent work on SunOS 4.1 leads me to believe that MDG will compile and
Xrun there also.
X
XPlayers of MDG must be identified by number.  The distributed version of
XMDG includes a program, ident.c, which will return the uid number and
Xreal name of a user for use by MDG.  This program is automatically called
Xby MDG to identify a player.  For BBS systems, however, this may not be
Xacceptible; if it is not, you will need to write your own ident.c to
Xprovide this information.  For instance, here on Tree the BBS password
Xfile is consulted by MDG to get the BBS userid-number and the player's
Xhandle.  ident can also be a shell script, if desired, though it will be 
Xslower that way... remember that the slowdown caused by ident affects
XALL current players, since the daemon task must run ident.  It is a 
Xtradeoff to have a separate ident program, but the additional overhead 
Xof the current program seems minimal.
X
XSystem resources:  MDG is tuned (as delivered) for a system with 40
Xmessage headers configured in the kernel.  If you have fewer than this, do
Xnot attempt to use MDG until you reconfigure as the daemon task can hang
Xif insufficient message headers are available.  If you want to use more
X(to speed up MDG's response or allow for more users) tune the
Xqueue-sizing parameters in struct.h.
X
XLikewise, MDG needs sufficient shared memory to store the current
Xplayers, the map, and the item table.  (The monster table is stored in
X"conventional" memory by the daemon, as is the note table).  This is
Xharder to figure because of the variable size of these tables.
X
XFinally, MDG needs as many semaphores as there are to be concurrent 
Xplayers... for 9 players (the maximum, see below) a single semaphore set
Xof 9 semaphores is created.
X
XThe game can handle up to 9 players if it is configured for that many.
XThe shipped version is tuned and configured for only 6.  For purposes of
Xa clean screen display, set MAX_PLAYERS to 3, 6, or 9.  The tuning of
Xmessage queue usage is based on 6 players so you will need more messages
Xto run 9 players.
X
X
XADMINISTRATION:
X
XThe primary MDG game programs will need to be installed in /usr/games (or
Xwherever you store your games.)  These programs are:
X
X	mdg		user-side MDG game program.
X
X	mdg_char	player "editor" used to buy lives, and review
X			stats.
X
X	mdg_daemon	the real game (daemon task).
X
X	mdg_stop,	really one program.  mdg_stop stops the game 
X	mdg_save	daemon (by sending a message).  mdg_save causes
X			the game to write a save file immediately.
X
XThe MDG programs will need to be setuid to "games" (or some other userid
Xwhich will own the files).  Alternately it can be setgid if the files
Xhave correct permission modes.  At the minimum, mdg_char will need this 
Xso it can access player files.  The daemon can safely be made setuid 
Xbecause it will protect itself using a lock file so that it can't be 
Xstarted twice; this would allow players to restart the daemon if the 
Xsystem admin forgets.  If the daemon dumps core, it will not remove it's 
Xlock file, which is a "feature" that prevents a presumably buggy daemon 
Xfrom being restarted.  The user-side game program, mdg, also needs to be 
Xsetuid so it can access the ident program.  The control program, 
Xmdg_stop/mdg_save, should likely NOT be setuid so that players can't stop 
Xthe daemon in progress.
X
XMDG will need a directory (GAME_HOME in setup.h) to store the game save
Xfiles and support programs (ident) in.  The shar'ed version of MDG includes 
Xa public domain set of save files in a subdirectory (mdg_dir) under the 
Xsource directory.
X
XThe following files comprise the game save:
X
X	map.start,	the game map.
X	map.save,
X	map.old
X
X	item.start,	item list save files.
X	item.save, 
X	item.old
X
X	monster.start,	monster table save file.
X	monster.save,
X	monster.old
X
X	note.start,	note table save file.
X	note.save,
X	note.old
X
XThese files contain data that is saved each time the game is stopped, 
Xand each time a player leaves the game.  EXCEPTION:  map saves are done
Xonly when the map has been changed and are delayed until there are no
Xplayers in the game.
X
XThe .start files are used only if the .save files do not exist.  The
X.old files are created by renaming old .save files before writing new
Xones.
X
XAdditionally, in the GAME_HOME directory of a running system you will find:
X
X	LOCK.daemon	used to prevent more than one daemon from being
X			started.  this is just a rudimentary protection.
X
X	errorlog	stderr output of mdg_daemon... check this if
X			the daemon core dumps.
X
X	statlog		when shut down, the daemon saves the peak
X			messages per second it experienced while running
X			in this file.
X
X	bin/mdg_clock	the daemon clock program; sends pulses to the
X			daemon task for timing.
X
X	bin/ident	described above.
X
X	players/default	standard beginning character.
X
X	players/plNNNNN player game save.
X
X	players/prNNNNN player "profile" (just the preferred name).
X
X
XRUNNING THE GAME:
X
XThe daemon task must be started before you can play MDG.  Just run
Xmdg_daemon.  It will find and run the mdg_clock task, automatically.
X(When you shut down the daemon with mdg_stop, mdg_daemon will kill the
Xclock before shutting itself down).
X
XPlayers can then enter the game by running mdg.  Player stats can be
Xviewed, names changed, and extra lives purchased by running mdg_char.
X
XFor players to leave MDG, they must go HOME.  This is done by running off
Xthe map onto a HOME sector.  (See mdg.doc for more info.)  The Quit spell
Xcan also be used.
X
X
XHOW IT WORKS:
X
X	+--------------+      ----------       +-------------------+
X	| Daemon Task  |---->| Shared   |----->| Viewer Task (mdg) |
X	| (mdg_daemon) |     | Memory   |      |-------------------|
X	+--------------+      ----------       | Input Task (mdg)  |
X        	^ ^   		               +-------------------+
X   Message >    | |                                 |
X   Queue   >    | +---------------------------------+       
X		|       
X	+--------------+
X	| Clock Task   |
X	| (mdg_clock)  |
X	+--------------+
X
Xmdg_daemon, when started, starts in turn mdg_clock.  The clock accesses
Xthe daemon's common input queue, and each second sends a "pulse" to
Xcause the daemon to update it's timers and perform monster moves and
Xother scheduled operations.
X
XWhen mdg is started on the user side, it fork()'s into two processes.
XOne is responsible for parsing user input, and sends messages to
Xmdg_daemon on the common input queue.  The other, the Viewer, accesses
Xthe shared memory of the daemon to read map sectors, player stats, and
Xthe item list.
X
XNot shown are the Viewer semaphores.  Each viewer task watches a
Xsemaphore, doing screen updates when the semaphore is raised.  The 
Xdaemon raises a player's semaphore to indicate that a screen update is
Xneeded.
X
XThis is an efficient system in terms of CPU resources.  Each process is
Xsuspended much of the time (the daemon blocked on a message queue, the 
Xviewer(s) blocked on semaphores, the clock sleeping, and the input task 
Xblocked on the keyboard).
X
XThere is an additional shared segment private to each pair of mdg
Xprocesses, used for the "help line" on the bottom of mdg's screen.
X
X
XCREATING YOUR OWN DATA FILES:
X
XFour basic datasets comprise a game startup set: map.start, item.start,
Xnote.start, and monster.start.  The formats of these files are listed
Xbelow:
X
X
XMap File map.start:
X
XThe first line is a sector count.  It is used to avoid a prescan, that
Xis, so the map file need only be read once.  The sector count is used in
Xcreating a shared memory segment large enough for the map.
X
XFollowing this are 16-line long sector entries, consisting of 13 lines
Xof actual sector layout and 3 lines of other data.  A sample sector
Xfollows:
X
X	+-----------------------------------------------+
X	|~                                     .  0	|
X	|~          #..#..#..#..#..#..#        .	|
X	|~~         .                 .        .	|
X	|~~      #..#                 #..#     .	|
X	|~       .                       .     .	|
X	|        #                       #     .	|
X	|        .                       .     .	|
X	|        #                       #     .	|
X	|        .                       .     .	|
X	|        #..#        #..#..#..#..#     .	|
X	|         ~~~~       ~~~~              .	|
X	|   ~~~~~~~~~~~     ~~~~~~~~~~~~~~~    .	|
X	|~~~~~~~~~~~~~      ~~~~~~~~~~~~~~~~~~~.	|
X	|Start Sector...				|
X	|26 -1 1 21 / -3 -3 / -3 -3 -3 -3 -3 -3 -3	|
X	|0 1 0 '~' G0					|
X	+-----------------------------------------------+
X
X(A border has been added for clarity.)
X
XEach of the first 13 lines contains 38 columns of map codes, a single
Xperiod (for ease of editing), and on the first line only, 2 spaces and a
Xsector number.  Note that the first number is 0, so that in a 10 sector
Xmap the highest sector number is 9.
X
XThe 14th line is the sector name, which is allowed to be up to 39
Xcharacters long.
X
XThe 15th line is the link-list.  Each sector connects in up to 11
X"directions" to other sectors.  Negative link numbers refer to one of
Xseveral special locations, as shown in mapstruct.h; the implemented
Xlinks are as follows:
X
X	LOC_HOME	-1 	(used to indicate where players may 
X				exit the game.)
X	LOC_NONESUCH	-3 	(nowhere, used to "block" a direction.)
X	LOC_TRADER	-4	(leads to the Trader's place of 
X				business.)
X
X-2 is LOC_PLAYER, and is not used on the map.
X
XThe link-list is read as follows:
X
X	N S E W / U D / G1 G2 G3 G4 G5 G6 G7
X
Xwhere N is North, S is South, etc., U is Up, D is Down, and the Gn
Xentries are teleport gates 1 to 7.
X
XNote that the link-list is used only to LEAVE a sector; entry to a
Xsector depends entirely on the link-list of the sector you are leaving.
X
XLine 16 of the sector is a general data line, as follows:
X
X	   0 1 0 '~' G0
X 	   ^ ^ ^  ^  ^
Xlevel -----+ | |  |  |
Xlight -------+ |  |  |
Xjump inhibit --+  |  |
Xborder character -+  |
Xgod limit -----------+
X
XLevel is used to distribute monsters; see below, under monster.start.
X
XLight may be one of the following levels:
X
X	-1	Tends to "cancel" player-provided light.
X	0	"Normal" darkness.
X	1	Lighted in the day only.
X	2	Lighted full-time.
X
XJump inhibit is used to limit teleportation to or from sectors.  It only
Xlimits teleportation by spell use, that is, it does not affect gates.
X
XBorder Character is a single character to show on the borders of a sector 
Xwhich lead to LOC_NONESUCH.
X
XGod Limit is used to block or limit Godlike map changes in the sector.  It 
Xmay be an integer from 0 up.  1 or higher is interpreted as a multiplier 
Xto apply to the cost in Creation Points spent modifying the sector.  If 
Xthe God Limit is 0, modifications are not allowed.
X
X
XItem File item.start:
X
XThis file, like map.start (and in fact like the other game save files)
Xbegins with a count of items.  Following the item count are the items,
Xwhere each item consists of two lines.  The first line of an item is
Xit's name, and the second is a stat record in the following format:
X
X	[0] 5, 6 <$$, 0, 0> 1:1 0 $600 (0) C0
X         ^  ^  ^  ^^  ^  ^  ^ ^ ^  ^    ^   ^
Xsector --+  |  |  ||  |  |  | | |  |    |   |
Xx ----------+  |  ||  |  |  | | |  |    |   |
Xy -------------+  ||  |  |  | | |  |    |   |
Xtype -------------+|  |  |  | | |  |    |   |
Xsymbol ------------+  |  |  | | |  |    |   |
Xeffect or value ------+  |  | | |  |    |   |
Xrange -------------------+  | | |  |    |   |
Xmax uses -------------------+ | |  |    |   |
Xremaining uses ---------------+ |  |    |   |
Xage ----------------------------+  |    |   |
Xcost ------------------------------+    |   |
Xmodification count ---------------------+   |
Xcurse flag ---------------------------------+
X
XSector, x, and y indicate where the item is.  If the sector is negative,
Xthe meaning is as follows:
X
X	-2	LOC_PLAYER... x is the player number who has it, y is 0.
X	-3	LOC_NONESUCH... the item is "lost," but may turn up when
X		a monster dies.
X	-4	LOC_TRADER... the item is in the Trader's.
X
X-1 (LOC_HOME) is meaningless here.
X
XType indicates what sort of item it is.  Symbol usually is the same
Xcode, but may be an underscore, which indicates an Obscured item.
X
XEffect (or value) is used as max damage for weapons, as protection for
Xarmor or shields, as note index for notes, and for other objects it is
Xused to indicate what effect or power the item confers.  See the
Xeffects.h file for more details.  Note that any cursed object other than
Xweapons and armor/shields acts as POISON! when activated by a player.
X
XThe handling of $ (treasure) and * (crystal) items is special... the
Xsector number is never LOC_PLAYER.  Instead, when such an item is picked
Xup it is set to LOC_NONESUCH and the value of the item is added to a
Xcounter in the player structure.  Do not create an item file with no
Xentries of these types!
X
XRange is normally only used by missile weapons, although I have plans in
Xa future release to use it as an "effectiveness" value for generic
Xobjects.
X
XMax Uses and Remaining Uses are normally used only on potions, scrolls,
Xand "objects" (these three item types are the ones I refer to as
X"generic objects").
X
XAge is used to prevent an item from lying about on the map, in the
XTrader's, or on an idle player for too long.  We had problems in
Xplaytest with players who would get ahold of all the good items, and
Xthen leave for a month on vacation.  So, item aging (or "decay") was
Xadded using this field to put items in LOC_NONESUCH after a certain
Xdelay (defined in setup.h as the DECAY_* parameters).
X
XCost is simply the base cost (value when sold) of the item.  It is
Xmultiplied by 1.5 when a player buys something.
X
XModification Count is used to limit changes to items made by Godlike
Xcharacters.
X
XCurse flag may be C0 (not cursed) or C1 (cursed).  Godlike characters
Xcan remove or apply curses to items.  A generic cursed item acts as
XPOISON! to the character activating it.  Cursed notes are unreadable.
XCursed weapons do little or no damage, and cursed armor and shields
Xprovide no protection.
X
X
XMonster File monster.start:
X
XThis file starts with a count of monsters.  Each monster is thereafter
Xrepresented in two lines, the first being the monster's name, and the
Xsecond being a stat record as follows:
X
X	a [-3] 24, 11 15:15 2 0/60:10 L3 D2 IQ=50
X	^  ^   ^   ^  ^  ^  ^ ^ ^  ^  ^  ^  ^
Xcode ---+  |   |   |  |  |  | | |  |  |  |  |
Xsector ----+   |   |  |  |  | | |  |  |  |  |
Xx -------------+   |  |  |  | | |  |  |  |  |
Xy -----------------+  |  |  | | |  |  |  |  |
Xmax hit points -------+  |  | | |  |  |  |  |
Xcurrent hit points ------+  | | |  |  |  |  |
Xmovement -------------------+ | |  |  |  |  |
Xattack type ------------------+ |  |  |  |  |
Xto hit -------------------------+  |  |  |  |
Xmax damage ------------------------+  |  |  |
Xmap level ----------------------------+  |  |
Xdanger level ----------------------------+  |
Xintelligence -------------------------------+
X
XCode is the character (must be a letter or ampersand) to be used to
Xdisplay the monster.  Use of a character other than a letter or
Xampersand will result in a monster immune to almost all attacks.
X
XSector, x, and y are used to indicate the monster's current location.
XDead monsters have a -3 sector (LOC_NONESUCH).  No other negative sector
Xnumber is meaningful.
X
XMax Hit Points and Current Hit Points indicate the potential damage the
Xmonster can take before death.  Monsters do not heal.  However, a dead
Xmonster can be resurrected automatically...
X
XMovement is the number of moves per "pulse" that the monster may take.
X
XAttack type is used to select between normal attacks and several special
Xmonster attacks... see the file "combat.h" for details.
X
XTo Hit and Damage are used (in most attack types) as the odds to hit a
Xplayer and the maximum damage that may be done.  The To Hit value is
Xcompared with the player's Fighting score to generate an actual percent
Xchance.
X
XMap Level is used to distribute the monster when resurrecting it.  A
Xmonster of level N may be resurrected in any sector of level N up to and
Xincluding level N+4.  Thus a "level band" is 5 levels wide.
X
XDanger Level is used to determine how much treasure the monster is
Xworth.  Danger Levels equal to or greater than DANGER_CUT (setup.h) will
Xresult in a monster able to "cough up" Magic Crystals when it dies.
X
XIntelligence is used to determine if the monster uses the stupid or
Xsmart movement routine.  It is a simple percentage chance that the
Xmonster will select a smart move.  Note that smart moves are not all
XTHAT smart.
X
X
XNote File note.start:
X
XThis file consists of a record count, followed by the note text, one
Xnote per line.  Notice that more than one note item can reference the
Xsame note in the note table... see the Mystic Slate(s) in the example
Xitem.start file.
X
X
XTIPS ON ADMINISTRATION:
X
X- Monsters are moved on the "pace and pulse with tick limit" method.
X  The original (beta) version of MDG used "pace and pulse"... monsters
X  each being moved once for each player move (if the monster had
X  movement left) with all remaining moves, if any, being made all at
X  once on the pulse.  This however was shown to move monsters all at
X  once pretty much at the beginning of the pulse.  Therefore, monsters
X  are moved in this version once per each N player moves.  Exception:  
X  if any monster is under attack, monsters move once per player attack.
X  "N" is set to the value TICKSPERPACE in setup.h.  It may need to be
X  increased on fast machines or decreased on slow ones.  Use the values
X  in the file "statlog" to help with the tuning... the value of
X  TICKSPERPACE should be around 1/4th or 1/5th of the Peak Messages per
X  Second of mdg_daemon.
X
X- Avoid giving out CP for free.  Players with possibly excellent ideas
X  will beg the game administrator to give them a few extra CP so they
X  can finish the area they are building.  Don't give in.  CP are hard to
X  come by for a reason... there would be little challenge if they were
X  cheap.
X
X  However YOU (the game admin) should have a large number of CP much of
X  the time, to allow you to patch up the map as needed.
X
X- By far the hardest part of administrating MDG is the God Powers.  Once
X  you have a Godlike player in the game, the map will begin undergoing
X  strange metamorphoses.  It is HIGHLY suggested, if you remodel the
X  sample map or build your own, that you LOCK (God Limit 0) all sectors
X  that have links to HOME in them.
X
X- The game limit values (in setup.h) can be changed to give a much
X  different feel to the game.  You might wish to raise the limits of God
X  Powers to make only the VERY toughest players Godlike.  Other limits
X  can be altered too... spell durations, limits for the levels of the
X  Detect spell, etc.
X
X- The Trader is located near HOME as a way of assisting newbie players.
X  You might make it harder to get to if your players require more of a
X  challenge... but don't remove it entirely.  Note that regardless of
X  how many ways you can enter the Trader's, there is effectively only
X  one Trader in the game.  One suggestion is to place the Traders at the
X  top or bottom of a set of stairs (what a surprise).
X
END_OF_FILE
if test 19787 -ne `wc -c <'README'`; then
    echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'MANIFEST' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'MANIFEST'\"
else
echo shar: Extracting \"'MANIFEST'\" \(1911 characters\)
sed "s/^X//" >'MANIFEST' <<'END_OF_FILE'
X   File Name		Archive #	Description
X-----------------------------------------------------------
X Copyright                  3	
X MANIFEST                   1	This shipping list
X Makefile                   5	
X Mk_sun.diff                6
X README                     1	
X clock.c                    5	
X cmain.c                    4	
X combat.c                   3	
X combat.h                   6	
X config.h                   6	
X dayclock.c                 6	
X defstruct.h                5	
X dident.c                   6	
X dmain.c                    5	
X dmsg.c                     6	
X effect.c                   4	
X effect.h                   6	
X files.h                    6	
X findplayer.c               5	
X genstruct.h                6	
X gident.c                   5	
X gmain.c                    1	
X godpower.c                 4	
X help.c                     5	
X help.h                     6	
X ident.c                    5	
X improve.c                  5	
X input.c                    3	
X loadconfig.c               5	
X magic.c                    3	
X mapstruct.h                6	
X mdg.doc                    4	
X mdg_dir                    1	
X mdg_dir/NoCopyright        5	
X mdg_dir/item.start         5	
X mdg_dir/map.start          2	
X mdg_dir/monster.start      5	
X mdg_dir/note.start         1	
X messages.h                 5	
X monsters.c                 5	
X monstruct.h                6	
X msghandler.c               1	
X msgstruct.h                6	
X players.c                  3	
X plrstruct.h                5	
X random.c                   6	
X ranged.c                   5	
X seg.c                      4	
X setup.h                    5	
X show.c                     4	
X show.h                     6	
X speak.c                    5	
X spells.c                   6	
X spells.h                   6	
X stopdaemon.c               6	
X strstr.c                   6	
X struct.h                   4	
X traps.c                    6	
END_OF_FILE
if test 1911 -ne `wc -c <'MANIFEST'`; then
    echo shar: \"'MANIFEST'\" unpacked with wrong size!
fi
# end of 'MANIFEST'
fi
if test -f 'gmain.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'gmain.c'\"
else
echo shar: Extracting \"'gmain.c'\" \(5884 characters\)
sed "s/^X//" >'gmain.c' <<'END_OF_FILE'
X/*
X	MDG Multiuser Dungeon Game -- mdg
X	
X	MDG is Copyright 1990 John C. Gonnerman
X	This program is subject to the general MDG 
X	copyright statement (see enclosed file, Copyright).
X*/
X
X
Xstatic char *sccsx = "@(#) MDG Copyright 1990 John C. Gonnerman";
Xstatic char *sccsvers = "@(#) gmain.c\t(1.4)\tcreated 1/3/91";
X
X#include <curses.h>
X#include <fcntl.h>
X#include <signal.h>
X#include <string.h>
X#include <ctype.h>
X
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <sys/ipc.h>
X#include <sys/msg.h>
X#include <sys/sem.h>
X#include <sys/shm.h>
X
X#include "setup.h"
X#include "show.h"
X#include "files.h"
X#include "struct.h"
X#include "messages.h"
X#include "effect.h"
X
Xchar *progname;
X
Xextern long dmsgkey, mapkey, playerkey;
X
Xstruct player_seg *pseg;
Xstruct map_seg *mseg;
X
Xstruct termio term, save;
X
Xextern int errno;
Xextern char *sys_errlist[];
X
Xextern char *spell_names[];
X
Xint pid, dqid, gseg_id, playernum, player_indx;
Xchar handle[20];
X
Xstruct game_msg *gseg;
X
X
Xmain(argc, argv)
Xint argc;
Xchar **argv;
X{
X	int ch, m_sem, p_sem, i_sem;
X	void game_over(), shutdown(); 
X	int eputc();
X	struct dmessage dmsg_buf;
X	struct sembuf ops[1];
X
X	if((progname = strrchr(argv[0], '/')) != NULL)
X		progname++;
X	else
X		progname = argv[0];
X
X	if(argc != 1) {
X		fprintf(stderr, ARGCOUNT, progname, argc);
X		exit(1);
X	}
X
X	setupterm(NULL, 2, NULL);
X	tputs(tigetstr("clear"), 1, eputc);
X
X	fputs(BAR, stderr);
X	fputs(TITLE, stderr);
X	fputs(COPYRIGHT, stderr);
X	fputs(PRESSMSG, stderr);
X	fputs(INDENT, stderr);
X
X	io_init();
X	(void)getchar();
X	io_reset();
X
X	fputs(WAITMSG, stderr);
X
X	chdir(GAME_HOME);
X
X	if((playernum = get_player(handle)) == -1) {
X		fputs(UNKPLAYER, stderr);
X		exit(1);
X	}
X
X	/* load keys from config file */
X
X	loadconfig();
X
X	/* open message queue */
X
X	if((dqid = msgget(dmsgkey, 0)) == -1) {
X		fputs(NOTONLINE, stderr);
X		exit(1);
X	}
X
X	/* set key interrupt traps */
X
X	signal(SIGINT, SIG_IGN);
X	signal(SIGQUIT, SIG_IGN);
X
X	/* create our private segment */
X
X	gseg_id = shmget(IPC_PRIVATE, sizeof(struct game_msg), 
X			IPC_CREAT | 0700);
X
X	if((gseg = (struct game_msg *)shmat(gseg_id, 0, 0)) == 
X		(struct game_msg *)-1) {
X			fprintf(CANTERR, progname, "attach private segment",
X				sys_errlist[errno]);
X			exit(1);
X	}
X
X	gseg->trader_offset = -1;
X	gseg->clear_ok = 0;
X
X	/* attach the daemon segments. */
X
X	seg_attach();
X
X	/* set other signal traps */
X
X	signal(SIGHUP, shutdown);
X
X	/* log in to the daemon */
X
X	dmsg_buf.msg_type = 1L;
X	dmsg_buf.playernum = playernum;
X	dmsg_buf.cmd = 'J';
X	tell_daemon(&dmsg_buf);
X
X	player_indx = wait_connect();
X
X	fputs(READYMSG, stderr);
X
X	/* start the subprocess. */
X
X	if((pid = fork()) == -1) {
X		fprintf(stderr, CANTERR, progname, 
X			"fork()", sys_errlist[errno]);
X		exit(1);
X	}
X
X	if(pid == 0)
X		input_process();
X
X	/* set up the terminal */
X
X	initscr();
X	curs_set(0);
X	io_init();
X
X	/* we got it, let's do it! */
X
X	m_sem = mseg->map_sem;
X	show_map();
X
X	i_sem = pseg->item_sem;
X	show_items();
X
X	p_sem = pseg->player_sem;
X	show_player();
X
X	show_gmsg();
X	show_modes();
X
X	while(1) {
X
X		if(pseg->p[player_indx].loc.sector == LOC_NONESUCH
X		|| pseg->p[player_indx].loc.sector == LOC_HOME)
X			game_over();
X
X		if(pseg->p[player_indx].loc.sector == LOC_TRADER) {
X			if(gseg->trader_offset < 0)
X				gseg->trader_offset = 0;
X			show_trader();
X		} else if(m_sem != mseg->map_sem) { 
X			gseg->trader_offset = -1;
X			m_sem = mseg->map_sem;
X			show_map();
X		} 
X
X		if(i_sem != pseg->item_sem) {
X			i_sem = pseg->item_sem;
X			show_items();
X		}
X
X		if(p_sem != pseg->player_sem) {
X			p_sem = pseg->player_sem;
X			show_player();
X		}
X
X		show_gmsg();
X		show_modes();
X
X		refresh();
X
X		ops[0].sem_num = player_indx;
X		ops[0].sem_op = -1;
X		ops[0].sem_flg = 0;
X
X		semop(pseg->sid, ops, 1);
X
X		switch(gseg->clear_ok) {
X		case 1 :
X			gseg->clear_ok = 0;
X			clearok(stdscr, TRUE);
X			break;
X		}
X	}
X}
X
X
Xeputc(ch)
Xint ch;
X{
X	putc(ch, stderr);
X}
X
X
Xio_init()
X{
X	ioctl(0, TCGETA, &term);
X	save = term;
X	term.c_iflag = 0;
X	term.c_oflag = 0;
X	term.c_lflag = ISIG;
X	term.c_cc[VMIN] = 1;
X	term.c_cc[VTIME] = 7;
X	ioctl(0, TCSETA, &term);
X}
X
X
Xio_reset()
X{
X	ioctl(0, TCSETA, &save);
X}
X
X
Xseg_attach()
X{
X	int seg_id;
X
X	seg_id = shmget(playerkey, 0, 0);
X	pseg = (struct player_seg *) shmat(seg_id, (char *)0, SHM_RDONLY);
X
X	seg_id = shmget(mapkey, 0, 0);
X	mseg = (struct map_seg *) shmat(seg_id, (char *)0, SHM_RDONLY);
X}
X
X
Xvoid game_over()
X{
X	struct dmessage msg_buf;
X	int status;
X
X	move(PARK_Y, PARK_X + 2);
X	clrtoeol();
X	move(PARK_Y, PARK_X + 2);
X
X	if(pseg->p[player_indx].loc.sector == LOC_NONESUCH)
X		addstr(DIED);
X	else
X		addstr(GAMEOVER);
X
X	refresh();
X
X	msg_buf.msg_type = 1L;
X	msg_buf.playernum = playernum;
X	msg_buf.cmd = 'Q';
X	tell_daemon(&msg_buf);
X
X	kill(pid, SIGTERM);
X	wait(&status);
X
X	io_reset();
X
X	noecho();
X	while(getch() != '\n');
X
X	endwin();
X
X	tputs(tigetstr("clear"), 1, eputc);
X
X	shmdt(mseg);
X	shmdt(pseg);
X
X	shmdt(gseg);
X	shmctl(gseg_id, IPC_RMID, NULL);
X
X	exit(0);
X}
X
X
Xvoid shutdown()
X{
X	struct dmessage msg_buf;
X
X	msg_buf.msg_type = 1L;
X	msg_buf.playernum = playernum;
X	msg_buf.cmd = 'A';
X	tell_daemon(&msg_buf);
X
X	shmdt(mseg);
X	shmdt(pseg);
X
X	shmdt(gseg);
X	shmctl(gseg_id, IPC_RMID, NULL);
X
X	kill(pid, SIGTERM);
X
X	endwin();
X
X	exit(0);
X}
X
X
Xint wait_connect()
X{
X	int i, indx;
X	struct dmessage dmsg_buf;
X
X	for(i = 0; i < 20; i++) {
X		sleep(1);
X
X		for(indx = 0; indx < MAX_PLAYERS; indx++)
X			if(pseg->p[indx].playernum == playernum)
X				return indx;
X	}
X
X	fputs(NORESPONSE, stderr);
X
X	dmsg_buf.msg_type = 1L;
X	dmsg_buf.playernum = playernum;
X	dmsg_buf.cmd = 'A';
X	tell_daemon(&dmsg_buf);
X
X	shmdt(mseg);
X	shmdt(pseg);
X
X	shmdt(gseg);
X	shmctl(gseg_id, IPC_RMID, NULL);
X
X	kill(pid, SIGTERM);
X
X	exit(1);
X}
X
X
Xint trader_item(p_item_indx)
Xint p_item_indx;
X{
X	int i, cnt;
X
X	cnt = 0;
X
X	for(i = 0; i < pseg->item_count; i++) {
X		if(pseg->itm[i].loc.sector == LOC_TRADER) {
X			if(cnt == (p_item_indx + gseg->trader_offset))
X				return i;
X			cnt++;
X		}
X	}
X
X	return -1;
X}
X
X
X/* end of file. */
END_OF_FILE
if test 5884 -ne `wc -c <'gmain.c'`; then
    echo shar: \"'gmain.c'\" unpacked with wrong size!
fi
# end of 'gmain.c'
fi
if test ! -d 'mdg_dir' ; then
    echo shar: Creating directory \"'mdg_dir'\"
    mkdir 'mdg_dir'
fi
if test -f 'mdg_dir/note.start' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'mdg_dir/note.start'\"
else
echo shar: Extracting \"'mdg_dir/note.start'\" \(42 characters\)
sed "s/^X//" >'mdg_dir/note.start' <<'END_OF_FILE'
X5
Xhuh?
XSure I will.
XR U Sure?
Xbah!
Xwhat?]
END_OF_FILE
if test 42 -ne `wc -c <'mdg_dir/note.start'`; then
    echo shar: \"'mdg_dir/note.start'\" unpacked with wrong size!
fi
# end of 'mdg_dir/note.start'
fi
if test -f 'msghandler.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'msghandler.c'\"
else
echo shar: Extracting \"'msghandler.c'\" \(27418 characters\)
sed "s/^X//" >'msghandler.c' <<'END_OF_FILE'
X/*
X	MDG Multiuser Dungeon Game -- msghandler.c message code
X	
X	MDG is Copyright 1990 John C. Gonnerman
X	This program is subject to the general MDG 
X	copyright statement (see enclosed file, Copyright).
X
X	Unfortunately, most of the daemon is in this file
X	somewhere...
X*/
X
Xstatic char *sccsvers = "@(#) msghandler.c\t(1.7)\tcreated 1/7/91";
X
X#include <stdio.h>
X#include <sys/types.h>
X#include <sys/ipc.h>
X#include <sys/msg.h>
X#include <sys/sem.h>
X
X#include "setup.h"
X#include "struct.h"
X
Xextern char *progname;
X
Xextern int errno;
Xextern char *sys_errlist[];
X
Xextern struct map_seg *mseg;
Xextern struct player_seg *pseg;
X
Xextern int dqid, map_size, player_size;
X
Xextern struct monster_tbl *m_table;
Xextern int n_monster;
X
Xextern struct note_tbl *note_table;
Xextern int n_notes;
X
Xint save_count = 0;
Xint minutes = 0;
X
X
Xmsg_handler(mbuf)
Xstruct dmessage mbuf;
X{
X	int indx, symbol, act_val;
X	static int ticker = 0;
X
X	if(mbuf.cmd == '@') /* stop daemon */
X		shutdown();
X	
X	if(mbuf.cmd == '$') { /* checkpoint game */
X		savegame();
X		save_count = 0;
X		return;
X	}
X
X	if(mbuf.cmd == '{') { /* tell all */
X		dmsg_all(mbuf.text);
X		return;
X	}
X
X	if(mbuf.playernum == -1) { /* "heartbeat" */
X		move_monsters();
X		heal_players();
X		update_timer();
X		move_ranged();
X		notify_all();
X		minute_clock();
X		savemap();
X		return;
X	}
X
X	switch(mbuf.cmd) {
X	case 'J' : /* Join game */
X		if(find_player(mbuf.playernum) != -1)
X			break;
X		if((indx = getfree()) == -1)
X			break;
X		loadplayer(indx, mbuf.playernum);
X		break;
X
X	case 'Q' : /* Quit game */
X		if((indx = find_player(mbuf.playernum)) == -1)
X			break;
X		saveplayer(indx);
X		savegame();
X		save_count = 0;
X		break;
X
X	case 'A' : /* Abort game */
X		if((indx = find_player(mbuf.playernum)) == -1)
X			break;
X		abortplayer(indx);
X		savegame();
X		save_count = 0;
X		break;
X
X	case 'e' : /* enter */
X		if((indx = find_player(mbuf.playernum)) == -1)
X			break;
X		enter_cmd(indx);
X		break;
X		
X	case 's' : /* speak */
X		if((indx = find_player(mbuf.playernum)) == -1)
X			break;
X		speak(indx, mbuf.text);
X		break;
X		
X	case 'S' : /* sell */
X		if((indx = find_player(mbuf.playernum)) == -1)
X			break;
X
X		if(delete_item(indx, mbuf.arg) == -1)
X			break;
X
X		deselect(indx, mbuf.arg);
X
X		pseg->itm[mbuf.arg].loc.sector = LOC_TRADER;
X		pseg->item_sem++;
X
X		pseg->p[indx].gold += pseg->itm[mbuf.arg].value;
X		pseg->player_sem++;
X
X		tell_player(indx);
X
X		break;
X
X	case 'B' : /* buy */
X		if((indx = find_player(mbuf.playernum)) == -1)
X			break;
X
X		if(item_cnt(indx) >= PMAX_ITEMS) {
X			dmsg_add(indx, "You are overloaded.");
X			break;
X		}
X
X		act_val = pseg->itm[mbuf.arg].value * 3 / 2;
X
X		if(act_val > pseg->p[indx].gold) {
X			dmsg_add(indx, "You can't afford it!");
X			break;
X		}
X
X		insert_item(indx, mbuf.arg);
X
X		pseg->p[indx].gold -= act_val;
X		pseg->player_sem++;
X
X		pseg->itm[mbuf.arg].decay_cnt = 0;
X		pseg->itm[mbuf.arg].loc.sector = LOC_PLAYER;
X		pseg->itm[mbuf.arg].loc.x = pseg->p[indx].playernum;
X		pseg->itm[mbuf.arg].symbol = pseg->itm[mbuf.arg].type;
X		pseg->itm[mbuf.arg].loc.y = ITEM_NEW;
X
X		pseg->item_sem++;
X
X		tell_player(indx);
X
X		break;
X
X	case 'L' : /* leave */
X		if((indx = find_player(mbuf.playernum)) == -1)
X			break;
X		if(pseg->p[indx].loc.sector != LOC_TRADER)
X			break;
X
X		if(place(indx, pseg->p[indx].loc.x) == -1) {
X			saveplayer(indx);
X			pseg->p[indx].loc.sector = LOC_HOME;
X			pseg->player_sem++;
X		}
X
X		move_sym(&(pseg->p[indx].loc), 1 + indx);
X
X		pseg->in_trader = -1;
X		tell_player(indx);
X		break;
X
X	case 'p' : /* partner */
X		if((indx = find_player(mbuf.playernum)) == -1)
X			break;
X		if(mbuf.subcmd < 0 || mbuf.subcmd >= MAX_PLAYERS)
X			break;
X		pseg->p[indx].partners[mbuf.subcmd] =
X			(pseg->p[indx].partners[mbuf.subcmd] ? 0 : 1);
X		break;
X		
X	case 't' : /* take */
X		if((indx = find_player(mbuf.playernum)) == -1)
X			break;
X		take_item(indx);
X		break;
X
X	case 'm' : /* move */
X		if((indx = find_player(mbuf.playernum)) == -1)
X			break;
X		symbol = pseg->p[indx].invis ? indx + 11 : indx + 1;
X		move_cmd(&(pseg->p[indx].loc), mbuf.subcmd, symbol, indx);
X		break;
X
X	case 'D' : /* drop cash */
X		if((indx = find_player(mbuf.playernum)) == -1)
X			break;
X		drop_cash(indx, mbuf.subcmd, mbuf.arg);
X		break;
X
X	case 'd' : /* drop item */
X		if((indx = find_player(mbuf.playernum)) == -1)
X			break;
X		drop_cmd(indx, mbuf.subcmd, mbuf.arg);
X		break;
X
X	case 'u' : /* use item */
X		if((indx = find_player(mbuf.playernum)) == -1)
X			break;
X		use_cmd(indx, &mbuf);
X		break;
X
X	case 'v' : /* verbose */
X		if((indx = find_player(mbuf.playernum)) == -1)
X			break;
X		if(pseg->p[indx].brief) {
X			pseg->p[indx].brief = 0;
X			dmsg_add(indx, "Verbose ON");
X		} else {
X			pseg->p[indx].brief = 1;
X			dmsg_add(indx, "Verbose OFF");
X		}
X		break;
X
X	case 'w' : /* write */
X		write_cmd(mbuf.arg, mbuf.text);
X		break;
X
X	case 'c' : /* cast spell */
X		if((indx = find_player(mbuf.playernum)) == -1)
X			break;
X		cast_spell(indx, &mbuf);
X		break;
X
X	case 'g' : /* god power */
X		if((indx = find_player(mbuf.playernum)) == -1)
X			break;
X		god_power(indx, &mbuf);
X		break;
X
X	default:
X		break;
X	}
X
X	if(++ticker == TICKSPERPACE) {
X		pace_monsters();
X		ticker = 0;
X	}
X
X	pace_ranged();
X}
X
X
Xnotify_all()
X{
X	int i;
X
X	mseg->map_sem++;
X	pseg->player_sem++;
X	pseg->item_sem++;
X
X	for(i = 0; i < MAX_PLAYERS; i++)
X		if(pseg->p[i].playernum >= 0)
X			tell_player(i);
X}
X
X
Xenter_cmd(indx)
Xint indx;
X{
X	struct location *l;
X	int symbol;
X
X	l = &(pseg->p[indx].loc);
X
X	symbol = pseg->p[indx].invis ? indx + 11 : indx + 1;
X
X	if(pseg->p[indx].loc.under == '<')  /* down */
X		move_cmd(l, L_DOWN, symbol, indx);
X	else if(pseg->p[indx].loc.under == '>')  /* up */
X		move_cmd(l, L_UP, symbol, indx);
X}
X
X
Xint player_online(p_num)
Xint p_num;
X{
X	int i;
X
X	for(i = 0; i < MAX_PLAYERS; i++)
X		if(pseg->p[i].playernum == p_num)
X			return 1;
X
X	return 0;
X}
X
X
Xminute_clock()
X{
X	if(minutes++ < MINUTE)
X		return;
X
X	minutes = 0;
X
X	if(++(mseg->dayclock) > GAMEDAY)
X		mseg->dayclock = 0;
X
X	switch(mseg->dayclock) {
X	case 0 :
X		dmsg_all("It's morning.");
X		break;
X	case G_MORNING :
X		dmsg_all("It's noon.");
X		break;
X	case G_AFTERNOON :
X		dmsg_all("It's evening.");
X		break;
X	case G_EVENING :
X		dmsg_all("It's night.");
X		break;
X	}
X
X	decay_items();
X}
X
X
Xdecay_items()
X{
X	int i, sector, x;
X
X	for(i = pseg->item_count - 1; i >= 0; i--) {
X		sector = pseg->itm[i].loc.sector;
X		x = pseg->itm[i].loc.x;
X
X		if(sector > 0 
X		|| (sector == LOC_PLAYER && !player_online(x))
X		|| (sector == LOC_TRADER))
X			pseg->itm[i].decay_cnt++;
X		else
X			pseg->itm[i].decay_cnt = 0;
X
X		if(pseg->itm[i].type == NOTE)
X			pseg->itm[i].decay_cnt = 0;
X
X		if(pseg->itm[i].type == CRYSTAL
X		&& pseg->itm[i].decay_cnt > DECAY_CRYSTAL)
X			recycle(i);
X		else if(pseg->itm[i].symbol == HIDDEN
X		&& pseg->itm[i].decay_cnt > DECAY_OBSCURE)
X			recycle(i);
X		else if(sector > 0 
X		&& pseg->itm[i].decay_cnt > DECAY_MAP)
X			recycle(i);
X		else if(sector == LOC_PLAYER 
X		&& pseg->itm[i].decay_cnt > DECAY_PLAYER)
X			recycle(i);
X		else if(sector == LOC_TRADER 
X		&& pseg->itm[i].decay_cnt > DECAY_TRADER
X		&& pseg->in_trader < 0)
X			recycle(i); 
X	}
X}
X
X
Xfreshen_item(indx)
Xint indx;
X{
X	pseg->itm[indx].decay_cnt = 0;
X}
X
X
Xrecycle(indx)
Xint indx;
X{
X	int sector, x, y;
X
X	if(!top_item(indx)) {
X		pseg->itm[indx].decay_cnt /= 2;
X		return;	
X	}
X
X	sector = pseg->itm[indx].loc.sector;
X	x = pseg->itm[indx].loc.x;
X	y = pseg->itm[indx].loc.y;
X 
X	if(sector >= 0) {
X		mseg->m[sector].map[y][x] = pseg->itm[indx].loc.under;
X		mseg->map_sem++;
X		notify(sector);
X	}
X
X	pseg->itm[indx].loc.sector = LOC_NONESUCH;
X	pseg->itm[indx].decay_cnt = 0;
X
X	pseg->item_sem++;
X}
X
X
Xupdate_timer()
X{
X	int indx;
X
X	if(++save_count > CHECKP_LEVEL) {
X		savegame();
X		save_count = 0;
X	}
X	
X	for(indx = 0; indx < MAX_PLAYERS; indx++) {
X
X		if(pseg->p[indx].blocked > 0)
X			pseg->p[indx].blocked--;
X
X		if(pseg->p[indx].user_spell > 0)
X			pseg->p[indx].user_spell--;
X
X		if(pseg->p[indx].ma_count > 0)
X			pseg->p[indx].ma_count--;
X
X		if(pseg->p[indx].knight > 0)
X			pseg->p[indx].knight--;
X
X		if(pseg->p[indx].invis > 0) {
X			pseg->p[indx].invis--;
X
X			if(pseg->p[indx].invis == 0)
X				visible(indx);
X		}
X
X		if(pseg->p[indx].detect > 0)
X			pseg->p[indx].detect--;
X
X		if(pseg->p[indx].light > 0) {
X			pseg->p[indx].light--;
X
X			if(pseg->p[indx].playernum >= 0
X			&& pseg->p[indx].light == 10)
X				dmsg_add(indx, "it's getting dark...");
X		}
X		pseg->p[indx].attacks = P_ATTACKS;
X	}
X}
X
X
Xwrite_cmd(i_indx, text)
Xint i_indx;
Xchar *text;
X{
X	int n_indx;
X
X	if((n_indx = pseg->itm[i_indx].effect) < 0 || n_indx >= n_notes)
X		return;
X
X	strncpy(note_table[n_indx].text, text, 34);
X	note_table[n_indx].text[34] = '\0';
X}
X
X
Xuse_cmd(p_indx, mptr)
Xint p_indx;
Xstruct dmessage *mptr;
X{
X	int playernum, item_indx, dir;
X
X	playernum = mptr->playernum;
X	item_indx = mptr->subcmd;
X
X	if(item_indx < 0
X	|| item_indx >= pseg->item_count)
X		return;
X
X	if(pseg->itm[item_indx].loc.sector != LOC_PLAYER 
X	|| pseg->itm[item_indx].loc.x != playernum)
X		return;
X
X	switch(pseg->itm[item_indx].type) {
X	case WEAPON :
X		pseg->weapon[p_indx] = item_indx;
X		pseg->item_sem++;
X		tell_player(p_indx);
X		break;
X
X	case SHIELD :
X		pseg->shield[p_indx] = item_indx;
X		pseg->item_sem++;
X		pseg->player_sem++;
X		tell_player(p_indx);
X		break;
X
X	case ARMOR :
X		pseg->armor[p_indx] = item_indx;
X		pseg->item_sem++;
X		pseg->player_sem++;
X		tell_player(p_indx);
X		break;
X
X	case SCROLL :
X	case POTION :
X	case OBJECT :
X	case MISSILE :
X	case NOTE :
X		activate_item(p_indx, mptr);
X		break;
X	}
X}
X
X
Xtell_player(p_indx)
Xint p_indx;
X{
X	struct sembuf ops[1];
X
X	ops[0].sem_num = p_indx;
X	ops[0].sem_op = 1;
X	ops[0].sem_flg = 0;
X
X	semop(pseg->sid, ops, 1);
X}
X
X
Xnotify(sector)
Xint sector;
X{
X	int i;
X
X	for(i = 0; i < MAX_PLAYERS; i++) 
X		if(pseg->p[i].loc.sector == sector) 
X			tell_player(i);
X}
X
X
Xdrop_cash(p_indx, dir, amount)
Xint p_indx, dir, amount;
X{
X	int i;
X
X	if(amount < 1 || amount > pseg->p[p_indx].gold)
X		return;
X
X	for(i = 0; i < pseg->item_count; i++) {
X		if(pseg->itm[i].loc.sector == LOC_NONESUCH
X		&& pseg->itm[i].type == CASH) {
X			pseg->itm[i].value = amount;
X			pseg->itm[i].decay_cnt = 0;
X			pseg->itm[i].symbol = pseg->itm[i].type;
X
X			if(pseg->p[p_indx].gold_hidden) {
X				pseg->p[p_indx].gold_hidden = 0;
X				pseg->itm[i].symbol = HIDDEN;
X			}
X
X			if(drop_it(p_indx, dir, i) != -1)
X				pseg->p[p_indx].gold -= amount;
X
X			pseg->player_sem++;
X			tell_player(p_indx);
X			return;
X		}
X	}
X
X	dmsg_add(p_indx, "No treasure slots free...");
X}
X
X
Xdrop_cmd(p_indx, dir, indx)
Xint p_indx, dir, indx;
X{
X	pseg->itm[indx].decay_cnt = 0;
X
X	if(drop_it(p_indx, dir, indx) != -1) {
X		delete_item(p_indx, indx);
X		deselect(p_indx, indx);
X	}
X}
X
X
Xint drop_it(p_indx, dir, indx)
Xint p_indx, dir, indx;
X{
X	int dest_x, dest_y, dest_a;
X
X	dest_a = pseg->p[p_indx].loc.sector;
X	dest_x = pseg->p[p_indx].loc.x;
X	dest_y = pseg->p[p_indx].loc.y;
X
X	switch(dir) {
X	case L_NORTH :
X		dest_y--;
X		if(dest_y < 0) {
X			dest_y = MAPROWS - 1;
X			dest_a = mseg->m[dest_a].links[L_NORTH];
X		}
X		if(dest_a < 0)
X			return -1;
X
X		if(is_clear(mseg->m[dest_a].map[dest_y][dest_x]) 
X		|| is_item(mseg->m[dest_a].map[dest_y][dest_x]))
X			return put_item(indx, dest_a, dest_x, dest_y);
X
X		return -1;
X		break;
X
X	case L_SOUTH :
X		dest_y++;
X		if(dest_y >= MAPROWS) {
X			dest_y = 0;
X			dest_a = mseg->m[dest_a].links[L_SOUTH];
X		}
X		if(dest_a < 0)
X			return -1;
X
X		if(is_clear(mseg->m[dest_a].map[dest_y][dest_x]) 
X		|| is_item(mseg->m[dest_a].map[dest_y][dest_x]))
X			return put_item(indx, dest_a, dest_x, dest_y);
X
X		return -1;
X		break;
X
X	case L_EAST :
X		dest_x++;
X		if(dest_x >= MAPCOLS) {
X			dest_x = 0;
X			dest_a = mseg->m[dest_a].links[L_EAST];
X		}
X		if(dest_a < 0)
X			return -1;
X
X		if(is_clear(mseg->m[dest_a].map[dest_y][dest_x]) 
X		|| is_item(mseg->m[dest_a].map[dest_y][dest_x]))
X			return put_item(indx, dest_a, dest_x, dest_y);
X
X		return -1;
X		break;
X
X	case L_WEST :
X		dest_x--;
X		if(dest_x < 0) {
X			dest_x = MAPCOLS - 1;
X			dest_a = mseg->m[dest_a].links[L_WEST];
X		}
X		if(dest_a < 0)
X			return -1;
X
X		if(is_clear(mseg->m[dest_a].map[dest_y][dest_x]) 
X		|| is_item(mseg->m[dest_a].map[dest_y][dest_x]))
X			return put_item(indx, dest_a, dest_x, dest_y);
X
X		return -1;
X		break;
X
X	case L_NEAST :
X		dest_x++;
X		dest_y--;
X
X		if(dest_y < 0 && dest_x >= MAPCOLS)
X			return -1;
X
X		if(dest_x >= MAPCOLS) {
X			dest_x = 0;
X			dest_a = mseg->m[dest_a].links[L_EAST];
X		}
X
X		if(dest_y < 0) {
X			dest_y = MAPROWS - 1;
X			dest_a = mseg->m[dest_a].links[L_NORTH];
X		}
X
X		if(dest_a < 0)
X			return -1;
X
X		if(is_clear(mseg->m[dest_a].map[dest_y][dest_x]) 
X		|| is_item(mseg->m[dest_a].map[dest_y][dest_x]))
X			return put_item(indx, dest_a, dest_x, dest_y);
X
X		return -1;
X		break;
X
X	case L_SEAST :
X		dest_x++;
X		dest_y++;
X
X		if(dest_y >= MAPROWS && dest_x >= MAPCOLS)
X			return -1;
X
X		if(dest_x >= MAPCOLS) {
X			dest_x = 0;
X			dest_a = mseg->m[dest_a].links[L_EAST];
X		}
X
X		if(dest_y >= MAPROWS) {
X			dest_y = 0;
X			dest_a = mseg->m[dest_a].links[L_SOUTH];
X		}
X
X		if(dest_a < 0)
X			return -1;
X
X		if(is_clear(mseg->m[dest_a].map[dest_y][dest_x]) 
X		|| is_item(mseg->m[dest_a].map[dest_y][dest_x]))
X			return put_item(indx, dest_a, dest_x, dest_y);
X
X		return -1;
X		break;
X
X	case L_NWEST :
X		dest_x--;
X		dest_y--;
X
X		if(dest_y < 0 && dest_x < 0)
X			return -1;
X
X		if(dest_x < 0) {
X			dest_x = MAPCOLS - 1;
X			dest_a = mseg->m[dest_a].links[L_WEST];
X		}
X
X		if(dest_y < 0) {
X			dest_y = MAPROWS - 1;
X			dest_a = mseg->m[dest_a].links[L_NORTH];
X		}
X
X		if(dest_a < 0)
X			return -1;
X
X		if(is_clear(mseg->m[dest_a].map[dest_y][dest_x]) 
X		|| is_item(mseg->m[dest_a].map[dest_y][dest_x]))
X			return put_item(indx, dest_a, dest_x, dest_y);
X
X		return -1;
X		break;
X
X	case L_SWEST :
X		dest_x--;
X		dest_y++;
X
X		if(dest_y >= MAPROWS && dest_x < 0)
X			return -1;
X
X		if(dest_x < 0) {
X			dest_x = MAPCOLS - 1;
X			dest_a = mseg->m[dest_a].links[L_WEST];
X		}
X
X		if(dest_y > MAPROWS) {
X			dest_y = 0;
X			dest_a = mseg->m[dest_a].links[L_SOUTH];
X		}
X
X		if(dest_a < 0)
X			return -1;
X
X		if(is_clear(mseg->m[dest_a].map[dest_y][dest_x]) 
X		|| is_item(mseg->m[dest_a].map[dest_y][dest_x]))
X			return put_item(indx, dest_a, dest_x, dest_y);
X
X		return -1;
X		break;
X	}
X}
X
X
Xint is_app_clear(ch)
Xchar ch;
X{
X	if(is_clear(ch) || is_item(ch))
X		return 1;
X
X	switch(ch) {
X	case '~' :
X	case '%' :
X		return 1;
X	default :
X		return 0;
X	}
X}
X
X
Xint is_clear(ch)
Xchar ch;
X{
X	switch(ch) {
X
X	/* gates */
X	case '=' :
X	case '{' :
X	case '}' :
X	case ':' :
X	case ';' :
X
X	case ' ' :
X	case '`' :
X	case '.' :
X	case ',' :
X	case '+' :
X	case '^' :
X	case '0' :
X	case '1' :
X	case '2' :
X	case '3' :
X	case '4' :
X	case '5' :
X	case '6' :
X	case '7' :
X	case '8' :
X	case '9' :
X		return 1;
X	default :
X		return 0;
X	}
X}
X
X
Xint put_item(indx, dest_a, dest_x, dest_y)
Xint indx, dest_a, dest_x, dest_y;
X{
X	int i, over_indx, code;
X	static int depth = 0;
X
X	if(indx < 0
X	|| indx >= pseg->item_count)
X		return -1;
X
X	if(dest_a < 0)
X		return -1;
X
X	if(is_clear(mseg->m[dest_a].map[dest_y][dest_x])) {
X		if(isdrop(mseg->m[dest_a].map[dest_y][dest_x])
X		&& depth < 10) {
X			depth++;
X			code = put_item(indx, mseg->m[dest_a].links[L_DOWN],
X				dest_x, dest_y);
X			depth--;
X			return code;
X		} else {
X			/* simple case. */
X			pseg->itm[indx].loc.under = 
X				mseg->m[dest_a].map[dest_y][dest_x];
X			mseg->m[dest_a].map[dest_y][dest_x] = 
X				pseg->itm[indx].symbol;
X		}
X	} else {
X		/* find the item that will be over this one. */
X		over_indx = -1;
X
X		for(i = pseg->item_count - 1; i > indx; i--) {
X			if(pseg->itm[i].loc.sector == dest_a
X			&& pseg->itm[i].loc.x == dest_x
X			&& pseg->itm[i].loc.y == dest_y)
X				over_indx = i;
X		}
X			
X		if(over_indx == -1) {
X			/* new item is on top. */
X			pseg->itm[indx].loc.under = 
X				mseg->m[dest_a].map[dest_y][dest_x];
X			mseg->m[dest_a].map[dest_y][dest_x] = 
X				pseg->itm[indx].symbol;
X		} else {
X			/* new item is under over_indx */
X			pseg->itm[indx].loc.under = 
X				pseg->itm[over_indx].loc.under;
X			pseg->itm[over_indx].loc.under = 
X				pseg->itm[indx].symbol;
X		}
X	}
X
X	pseg->itm[indx].loc.sector = dest_a;
X	pseg->itm[indx].loc.x = dest_x;
X	pseg->itm[indx].loc.y = dest_y;
X
X	mseg->map_sem++;
X	pseg->item_sem++;
X
X	notify(dest_a);
X
X	return 0;
X}
X
X
Xmove_cmd(locptr, dir, symbol, p_indx)
Xstruct location *locptr;
Xint dir;
Xchar symbol;
Xint p_indx;
X{
X	struct location loc;
X	int sector;
X	char buf[35];
X
X	if((sector = locptr->sector) < 0)
X		return;
X
X	mseg->m[sector].map[locptr->y][locptr->x] = locptr->under;
X
X	loc.x = locptr->x;
X	loc.y = locptr->y;
X	loc.sector = locptr->sector;
X
X	if(get_dest(&loc, dir) != -1)
X		move_subcmd(locptr, loc.sector, loc.x, loc.y, p_indx);
X
X	if(pseg->p[p_indx].brief == 0)
X		if(locptr->under == '>' || locptr->under == '<') {
X			sprintf(buf, "stairs going %s", 
X				(locptr->under == '<' ? "down" : "up" ));
X			dmsg_add(p_indx, buf);
X		}
X
X	move_sym(locptr, symbol);
X}
X
X
Xint get_dest(lptr, dir)
Xstruct location *lptr;
Xint dir;
X{
X	switch(dir) {
X	case L_UP :
X		lptr->sector = mseg->m[lptr->sector].links[L_UP];
X		return 0;
X
X	case L_DOWN :
X		lptr->sector = mseg->m[lptr->sector].links[L_DOWN];
X		return 0;
X
X	case L_NORTH :
X		lptr->y--;
X
X		if(lptr->y < 0) {
X			lptr->y = MAPROWS - 1;
X			lptr->sector = 
X				mseg->m[lptr->sector].links[L_NORTH];
X		}
X		return 0;
X
X	case L_SOUTH :
X		lptr->y++;
X
X		if(lptr->y >= MAPROWS) {
X			lptr->y = 0;
X			lptr->sector = 
X				mseg->m[lptr->sector].links[L_SOUTH];
X		}
X		return 0;
X
X	case L_EAST :
X		lptr->x++;
X
X		if(lptr->x >= MAPCOLS) {
X			lptr->x = 0;
X			lptr->sector = 
X				mseg->m[lptr->sector].links[L_EAST];
X		}
X		return 0;
X
X	case L_WEST :
X		lptr->x--;
X
X		if(lptr->x < 0) {
X			lptr->x = MAPCOLS - 1;
X			lptr->sector = 
X				mseg->m[lptr->sector].links[L_WEST];
X		}
X		return 0;
X
X	case L_NEAST :
X		lptr->x++;
X		lptr->y--;
X
X		if(lptr->y < 0 && lptr->x >= MAPCOLS)
X			return -1;
X
X		if(lptr->y < 0) {
X			lptr->y = MAPROWS - 1;
X			lptr->sector = 
X				mseg->m[lptr->sector].links[L_NORTH];
X		}
X
X		if(lptr->x >= MAPCOLS) {
X			lptr->x = 0;
X			lptr->sector = 
X				mseg->m[lptr->sector].links[L_EAST];
X		}
X		return 0;
X
X	case L_NWEST :
X		lptr->x--;
X		lptr->y--;
X
X		if(lptr->y < 0 && lptr->x < 0)
X			return -1;
X
X		if(lptr->y < 0) {
X			lptr->y = MAPROWS - 1;
X			lptr->sector = 
X				mseg->m[lptr->sector].links[L_NORTH];
X		}
X
X		if(lptr->x < 0) {
X			lptr->x = MAPCOLS - 1;
X			lptr->sector = 
X				mseg->m[lptr->sector].links[L_WEST];
X		}
X		return 0;
X
X	case L_SEAST :
X		lptr->x++;
X		lptr->y++;
X
X		if(lptr->y >= MAPROWS && lptr->x >= MAPCOLS)
X			return -1;
X
X		if(lptr->y >= MAPROWS) {
X			lptr->y = 0;
X			lptr->sector = 
X				mseg->m[lptr->sector].links[L_SOUTH];
X		}
X
X		if(lptr->x >= MAPCOLS) {
X			lptr->x = 0;
X			lptr->sector = 
X				mseg->m[lptr->sector].links[L_EAST];
X		}
X		return 0;
X
X	case L_SWEST :
X		lptr->x--;
X		lptr->y++;
X
X		if(lptr->y >= MAPROWS && lptr->x < 0)
X			return -1;
X
X		if(lptr->y >= MAPROWS) {
X			lptr->y = 0;
X			lptr->sector = 
X				mseg->m[lptr->sector].links[L_SOUTH];
X		}
X
X		if(lptr->x < 0) {
X			lptr->x = MAPCOLS - 1;
X			lptr->sector = 
X				mseg->m[lptr->sector].links[L_WEST];
X		}
X		return 0;
X	}
X
X	return -1;
X}
X
X
Xmove_subcmd(locptr, dest_a, dest_x, dest_y, p_indx)
Xstruct location *locptr;
Xint dest_a, dest_x, dest_y, p_indx;
X{
X	int rc, newsector, oldsector;
X
X	/* move_to does not always move a player to the sector */
X	/* stored in dest_a due to pits, etc. */
X
X	oldsector = pseg->p[p_indx].loc.sector;
X
X	rc = move_to(locptr, dest_a, dest_x, dest_y, p_indx);
X
X	newsector = pseg->p[p_indx].loc.sector;
X
X	if(rc == 0 
X	&& newsector >= 0
X	&& oldsector != newsector
X	&& mseg->m[newsector].light < 0) {
X		pseg->p[p_indx].light -= rnd(500);
X		if(pseg->p[p_indx].light < 0)
X			pseg->p[p_indx].light = 0;
X		dmsg_add(p_indx, "Your light dims...");
X	}
X
X	if(p_indx >= 0 && (is_critter(rc) || is_player(rc)))
X		attack(p_indx, dest_a, dest_x, dest_y);
X
X	if(oldsector != newsector)
X		notify(oldsector);
X}
X
X
Xmove_sym(locptr, symbol)
Xstruct location *locptr;
Xchar symbol;
X{
X	if(locptr->sector >= 0) {
X		mseg->m[locptr->sector].map[locptr->y][locptr->x] = symbol;
X		mseg->map_sem++;
X		notify(locptr->sector);
X	}
X}
X
X
Xint move_to(locptr, dest_a, dest_x, dest_y, p_indx)
Xstruct location *locptr;
Xint dest_a, dest_x, dest_y, p_indx;
X{
X	char at_loc;
X	int prev_s, temp_a, code, gatenum;
X	static int depth = 0;
X
X	if(dest_a == LOC_HOME && p_indx >= 0) {
X		mseg->map_sem++;
X		pseg->p[p_indx].home = locptr->sector;
X		locptr->x = locptr->sector;
X		locptr->sector = dest_a;
X		tell_player(p_indx);
X		return 0;
X	}
X
X	if(dest_a == LOC_TRADER && p_indx >= 0) {
X		if(pseg->in_trader >= 0) {
X			dmsg_add(p_indx, "Trader is busy.");
X			return -1;
X		}
X		pseg->in_trader = p_indx;
X		mseg->map_sem++;
X		tell_player(p_indx);
X		locptr->x = locptr->sector;
X		locptr->sector = dest_a;
X		return 0;
X	}
X
X	if(dest_a < 0)
X		return -1;
X
X	at_loc = mseg->m[dest_a].map[dest_y][dest_x];
X
X	switch(at_loc) {
X	case '^' : /* air */
X	case '0' : /* pit */
X
X		if(p_indx < 0 && at_loc == '^') 
X			return '^';
X
X		if(p_indx >= 0
X		&& (temp_a = mseg->m[dest_a].links[L_DOWN]) >= 0
X		&& is_app_clear(mseg->m[temp_a].map[dest_y][dest_x])) {
X			dmsg_add(p_indx, "You fell!");
X			pgetzapped(p_indx, rnd(10));
X			if(locptr->sector == LOC_NONESUCH)
X				break;
X			if(isdrop(mseg->m[temp_a].map[dest_y][dest_x])
X			&& depth < 10) {
X				depth++;
X				code = move_to(locptr, temp_a, 
X					dest_x, dest_y, p_indx);
X				depth--;
X				return code;
X			}
X			locptr->sector = mseg->m[dest_a].links[L_DOWN];
X		} else 
X			locptr->sector = dest_a;
X
X		locptr->x = dest_x;
X		locptr->y = dest_y;
X		locptr->under = mseg->m[locptr->sector].map[dest_y][dest_x];
X		mseg->map_sem++;
X		return 0;
X
X	case '=' : /* gateway 1 */
X	case '{' : /* gateway 2 */
X	case '}' : /* gateway 3 */
X	case ':' : /* gateway 4 */
X	case ';' : /* gateway 5 */
X	case '-' : /* gateway 6 */
X	case '|' : /* gateway 7 */
X
X		prev_s = locptr->sector;
X		locptr->sector = dest_a;
X
X		if(p_indx >= 0) {
X			gatenum = gate_id(at_loc);
X
X			temp_a = mseg->m[dest_a].links[L_GATE+gatenum];
X
X			if(temp_a == LOC_HOME) {
X				locptr->x = prev_s;
X				locptr->sector = temp_a;
X				mseg->map_sem++;
X				return 0;
X			} else if(temp_a >= 0) {
X				at_loc = mseg->m[temp_a].map[dest_y][dest_x];
X				if(is_clear(at_loc)) {
X					pseg->p[p_indx].prev_sect = prev_s;
X					locptr->sector = temp_a;
X				} else if(is_critter(at_loc))
X					knockoffm(temp_a, dest_x, dest_y);
X			} 
X		}
X
X		locptr->x = dest_x;
X		locptr->y = dest_y;
X		locptr->under = 
X			mseg->m[locptr->sector].map[dest_y][dest_x];
X		mseg->map_sem++;
X		return 0;
X
X	case '1' : /* 1-9 are traps */
X	case '2' :
X	case '3' :
X	case '4' :
X	case '5' :
X	case '6' :
X	case '7' :
X	case '8' :
X	case '9' :
X		if(p_indx >= 0) {
X			do_trap(p_indx, at_loc);
X			if(locptr->sector == LOC_NONESUCH)
X				break;
X		}
X		locptr->sector = dest_a;
X		locptr->x = dest_x;
X		locptr->y = dest_y;
X		locptr->under = at_loc;
X		mseg->map_sem++;
X		return 0;
X
X	case '.' :
X		if(p_indx < 0)
X			return (int)at_loc;
X		/* fall through here. */
X	case ' ' :
X	case ',' :
X	case '`' : /* shallow water */
X	case '+' : /* secret door */
X	case '<' : /* down */
X	case '>' : /* up */
X	case POTION :
X	case SCROLL :
X	case ARMOR :
X	case SHIELD :
X	case WEAPON :
X	case MISSILE :
X	case OBJECT :
X	case CASH :
X	case CRYSTAL :
X	case NOTE :
X	case HIDDEN :
X		if(p_indx < 0 && is_item(at_loc))
X			punt_item(dest_a, dest_x, dest_y);
X
X		locptr->sector = dest_a;
X		locptr->x = dest_x;
X		locptr->y = dest_y;
X		locptr->under = mseg->m[dest_a].map[dest_y][dest_x];
X		mseg->map_sem++;
X		return 0;
X
X	case '%' :
X	case '~' :
X	      	if(locptr->under == at_loc)
X			return (int)at_loc;
X		else {
X			locptr->sector = dest_a;
X			locptr->x = dest_x;
X			locptr->y = dest_y;
X			locptr->under = at_loc;
X			mseg->map_sem++;
X			return 0;
X		}
X
X	default :
X		return (int)at_loc;
X	}
X
X}
X
X
Xknockoffm(dest_a, dest_x, dest_y)
Xint dest_a, dest_x, dest_y;
X{
X	int i;
X
X	for(i = 0; i < n_monster; i++)
X		if(m_table[i].loc.sector == dest_a 
X		&& m_table[i].loc.x == dest_x 
X		&& m_table[i].loc.y == dest_y) {
X			clearm(i, 0);
X			m_table[i].loc.sector = LOC_NONESUCH;
X			return;
X		}
X}
X
X
Xint is_player(val)
Xint val;
X{
X	return (val >= 1 && val <= 19);
X}
X
X
Xint is_critter(val)
Xint val;
X{
X	return (val == '&') 
X		|| (val >= 'a' && val <= 'z') 
X		||  (val >= 'A' && val <= 'Z');
X}
X
X
Xint is_item(val)
Xint val;
X{
X	switch(val) {
X	case POTION :
X	case SCROLL :
X	case ARMOR :
X	case SHIELD :
X	case WEAPON :
X	case MISSILE :
X	case OBJECT :
X	case CASH :
X	case CRYSTAL :
X	case NOTE :
X	case HIDDEN :
X		return 1;
X	default :
X		return 0;
X	}
X}
X
X
Xint top_item(indx)
Xint indx;
X{
X	int i;
X
X	for(i = pseg->item_count - 1; i > indx; i--)
X		if(pseg->itm[i].loc.sector == pseg->itm[indx].loc.sector
X		&& pseg->itm[i].loc.x == pseg->itm[indx].loc.x
X		&& pseg->itm[i].loc.y == pseg->itm[indx].loc.y)
X			return 0;
X
X	return 1;
X}
X
X
Xtake_item(indx)
Xint indx;
X{
X	int i;
X
X	if(indx < 0 
X	|| pseg->p[indx].playernum < 0)
X		return;
X
X	for(i = pseg->item_count - 1; i >= 0; i--)
X		if(pseg->itm[i].loc.sector == pseg->p[indx].loc.sector
X		&& pseg->itm[i].loc.x == pseg->p[indx].loc.x
X		&& pseg->itm[i].loc.y == pseg->p[indx].loc.y) {
X
X			if(pseg->itm[i].type == CASH) {
X				pseg->p[indx].gold += 
X					pseg->itm[i].value;
X				pseg->itm[i].loc.sector = LOC_NONESUCH;
X				pseg->itm[i].symbol = pseg->itm[i].type;
X				pseg->player_sem++;
X			} else if(pseg->itm[i].type == CRYSTAL) {
X				if(pseg->p[indx].max_hp >= HP_CUTOFF
X				&& pseg->p[indx].max_mp >= MP_CUTOFF)
X					pseg->p[indx].createpts +=
X						pseg->itm[i].effect;
X				pseg->itm[i].loc.sector = LOC_NONESUCH;
X				pseg->itm[i].symbol = pseg->itm[i].type;
X				pseg->player_sem++;
X			} else {
X				if(insert_item(indx, i) == -1)
X					return;
X
X				pseg->item_sem++;
X
X				pseg->itm[i].decay_cnt = 0;
X				pseg->itm[i].loc.sector = LOC_PLAYER;
X				pseg->itm[i].loc.x = 
X					pseg->p[indx].playernum;
X				pseg->itm[i].symbol = pseg->itm[i].type;
X				pseg->itm[i].loc.y = ITEM_NEW;
X			}
X
X			pseg->p[indx].loc.under = pseg->itm[i].loc.under;
X
X			tell_player(indx);
X
X			return;
X		}
X
X	/* if we got here, it is an item but we can't find it! */
X	/* so, clear the space. */
X
X	if(is_item(pseg->p[indx].loc.under))
X		pseg->p[indx].loc.under = ' ';
X}
X
X
Xpunt_item(sector, x, y)
Xint sector, x, y;
X{
X	int i;
X
X	for(i = pseg->item_count - 1; i >= 0; i--)
X		if(pseg->itm[i].loc.sector == sector
X		&& pseg->itm[i].loc.x == x
X		&& pseg->itm[i].loc.y == y) {
X
X			pseg->itm[i].loc.sector = LOC_NONESUCH;
X			pseg->itm[i].symbol = pseg->itm[i].type;
X
X			mseg->m[sector].map[y][x] = pseg->itm[i].loc.under;
X			mseg->map_sem++;
X
X			notify(sector);
X
X			return;
X		}
X}
X
X
Xint item_cnt(p_indx)
Xint p_indx;
X{
X	int i, cnt;
X
X	cnt = 0;
X
X	for(i = 0; i < pseg->item_count; i++) {
X		if(pseg->itm[i].loc.sector == LOC_PLAYER
X		&& pseg->itm[i].loc.x == pseg->p[p_indx].playernum)
X			cnt++;
X	}
X
X	return cnt;
X}
X
X
Xint insert_item(p_indx, i_indx)
Xint p_indx, i_indx;
X{
X	int i;
X
X	for(i = 0; i < PMAX_ITEMS; i++)
X		if(pseg->p[p_indx].items[i] == -1) {
X			pseg->p[p_indx].items[i] = i_indx;
X			return i;
X		}
X
X	return -1;
X}
X
X
Xdeselect(p_indx, i_indx)
Xint p_indx, i_indx;
X{
X	if(pseg->armor[p_indx] == i_indx)
X		pseg->armor[p_indx] = -1;
X
X	if(pseg->shield[p_indx] == i_indx)
X		pseg->shield[p_indx] = -1;
X
X	if(pseg->weapon[p_indx] == i_indx)
X		pseg->weapon[p_indx] = -1;
X}
X
X
Xint delete_item(p_indx, i_indx)
Xint p_indx, i_indx;
X{
X	int i;
X
X	for(i = 0; i < PMAX_ITEMS; i++)
X		if(pseg->p[p_indx].items[i] == i_indx) {
X			pseg->p[p_indx].items[i] = -1;
X			return i;
X		}
X
X	return -1;
X}
X
X
Xint gate_id(ch)
Xint ch;
X{
X	switch(ch) {
X	case '=' : /* gateway 1 */
X		return 0;
X	case '{' : /* gateway 2 */
X		return 1;
X	case '}' : /* gateway 3 */
X		return 2;
X	case ':' : /* gateway 4 */
X		return 3;
X	case ';' : /* gateway 5 */
X		return 4;
X	case '-' : /* gateway 6 */
X		return 5;
X	case '|' : /* gateway 7 */
X		return 6;
X	default :
X		return 0;
X	}
X}
X
X
Xint isdrop(ch)
Xint ch;
X{
X	switch(ch) {
X	case '^' :
X	case '0' :
X		return 1;
X	}
X
X	return 0;
X}
X
X
X/* end of file. */
END_OF_FILE
if test 27418 -ne `wc -c <'msghandler.c'`; then
    echo shar: \"'msghandler.c'\" unpacked with wrong size!
fi
# end of 'msghandler.c'
fi
echo shar: End of archive 1 \(of 6\).
cp /dev/null ark1isdone
MISSING=""
for I in 1 2 3 4 5 6 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 6 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0