[net.sources.games] WAR: Two player real-time multi-men maze war

dbell@daisy.UUCP (David I. Bell) (04/05/85)

> P> 
#---Cut here and place in it's own directory, then feed to Bourne shell---
# This is a shell archive.  Remove anything before this line, then
# unpack it by saving it in a file and typing "sh file".  (Files
# unpacked will be owned by you and have default permissions.)
# This archive contains:
#   README (1036 chars)
#   war.6 (19117 chars)
#   objects.war (656 chars)
#   boards.war (3785 chars)
#   makefile (229 chars)
#   war.h (6354 chars)
#
echo x - README
sed -e 's/^X//' > "README" << '//E*O*F README//'
XThis program was written for 4.2 BSD running on a VAX 750.   It makes
Xuse of sockets for communicating between two copies of the program.
XTherefore, porting this program to other flavors of UNIX will be painful.
X
XThis program requires the "dpy" library module which I have previously
Xposted to net.sources.  If you do not have this module, then you must
Xget it from someone else on the net.  I can no longer supply copies of
Xdpy since this is my last day at Daisy.
X
XThe following files need to be handled specially:
X
Xobjects.war	This is the default object initialization file.  Copy it
X		to /usr/games/lib.
X
Xboards.war	This is an example setup file.  Each player actually has
X		their own setup file ".warsetups" in their HOME directory.
X
Xwar.6		This is the manual page for war.  Copy it to /usr/man/man6.
X
XThere are still several features that I would like to see in the game,
Xbut I am out of time (see the bug list in the man page).  However, the
Xcurrent version still contains enough features to be playable.  Enjoy!
X
X		David I. Bell
//E*O*F README//
echo x - war.6
sed -e 's/^X//' > "war.6" << '//E*O*F war.6//'
X.TH WAR 6 "3 April 1985"
X.UC
X.SH NAME
Xwar \- two person seek and destroy game played in mazes
X.SH SYNOPSIS
X.B war
X[
X.B \-f
Xsetupfile ] [
X.B \-s
Xsetupname ] [
X.B \-o
Xobjectfile ] username [ ttyname ]
X.br
X.B war -e
X[
X.B \-f
Xsetupfile ] [
X.B \-s
Xsetupname ] [
X.B \-o
Xobjectfile ]
X.SH DESCRIPTION
X.I War
Xis a war game played between two people on display terminals.
XIt is played on a board which has 24 rows and 48 columns.
XThe top and bottom ten rows of the board are the
X.I home
Xareas for the two players.
XThe middle four rows are the
X.IR "no man's land" .
XAt the beginning of the game each player creates a maze in his own home
Xarea, and populates it with fighting men, blasting men, and a goal.
XAfter this setup phase is complete, the two players fight each other.
XThe object of the game is to find and destroy the enemy goal.
XAlong the way, battles are fought between your men and the enemy's men,
Xthe enemy's maze is explored and perhaps blasted through,
Xand your own maze and goal is defended.
X.I War
Xis a game of imperfect information.
XYou know everything about your own men, but know very little about the enemy.
XAs the game progresses, you can determine the flow of the battle from a
Xset of statistics which are displayed for both players.
XHowever, you do not know the details concerning any specific men.
XTo know where the enemy men are, you must place your own men close enough
Xto them to seen them.
X.PP
XThe person to play with is specified by giving his login name.
XIf he is logged in on more than one terminal, you must then also give
Xthe terminal name to play with.
XOf course, for the game to actually play, the opponent must also run
X.I war
Xand specify you as the person for him to play with.
XIf you don't want to play with anyone, but just want to edit different
Xmazes for future play, then use the
X.B \-e
Xoption.
X.SH "THE DISPLAY"
XThe screen is split up into three parts.
XThe left half of the screen contains a picture of the playing board.
XThe board occupies all the space between the two vertical rows of characters.
XYour home area is always in the upper ten rows of the board, and the
Xenemy's home area is always in the lower ten rows.
XThis means that if you compare your view of the board with the other player,
Xhis view seems flipped over.
XHowever, directions are transformed uniformly so that no problems appear.
XYou can always see all of your own walls and men.
XHowever, you cannot see the enemy's walls or men unless you have men close
Xenough to them (even in your own maze).
XHowever, once you have see any of the enemy's walls (or other immovable
Xobjects), they will stay visible as long as they exist.
X.PP
XThe lower right of the screen shows the status of the game.
XYou can compare the status of your men with the enemy's men.
XEach pair of numbers separated with a slash gives the number of men
Xleft of that type, and the total life of those men.
X.PP
XThe upper right of the screen shows the current status of all of your men.
XThe parameters of the men are configurable, and are normally read from
Xthe file
X.IR /usr/games/lib/objects.war .
XYou can play with different men by creating a new configuration file and
Xspecifying it using the
X.B \-o
Xoption.
XThe enemy has men similar to yours, but you don't know their life values or
Xwhich ones are still alive.
XThis display only shows men which are still alive, so when a man of yours
Xdies, it is removed from the display.
XThe two characters under the OBJ heading are a description of your men.
XThe leftmost character is how your object (man or wall) appears to you.
XThe rightmost character is how your object appears to the enemy.
XNotice that several men appear identically to the enemy (as numbers), so
Xthat he can't distinguish between them.
XDifferent kinds of men have different characters, however.
X.PP
XThe life of the object is how many hits it can take before it is destroyed.
XFor walls, the life is instead an indication of how tough each wall is
Xto blast away.
XThe view of the object is the range at which the object can see enemy objects.
XThe range specifies a square of the specified radius (not a circle).
XThus a man with a view of 3 could see an enemy man which was 3 rows down
Xand 3 columns to the right.
XViewing is normally blocked by walls and men, so the full viewing range is
Xnot always usable (especially when inside a cramped maze).
X.PP
XThe flags column gives the capabilities of the object.
XThese capabilities are as follows:
X.TP 6
X.B b
XObject can blast walls.
X.TP
X.B f
XObject can fight men.
X.TP
X.B g
XObject is a goal.
X.TP
X.B i
XObject is immobile.
X.TP
X.B t
XObject is transparent.
X.TP
X.B v
XObject is always visible.
X.TP
X.B w
XObject is a wall.
X.TP
X.B x
XObject has x-ray vision.
XThis means it can see through walls and men.
X.SH "SETUP PHASE"
XThe first phase of the game is the setup, or editing phase.
XThis is when you design your maze and place your men into it.
XTo make this phase go quicker, you can read in a setup that you had
Xprevious created.
XThis can be done on the command line by using the
X.B \-s
Xoption.
XYou can also read in the setup after starting the program.
XThese setups are stored in the file ".warsetups" in your HOME directory.
XThis can be changed to be any file you wish by using the
X.B \-f
Xoption.
XA setup file contains a number of setups in sequence.
XEach setup begins with a line containing a name surrounded by double quotes.
XFollowing the name is simply a picture of the setup.
X.PP
XAn example of a setup file entry named 'nice' is:
X.sp
X.nf
X	"nice"
X	*    *                      *   *
X	*  *   ****************** *   * * * ***********
X	****** *             *  * ******* *          *
X	 ** **** ********* *    * *#      * ******** * *
X	      **    *   *   ***** ********* * *   *  *
X	 ******  ** * *   *                 *   *    **
X	 ******* ** ************************************
X						  E X
X	**********BDC*****H*****G***F*************Y Z***
X	***********A******************************* ****
X.fi
X.PP
XIf you read in a setup, you can still perform last minute changes to it
Xbefore playing.
XOnce you create a setup that you like, you can save it in your setup file
Xfor use in later games.
X.PP
XThere is a restriction on the creation of a setup.
XWalls cannot block off any location on the board (even other walls).
XThat is, there must be a path from below the setup to every location
Xin the home area, without having to travel through a wall.
XThe path must exist orthogonally (left-right or up-down).
XThis restriction prevents the creation of mazes which are just made up of
Xsolid walls.
XIt also guarantees that the goal is accessible without having to remove
Xany walls.
XThe program enforces this restriction rigidly, even if a saved setup is
Xread in.
X.PP
XCommands in the setup phase are single letter commands, possibly preceeded
Xby a numeric argument, which is used as a repeat count.
XThese commands do not echo, and need no terminating carriage return.
XBefore completion, the normal editing characters can be used, and
Xthe escape character will clear the command.
XUnknown or illegal commands will ring the terminal bell.
XMost commands are executed immediately when they are completely typed.
XBut those commands with dangerous consequences or that require input strings
Xwill prompt you for a response.
XAll such responses require a terminating carriage return.
XIf a blank response or an escape is typed, the command is aborted.
X.PP
XThe commands used for editing a setup are the following:
X.TP 6
X.B r
XClears the current setup and reads in a setup from your ".warsetups" file.
XThe name of the setup is prompted for.
XThe first setup in the file which matches the name is used.
XThe setup will be checked for legality, and unknown or illegal locations
Xwill be left blank.
X.TP
X.B w
XWrites the current setup to your ".warsetups" file.
XThe name of the setup will be prompted for.
XThe setup is appended to the end of the file, and so duplications of
Xa setup name are undetected.
X.TP
X.B f
XFlips the setup from left to right.
XThis allows you to create the mirror image of a setup.
X.TP
X.B c
XClears the current setup.
XYou are asked to confirm this command.
X.TP
X.B p
XPlaces an object at the current position.
XThe object to be placed is typed immediately after the 'p'.
XThis object is also remembered later for certain other commands.
XIf the object is unknown or cannot be placed, nothing is done.
X.TP
X.B "."
XPlaces the remembered object at the current position.
XThis is mostly useful for walls, so that you can easily place many of them.
X.TP
X.B x
XRemoves the object at the current position, and moves to the next column.
XIf a count is specified, many objects in a row can be removed.
X.TP
X.B <cr>
XMoves to the beginning of the next row.
X.TP
X.B \-
XMoves to the beginning of the previous row.
X.TP
X.B <tab>
XMoves to the next column which is at a multiple of 8.
X.TP
X.B ^
XMoves to the beginning of the current row.
X.TP
X.B $
XMoves to the end of the current row.
X.TP
X.B h
XMoves to the left.
X.TP
X.B j
XMoves down.
X.TP
X.B k
XMoves up.
X.TP
X.B l
XMoves right.
X.TP
X.B <space>
XMoves right.
X.TP
X.B y
XMoves to the upper left.
X.TP
X.B u
XMoves to the upper right.
X.TP
X.B b
XMoves to the lower left.
X.TP
X.B n
XMoves to the lower right.
X.TP
X.B H
XMoves to the left placing as many objects as it can.
XThe object that is placed is the remembered object from the 'p' command.
XThis and similar commands stop when an illegal placement is attempted,
Xor when the specified count is reached.
XIf no count is specifed, a large one is assumed.
XThis command is used to place many walls in a row.
X.TP
X.B J
XMoves down placing many objects.
X.TP
X.B K
XMoves up placing many objects.
X.TP
X.B L
XMoves right placing many objects.
X.TP
X.B Y
XMoves to the upper left placing many objects.
X.TP
X.B U
XMoves to the upper right placing many objects.
X.TP
X.B B
XMoves to the lower left placing many objects.
X.TP
X.B N
XMoves to the lower right placing many objects.
X.TP
X.B s
XStart to play the game.
XIf you have not specified a player you wish to play with,
Xor if your setup is not complete because of missing men,
Xthen the terminal bell will ring and the command will be ignored.
XIf playing is allowed, you are asked to confirm this command.
XOnce you have confirmed that you want to play, no further setup commands
Xcan be used.
XTherefore you must save your setup previous to this command if you want
Xto use it again in a new game.
X.I War
Xwill then wait until both players have said that they are ready to play.
XWhen this is so, the terminal bell will beep and the second phase of
Xthe game begins.
XYour input then controls the running of your men.
X.TP
X.B q
XQuit the program.
XYou are asked to confirm this command.
XIf you wish to save your setup, you must do so before quiting.
X.TP
X.B ^L
XRedraws the screen.
X.SH "PLAYING PHASE"
XThe game is played in turns lasting about 2 seconds each.
XPlay alternates between the two players in real-time.
XTherefore, the game does not wait for your input.
XInstead, you type your commands while the game plays,
Xand they are acted upon on the next time is is your turn.
XIf you type fast, then you can specify many commands each turn.
X.PP
XYou program each of your men with a sequence of simple commands which
Xtells each man how to act.
XThese simple commands direct the man to move, fight, or blast.
XWhen your turn arrives, each man tries to execute each command of his program
Xin sequence until one of them succeeds.
XAt that point, the man has finished moving for the current turn.
XWhen none of your men can successfully execute anything, then the turn is over.
XEach man will continue to try to execute its command sequence each turn,
Xuntil you supercede it or the man dies.
X.PP
XEach of your commands cancels the existing command sequence for a man,
Xand completely replaces it with the new sequence.
XYour skill in the game depends on recognizing when commands need changing
Xfor your men, and to specify the new commands quickly and accurately.
XYou specify a new set of commands for a man by typing the character of
Xthe man, the set of commands to be executed, and then a space or carriage
Xreturn character.
XUntil you type the terminating space or return, the command is incomplete
Xand can be edited with the normal editing characters.
XIt can also be cancelled by typing an escape character.
XWhen a command is completed, it is checked for legality before it is used.
XIf an illegal command was specified, the terminal bell will be rung,
Xand the man's previous commands are still in effect.
XIf the command sequence was legal, it then immediately becomes the new
Xcommand sequence for the specified man.
X.PP
XIf you require careful timing for the specifying of a sequence of commands,
Xthen you can type it all except for the final space, wait until conditions
Xare right, and then terminate the command.
XYou must do this immediately after the screen updates, otherwise the
Xnext turn could already be started.
X.SH "MOVEMENT"
XMovement of all men is orthogonal (left-right or up-down).
XWhen a man is able to move, it moves by exactly one location.
XThis means that it takes many turns for a man to cross the board.
XNo man can move through another man, or through a wall.
XIf commands are specified which attempt to do this, the command fails.
XHowever, if conditions later change (such as other men moving out of the way),
Xthen the command could then be executed successfully.
X.PP
XIt is very often useful to specify several directions for a man to move.
XThis allows a man to automatically travel around many obstacles.
XFor example, you can specify to move right and then down.
XWhen rightward movement is possible, the man moves right.
XWhenever that way is blocked, the man moves down instead.
XSometimes it is even convenient to specify three directions for movement.
X.PP
XIf a count is given for a movement command, then after that many moves have
Xbeen made, that movement command will be ignored.
XThis allows you to position a man exactly, since it will stop when the
Xspecified count runs out.
X.SH "FIGHTING"
XWhenever one of your men and one of the enemy's men are next to each other
Xin an orthogonal direction (left-right or up-down), then fighting can occur.
XIf you allow your man to fight, then he can take one shot each turn.
XEach shot has a 50% chance of success.
XIf successful, then the life of the enemy man is reduced by one.
XWhen the life is reduced to zero, the man dies and is removed from the board.
XIf there is someone to fight, the turn is used even if the shot misses.
X.PP
XYou normally do not need to actively specify that fighting should occur.
XAn implicit fight command is added to the end of every command you set
Xfor a man, so that if the man fails to execute your commands, fighting will
Xthen occur when possible.
XOne reason to explicitly specify a fight command is when you want to fight
Xfirst, and move only when there is no one to fight.
X.PP
XThe other reason for specifing the fight command is when there are multiple
Xmen you could fight with.
XIf you have a choice, and you have not specified otherwise, then your man
Xpicks one of the men at random each turn, and fights him.
XThis means that you are spreading your attacks between more than one person.
XIf you specify an attack in a certain direction, then the other men will
Xbe ignored.
XThis is important if you are surrounded and need to kill one of your
Xattackers in order to escape before you die.
X.SH "BLASTING"
XCertain men have the capability to blast walls.
XThis is done by moving your man next to the wall to be blasted, and
Xspecifying that a wall is to be blasted.
XBlasting of a wall is probabilistic, and depends on the "life" of the wall.
XEach time that your man can attempt blasting, the chance of the blast
Xsucceeding is the inverse of the walls' life.
XThus if a wall has a life of 10, there is a 10% chance of a blast succeeding.
XWhen a wall is successfully blasted, it is simply removed from the board.
XMen can then travel through the opening.
XEven when an attempt to blast fails, it succeeds in using up a turn.
X.PP
XUnlike fighting, blasting is not an implicit activity.
XMen only attempt to blast when you direct it.
XThis is because much harm can occur from misdirected blasting (especially
Xin your own maze).
X.PP
XWhen combined with movement commands, blasting can automatically punch a
Xhole through multiple walls.
XTo do this you specify that the man blasts in a direction, and then moves
Xin the direction.
XWhen blasting is possible, the man blasts.
XWhen blasting is not possible (because the wall is gone), the man moves.
X.PP
XYou should be careful of losing all of your blasting men, because if you
Xdo so then you cannot do anything about the opponent's maze defense.
X.SH "PLAYING COMMANDS"
XFollowing are the commands used in the playing phase of the game.
XThese commands can be preceeded by an optional number, which is a
Xrepeat count.
XThey can be edited before they are complete by using the normal
Xediting characters.
XA partial command can be cleared by typing an escape character.
X.TP 6
X.B h
XMove to the left when possible.
XA missing count means move left by one location.
X.TP
X.B j
XMove downward when possible.
X.TP
X.B k
XMove upward when possible.
X.TP
X.B l
XMove right when possible.
X.TP
X.B H
XMove to the left when possible.
XA missing count implies an infinite movement.
X.TP
X.B J
XMove downward when possible.
X.TP
X.B K
XMove upward when possible.
X.TP
X.B L
XMove to the right when possible.
X.TP
X.B F<dir>
XFight the man in the specified direction when possible.
XThe direction character can be one of the four characters 'h', 'j', 'k', or 'l'.
XAlso, 'a' picks randomly among all directions where men can be fought.
XFinally, 'n' fights in no direction at all.
X.TP
X.B B<dir>
XBlast a wall in the specified direction when possible.
XThe direction characters are the same as in the 'F' command.
X.TP
X.B <space>
XIf a null command is given, then this causes the man to stop and return
Xto the idle state.
XIn this state, fighting still occurs if possible because of the implicit
Xfight command.
X.TP
X.B ^L
XRedraw the screen to fix glitches.
XThis command does not need any terminating space or carriage return.
X.PP
XThe following gives some examples of commands to give to men.
XIn the following, "<sp>" represents the space character.
X.sp
X.TP 15
X.B aL4j<sp>
XTell man 'a' to move as far as possible to the right, and to move a total
Xof four rows down.
XThe rightmost movement is to occur first when possible.
X.TP
X.B ybkK<sp>
XTell man 'y' to try to blast a wall directly above him, and when there is
Xno wall to blast, then try to move up.
X.TP
X.B cfjfh<sp>
XTell man 'c' to fight a man directly down from him, and if there is no
Xman there, then fight the man to his left.
X.TP
X.B b<sp>
XTell man 'b' to do nothing.
XHowever, if any enemy is next to him, he will fight them.
X.TP
X.B efn<sp>
XTell man 'e' to really do nothing, not even fight.
X.SH AUTHOR
XDavid I. Bell
X.SH BUGS
XIf the two players don't use the same object file, havoc will result.
XMultiple setups in a setup file by the same name should be detected.
XThe other player should be notified that you are ready to play.
XPlayers should be able to send messages to each other.
XThere is no way to temporarily stop the game (except for using ^Z).
XThere is no code for determining when the game is won, so the game
Xwill keep playing until one player quits.
//E*O*F war.6//
echo x - objects.war
sed -e 's/^X//' > "objects.war" << '//E*O*F objects.war//'
X# Object definition file
X# Possible characters in the flags field:
X#   b	object can blast walls
X#   f	object can fight men
X#   g	object is a goal
X#   i	object is immobile
X#   t	object is transparent
X#   v	object is always visible
X#   w	object is a wall
X#   x	object has x-ray vision
X#
X#flags	display	life	view	min	max
Xw	**	10	0	0	1000		# normal walls
Xtw	..	5	0	0	1000		# windows
Xvwx	++	10	4	0	2		# sentry posts
Xf	A1	25	3	1	1		# best men
Xf	B1	25	3	1	1
Xf	C1	25	3	1	1
Xf	D2	20	2	1	1		# secondary men
Xf	E2	20	2	1	1
Xf	F2	20	2	1	1
Xf	G2	20	2	1	1
Xf	H2	20	2	1	1
Xfx	X3	15	2	1	1		# x-ray vision man
Xb	Y4	30	2	1	1		# wall eater men
Xb	Z4	30	2	1	1
Xgi	##	50	4	1	1		# goal
//E*O*F objects.war//
echo x - boards.war
sed -e 's/^X//' > "boards.war" << '//E*O*F boards.war//'
X"a"
X*    *                      *   *
X*  *   ****************** *   * * * ***********
X****** *             *  * ******* *          *
X ** **** ********* *    * *#      * ******** * *
X      **    *   *   ***** ********* * *   *  *
X ******  ** * *   *                 *   *    **
X ******* ** ************************************
X                                          E X
X**********BDC*****H*****G***F*************Y Z***
X***********A******************************* ****
X"b"
X        * *                        *
X ******** * *************** **** * * *** *** **
X        *   *      * *      **#* * * * * * * *
X******* ********** * ********* * * * * * * * * *
X *           *     *         * * * * * * * * *
X ***** ******* ************* * *** * * * * * **
X *                                 * * * * * *
X   ********* ***F****************  *** * * ***GH
X**           +*CED            Z*+            *XY
X***************B.A******************************
X"teeth"
X * * * * * * * * * * * * * * * * * * * * * * * *
X * * * * * * * * * * * * * * * * * * * * * * * *
X * * * * * * * * * * * * * * * * * * * * * * * *
X * * * * * * * * * * * * * * * * * * * * * * * *
X                                               *
X********************************* **************
X* * *X* * * * * * * * *Y* * * * * * * * * *Z* *
X* * * * * * * * # * * * * * * * * * * * * * * *
X* * * * * * * *G***H* * * * * * * * * * * * * *
X* * * * * *A*B*C*D*E*F* * * * * * * * * * * * *
X"rooms"
X     *      *       *      *      *      *
X****   ****   *****   ****   ****   ****   ****
X    * *    * *     * *    * *    * *    * *   *
X ** * * ** * * *** * * ** * * ** * * ** * * * *
X *  * * *  * * *#* * * *  * * *  * * *  * * * *
X  * * * *  * * *C  * * *  * * *  * * *  * * * *Z
X*  ** * **** * ***** * **** * **** * **** * ***F
X**    *              *             *        XGDE
XY**********************************************B
XH****+ ************** ************* ****** +***A
X"stronghole"
X       *            **  E  *   F*****      *
X *************** *** ***#*   **       **** * ***
X             * * * *  *******  G***** *  * * *
X************ * * * * *  *   **      * *  * * *
X        YZC
X********X**************************************
X    *  DAB *                 **           **
X* ***  * * ******* ************ ************ ***
X       * *
X********H***************************************
X"sinewave"
X          **               *  *      *
X**** *** *** *** **** **** *  * *  * *  * *
X      **       *    *    *      *  *C*  * *
X *******.***************************#*****  ****
X ****   ******* * ** * ** * **   *******   *****
X      *         *    *    *    *    *    *
X***** * *******   **   **   ** * **   ** * ****F
X***** ***************************************  E
X  HXY                  D                     ZGB
X** ************+****************+**************A
X"badsides"
X     ** ***         C#** *   **
X * * **     ************   *  * * ****   ** ***
X *   **  ***        ** * **   * * **   * **   *
X ******  *   ******    * *  **  * ** * * ******
X      **   ************  * *   *  ** *  **    *
X   *   **  **        *  *  * **  **    **   *
X******  **  *******  * **    *  **  ****  ******
XYG  *  **              *  *  ** **  ** **  * EXZ
X F*   **  **************   *  * **      **   *D
X*A************+*******H*******+***************B*
X"diag"
X*       *     *     *     *     *     *       C
X  *   *  *  *  *  *  *  *  *  *  *  *  *  *  * #
X *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  **
X  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *
X*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *
X *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *
X  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *
X      *  *  *     *     *     *     *     *    *
X ****  *      *  *  *  *  *  *  *  *  *  *  *  Z
XGABYE***************X**F**H********************D
//E*O*F boards.war//
echo x - makefile
sed -e 's/^X//' > "makefile" << '//E*O*F makefile//'
X# @(#)makefile	1.1	4/5/85
X# @(#)
X
XCFLAGS = -O
XCC = cc
X
XCFILES = main.c talk.c cmd.c view.c init.c scan.c
XOFILES = main.o talk.o cmd.o view.o init.o scan.o
X
Xwar:	${OFILES}
X	${CC} -o war ${OFILES} -ldpy -ltermlib
X
X${OFILES}:	war.h
//E*O*F makefile//
echo x - war.h
sed -e 's/^X//' > "war.h" << '//E*O*F war.h//'
X/*
X * @(#)war.h	1.1	4/5/85
X * @(#)Copyright (C) 1985 by D Bell
X */
X
X#include <stdio.h>
X#include <setjmp.h>
X#include <signal.h>
X#include <errno.h>
X#include <sgtty.h>
X
X#define	GAMEDIR	"/tmp"			/* for UNIX domain socket names */
X#define	LIBDIR	"/usr/games/lib"	/* object file library area */
X#define	OBJECTFILE "/usr/games/lib/objects.war"	/* default object file */
X#define	SETUPFILE ".warsetups"		/* default setup file in user's HOME */
X
X#define	ROWS	24		/* rows on board */
X#define	COLS	48		/* columns on board */
X#define	HOMEROW	9		/* last row of my home area */
X#define	DATAROW	17		/* row where data statistics go */
X#define	INFOCOL	(COLS + 3)	/* leftmost column of information area */
X#define	OBJS	50		/* max number of objects */
X#define	CMDS	20		/* maximum number of commands */
X#define	INFINITY 10000		/* "infinite" loop value */
X
X#define	SCAN_SIZE 100		/* buffer size for scanning */
X#define	SCAN_EDIT 2		/* kinds of scan longjmps */
X#define	SCAN_ABORT 3
X#define	SCAN_EOF 4
X
X#define	STDIN	0		/* normal file descriptors */
X#define	STDOUT	1
X#define	STDERR	2
X
X
X/*
X * Stored commands for execution by an object
X */
Xstruct	cmd	{
X	char	c_type;		/* command type */
X	char	c_subtype;	/* subtype */
X	short	c_count;	/* repeat counter */
X};
X
X
X/*
X * Information about each object in the game (walls, men, goals, or edges).
X * Singly placed objects point to the cell the object is at.
X * Multiply placed objects (such as walls) have a NULL pointer.
X * In all cases, each cell the object is at points back to the object.
X */
Xstruct	object	{
X	short o_flags;		/* flags about object (see below) */
X	short o_id;		/* unique id for object */
X	short o_life;		/* hit points left */
X	short o_min;		/* minimum number of this object to place */
X	short o_max;		/* maximum number of this object to place */
X	short o_count;		/* current count of objects on board */
X	char o_view;		/* viewing range of object */
X	char o_side;		/* which side object is owned by */
X	char o_ownch;		/* object as seen by its owner */
X	char o_altch;		/* object as seen by others */
X	struct cell *o_cell;	/* cell object is at if a single object */
X	struct cmd o_cmds[CMDS];	/* current command list */
X};
X
X
X/* Flags in the o_flags field of an object */
X#define	F_FIGHT 0x1		/* object can fight others - 'f' */
X#define	F_BLAST	0x2		/* object can blast walls - 'w' */
X#define	F_IMMOB	0x4		/* object is immobile - 'i' */
X#define	F_GOAL	0x8		/* object is a goal - 'g' */
X#define	F_WALL	0x10		/* object is a wall - 'w' */
X#define	F_XRAY	0x20		/* object has x-ray vision - 'x' */
X#define	F_TRANS	0x40		/* object is transparent - 't' */
X#define	F_VIS	0x80		/* object is always visible - 'v' */
X#define	F_MOVED	0x100		/* object has moved this turn */
X#define	F_EDGE	0x200		/* object is an edge */
X
X/* Macro to find the proper character for an object */
X#define	objectchar(obj)	(((obj)->o_side==myside)?(obj)->o_ownch:(obj)->o_altch)
X
X
X/*
X * Information about each location on the board.  Each cell has a pointer
X * to the object which is at that cell, if any.  Cells are linked together
X * in various ways to make it easy to move to adjacent cells.  The edges of
X * the board are linked to a special 'edge' cell.
X */
Xstruct	cell	{
X	unsigned char c_row;	/* row that this cell is for */
X	unsigned char c_col;	/* column that this cell is for */
X	char c_seen;		/* object is seen by the player */
X	struct object *c_obj;	/* object at this location */
X	long c_checkcount;	/* counter for board validity checking */
X	struct cell *c_next;	/* next cell on board */
X	struct cell *c_dirs[8];	/* pointers to other cells (see below) */
X};
X
X
X/* Directions.  The first four must be the orthagonal directions. */
X#define	c_up	c_dirs[0]	/* next cell upwards */
X#define	c_right	c_dirs[1]	/* next cell to the right */
X#define	c_down	c_dirs[2]	/* next cell downwards */
X#define	c_left	c_dirs[3]	/* next cell to the left */
X#define	c_ul	c_dirs[4]	/* cell to upper left */
X#define	c_ur	c_dirs[5]	/* cell to upper right */
X#define	c_ll	c_dirs[6]	/* cell to lower left */
X#define	c_lr	c_dirs[7]	/* cell to lower right */
X
X
X
X/*
X * Information which is transferred between the players.
X * When it is a player's turn to transmit, as many of these messages are
X * sent as is necessary.  The last such message has a type of 'r' as a
X * signal that it is the opponent's turn to transmit.
X */
Xstruct	info	{
X	unsigned char i_type;	/* type of information */
X	unsigned char i_row;	/* row number */
X	unsigned char i_col;	/* column number */
X	short i_id;		/* id of object */
X};
X
X
X/*
X * Statistics about the games which is known to both players.
X * There is one of these structures for each player.
X */
Xstruct	data	{
X	short d_fightmen;	/* count of fighting men */
X	short d_blastmen;	/* count of blasting men */
X	short d_movemen;	/* count of moving men */
X	short d_goalmen;	/* count of goal men */
X	short d_totalmen;	/* total number of men */
X	short d_fightlife;	/* total life of fighting men */
X	short d_blastlife;	/* total life of blasting men */
X	short d_movelife;	/* total life of movable men */
X	short d_goallife;	/* total life of goal men */
X	short d_totallife;	/* total life of all men */
X	short d_totalwalls;	/* total number of walls */
X};
X
X
Xextern	int	errno;			/* error value */
Xchar	myside;				/* what side I am on */
Xchar	newstat;			/* need new status display */
Xchar	playing;			/* actually playing now */
Xchar	editflag;			/* just want to edit some setups */
Xchar	*objectfile;			/* file name for reading objects */
Xchar	*setupfile;			/* file name for board setups */
Xchar	*setupname;			/* setup name to read in */
Xchar	*enemyname;			/* login name of the enemy */
Xchar	*enemytty;			/* tty name of the enemy (if given) */
Xstruct	object	*endobjects;		/* end of active objects */
Xstruct	cell	*firstcell;		/* first cell of whole board */
Xstruct	cell	*homecell;		/* first cell in home area */
Xstruct	cell	edge;			/* edge cell */
Xstruct	object	edgeobj;		/* edge object */
Xstruct	object	objects[OBJS];		/* table of objects */
Xstruct	cell	board[ROWS][COLS];	/* cells of the board */
Xstruct	data	mydata;			/* data for myself */
Xstruct	data	hisdata;		/* data for other player */
Xjmp_buf	ttyjmp;				/* input jump buffer */
X
Xstruct	cell	*pickdir();		/* routine to select a direction */
Xstruct	object	*findobject();		/* routine to look up an object */
Xstruct	object	*findid();		/* find an object by its id */
Xint	ttychar();			/* terminal input routine */
//E*O*F war.h//
echo done

dbell@daisy.UUCP (David I. Bell) (04/05/85)

> P> 
#---Cut here and place in it's own directory, then feed to Bourne shell---
# This is a shell archive.  Remove anything before this line, then
# unpack it by saving it in a file and typing "sh file".  (Files
# unpacked will be owned by you and have default permissions.)
# This archive contains:
#   cmd.c (7867 chars)
#   init.c (18991 chars)
#   main.c (8760 chars)
#   scan.c (3505 chars)
#   talk.c (7531 chars)
#   view.c (6135 chars)
#
echo x - cmd.c
sed -e 's/^X//' > "cmd.c" << '//E*O*F cmd.c//'
X#ifdef SCCS
Xstatic char *sccsid = "@(#)cmd.c	1.2	4/5/85";
Xstatic char *cpyrid = "@(#)Copyright (C) 1985 by D Bell";
X#endif
X
X#include "war.h"
X
Xint	testblast();		/* routine to test is blasting is ok */
Xint	testfight();		/* routine to test if fighting is ok */
X
X/*
X * Read any command from us and set them for the object to work on.
X * This routine never blocks for input.  Partially finished commands
X * will be delayed until they are complete.  Such partial commands
X * can be edited.  Command errors ring the terminal bell.
X */
Xdocommands()
X{
X	register char	*cp;			/* character pointer */
X	register int	ch;			/* current character */
X	register int	objchar;		/* character for object */
X	char	buf[SCAN_SIZE+1];		/* input buffer */
X
X	if (setjmp(ttyjmp) == SCAN_EOF) {	/* done with commands */
X		runcommands();
X		sendinfo('r', 0, 0, 0);
X		return;
X	}
X	objchar = scanchar();			/* get object char */
X	if ((objchar == ' ') || (objchar == '\n') || (objchar == '\033'))
X		scanabort();
X	if (ch == '\014') {
X		dpyredraw();
X		scanabort();
X	}
X	cp = buf;
X	while (1) {				/* get commands for object */
X		ch = scanchar();
X		if ((ch == ' ') || (ch == '\n')) break;
X		if (ch == '\033') scanabort();
X		*cp++ = ch;
X	}
X	*cp = '\0';
X	if (setcmds(findobject(objchar), buf))
X		write(STDERR, "\7", 1);
X	scanabort();
X}
X
X
X/*
X * Routine call by scanchar to read the next input character from
X * the terminal.  Longjmps away if a character is not ready yet.
X */
Xttychar()
X{
X	long	n;			/* char count */
X	unsigned char	ch;		/* char to return */
X
X	if (playing && (ioctl(STDIN, FIONREAD, &n) == 0) && (n <= 0)) {
X		scaneof();		/* no char available now */
X	}
X	do {
X		errno = 0;
X		n = read(STDIN, &ch, 1);	/* read one char */
X	} while ((n < 0) && (errno == EINTR));
X	return((int)(ch &= 0x7f));
X}
X
X
X
X/*
X * Execute one move's worth of commands for all of the user's objects.
X * Returns nonzero if something at all moved.
X */
Xruncommands()
X{
X	register struct	object	*obj;		/* current object */
X	register int	trymore;		/* nonzero if try again */
X	register int	moved;			/* something moved */
X
X	for (obj = objects; obj < endobjects; obj++)
X		obj->o_flags &= ~F_MOVED;
X	moved = 0;
X	do {
X		trymore = 0;
X		for (obj = objects; obj < endobjects; obj++) {
X			trymore |= runobject(obj);
X		}
X		moved |= trymore;
X	} while (trymore);
X	return(moved);
X}
X
X
X/*
X * Attempt to run one move's worth of commands for an object.
X * Returns nonzero if a command was actually executed.
X * In addition, the object is flagged as having made it's move.
X */
Xrunobject(obj)
X	register struct	object	*obj;		/* object to execute */
X{
X	register struct	cmd	*cmd;		/* current command to execute */
X	register struct	cell	*cell;		/* current cell */
X	register struct	cell	*newcell;	/* new cell */
X	register struct	object	*newobj;	/* other object */
X	int	trying;				/* trying to do commands */
X
X	if ((obj->o_side != myside) || (obj->o_flags & F_MOVED)) return(0);
X	cell = obj->o_cell;
X	if (cell == NULL) return(0);
X	trying = 0;
X	for (cmd = obj->o_cmds; cmd->c_type; cmd++) {
X		if (cmd->c_count <= 0) continue;
X		newcell = NULL;
X		switch (cmd->c_type) {
X			case 'b':		/* blast a wall */
X				if (tryblast(obj, cmd->c_subtype)) return(1);
X				break;
X			case 'f':		/* fight the enemy */
X				if (tryfight(obj, cmd->c_subtype)) return(1);
X				break;
X			case 'h':		/* move left */
X				newcell = cell->c_left;
X				break;
X			case 'j':		/* move down */
X				newcell = cell->c_down;
X				break;
X			case 'k':		/* move up */
X				newcell = cell->c_up;
X				break;
X			case 'l':		/* move right */
X				newcell = cell->c_right;
X				break;
X			default:
X				panic("running unknown command");
X		}
X		if ((newcell == NULL) || (newcell == &edge)) continue;
X		newobj = newcell->c_obj;
X		if (newobj) {
X			trying |= ((newobj->o_flags & F_WALL) == 0);
X			continue;
X		}
X		placeobject(obj, newcell->c_row, newcell->c_col);
X		sendinfo('p', newcell->c_row, newcell->c_col, obj->o_id);
X		obj->o_flags |= F_MOVED;
X		if (cmd->c_count < INFINITY) cmd->c_count--;
X		return(1);
X	}
X	return(0);
X}
X
X
X/*
X * Attempt to blast a wall in a given direction.
X * Returns nonzero if we actually tried to do it.
X */
Xtryblast(obj, dir)
X	register struct	object	*obj;		/* object doing fighting */
X{
X	register struct	cell	*cc;		/* current cell */
X
X	if ((obj->o_flags & F_MOVED) || ((obj->o_flags & F_BLAST) == 0))
X		return(0);
X	cc = pickdir(obj->o_cell, dir, testblast);
X	if (cc == NULL) return(0);
X	obj->o_flags |= F_MOVED;
X	obj = cc->c_obj;
X	if ((obj->o_life >= 100) ||
X		((obj->o_life > 0) && (random() % obj->o_life)))
X			return(1);
X	removeobject(cc->c_row, cc->c_col);
X	sendinfo('b', cc->c_row, cc->c_col, 0);
X	return(1);
X}
X
X
X/* See if a location is worth blasting */
Xtestblast(cc)
X	register struct	cell *cc;		/* cell to check */
X{
X	return((cc != &edge) && cc->c_obj && (cc->c_obj->o_flags & F_WALL));
X}
X
X
X/*
X * Attempt to fight in a given certain direction.
X * Returns nonzero if we actually did fight.
X */
Xtryfight(obj, dir)
X	register struct	object	*obj;		/* object doing fighting */
X{
X	register struct	cell	*cc;		/* current cell */
X
X	if ((obj->o_flags & F_MOVED) || ((obj->o_flags & F_FIGHT) == 0))
X		return(0);
X	cc = pickdir(obj->o_cell, dir, testfight);
X	if (cc == NULL) return(0);
X	obj->o_flags |= F_MOVED;
X	if (random() % 2) return(1);
X	sendinfo('h', cc->c_row, cc->c_col, 0);
X	hitobject(cc->c_row, cc->c_col);
X	return(1);
X}
X
X
X/* See if a cell is worth fighting */
Xtestfight(cc)
X	struct	cell *cc;			/* cell to check */
X{
X	register struct	object	*obj;		/* object to fight */
X
X	obj = cc->c_obj;
X	if (obj == NULL) return(0);
X	return(((obj->o_flags&(F_WALL|F_EDGE))== 0) && (obj->o_side != myside));
X}
X
X
X/*
X * Specify the given list of commands to the specified object.
X * Returns nonzero if a bad command is given or if too many are specified.
X * If the string is valid, any previous commands are cancelled and
X * the new ones are in effect.
X */
Xsetcmds(obj, str)
X	register struct	object	*obj;		/* current object */
X	register char	*str;			/* command string */
X{
X	register struct	cmd	*cmd;		/* next command to store */
X	register int	count;			/* count for command */
X	register int	gotcount;		/* got a count */
X	struct	cmd	newcmds[CMDS];		/* new commands */
X
X	if ((obj == NULL) || (obj->o_side != myside)) return(1);
X	cmd = newcmds;
X	while (*str) {
X		count = 0;			/* read numeric argument */
X		gotcount = 0;
X		while ((*str >= '0') && (*str <= '9')) {
X			count = (count * 10) + *str++ - '0';
X			gotcount = 1;
X		}
X		if (count >= (INFINITY - 1)) count = (INFINITY - 1);
X		if (gotcount == 0) count = INFINITY;
X		if (count <= 0) return(1);
X		switch (*str) {
X			case 'b':		/* blast in a direction */
X			case 'f':		/* fight in a direction */
X				if (cmd >= &newcmds[CMDS-2]) return(1);
X				cmd->c_type = *str++;
X				switch (*str) {		/* verify */
X					case 'h': case 'l':
X					case 'j': case 'k':
X					case 'a': case 'n':
X						cmd->c_subtype = *str++;
X						cmd->c_count = INFINITY;
X						cmd++;
X						continue;
X				}
X				return(1);
X
X			case 'h':		/* move left small amount */
X			case 'j':		/* move down small amount */
X			case 'k':		/* move up small amount */
X			case 'l':		/* move right small amount */
X				if (cmd >= &newcmds[CMDS-2]) return(1);
X				if (gotcount == 0) count = 1;
X				cmd->c_type = *str++;
X				cmd->c_subtype = '\0';
X				cmd->c_count = count;
X				cmd++;
X				continue;
X
X			case 'H':		/* move left large amount */
X			case 'J':		/* move down large amount */
X			case 'K':		/* move up large amount */
X			case 'L':		/* move right large amount */
X				if (cmd >= &newcmds[CMDS-2]) return(1);
X				cmd->c_type = *str++ - 'A' + 'a';
X				cmd->c_subtype = '\0';
X				cmd->c_count = count;
X				cmd++;
X				continue;
X
X			default:		/* illegal */
X				return(1);
X		}
X	}
X	cmd->c_type = 'f';			/* add default fight command */
X	cmd->c_subtype = 'a';
X	cmd->c_count = INFINITY;
X	cmd++;
X	cmd->c_type = '\0';			/* end command table */
X	bcopy(newcmds, obj->o_cmds, sizeof(obj->o_cmds));	/* set cmds */
X	return(0);
X}
//E*O*F cmd.c//
echo x - init.c
sed -e 's/^X//' > "init.c" << '//E*O*F init.c//'
X#ifdef SCCS
Xstatic char *sccsid = "@(#)init.c	1.2	4/5/85";
Xstatic char *cpyrid = "@(#)Copyright (C) 1985 by D Bell";
X#endif
X
X#include "war.h"
X
Xstatic	int	checkcount;		/* counter for board checking */
Xstatic	int	didescape;		/* did escape */
Xchar	*ask();				/* routine to ask a question */
Xint	getaskchar();			/* routine to get chars */
XFILE	*opensetupfile();		/* open setup files */
X
X
X/*
X * Get commands from the user to initialize the board.
X */
Xuserinit()
X{
X	register struct	object	*obj;	/* object being placed */
X	register int	ch;		/* current character */
X	register char	*str;		/* answer for user's question */
X	register int	countone;	/* command count defaulted to one */
X	register int	countinf;	/* count defaulted to infinity */
X	int	gotcount;		/* true if read a count */
X	int	row;			/* row to place it on */
X	int	col;			/* column to place it on */
X
X	row = 0;
X	col = 0;
X	obj = NULL;
X	newstat = 1;
X	setjmp(ttyjmp);			/* come back here for every command */
X	if (newstat) viewstats();
X	dpymove(row, col);
X	dpyupdate();
X	countone = 0;
X	gotcount = 0;
X	while (1) {			/* read numeric argument if any */
X		ch = scanchar();
X		if ((ch < '0') || (ch > '9')) break;
X		countone = (countone * 10) + ch - '0';
X		gotcount = 1;
X	}
X	if (countone < 0) countone = 0;
X	if (countone > 100) countone = 100;
X	countinf = countone;
X	if (gotcount == 0) {		/* default values if necessary */
X		countone = 1;
X		countinf = 100;
X	}
X	switch (ch) {
X		case '\033':		/* ESCAPE - ignore command */
X			break;
X		case '\014':		/* ^L - redraw screen */
X			dpyredraw();
X			break;
X		case '\n':		/* move to next line */
X			row += countone;
X			if (row > HOMEROW) row = HOMEROW;
X			col = 0;
X			break;
X		case '-':		/* move to previous line */
X			row -= countone;
X			if (row < 0) row = 0;
X			col = 0;
X			break;
X		case '\t':		/* move to next tab stop */
X			while ((col < (COLS-1)) && (++col % 8)) ;
X			break;
X		case '^':		/* move to beginning of row */
X			col = 0;
X			break;
X		case '$':		/* move to end of row */
X			col = (COLS - 1);
X			break;
X		case 'h':		/* move left */
X			col -= countone;
X			if (col < 0) col = 0;
X			break;
X		case 'j':		/* move down */
X			row += countone;
X			if (row > HOMEROW) row = HOMEROW;
X			break;
X		case 'k':		/* move up */
X			row -= countone;
X			if (row < 0) row = 0;
X			break;
X		case 'l':		/* move right */
X		case ' ':
X			col += countone;
X			if (col >= COLS) col = COLS - 1;
X			break;
X		case 'y':		/* move to upper left */
X			while (countone-- && (row > 0) && (col > 0)) {
X				row--;
X				col--;
X			}
X			break;
X		case 'u':		/* move to upper right */
X			while (countone-- && (row > 0) && (col < COLS-1)) {
X				row--;
X				col++;
X			}
X			break;
X		case 'b':		/* move to lower left */
X			while (countone-- && (row < HOMEROW) && (col > 0)) {
X				row++;
X				col--;
X			}
X			break;
X		case 'n':		/* move to lower right */
X			while (countone-- && (row<HOMEROW) && (col<COLS-1)) {
X				row++;
X				col++;
X			}
X			break;
X		case 'f':		/* flip board left-right */
X			flipboard();
X			break;
X		case 'p':		/* place an object */
X			obj = findobject(scanchar());
X			/* proceed into next case */
X		case '.':		/* place same object */
X			placeobject(obj, row, col);
X			break;
X		case 'x':		/* delete an object */
X			while (countone--) {
X				removeobject(row, col);
X				if (col >= (COLS-1)) break;
X				col++;
X			}
X			break;
X		case 'H':		/* place lots left */
X			placeline(obj, &row, &col, 0, -1, countinf);
X			break;
X		case 'J':		/* place lots down */
X			placeline(obj, &row, &col, 1, 0, countinf);
X			break;
X		case 'K':		/* place lots up */
X			placeline(obj, &row, &col, -1, 0, countinf);
X			break;
X		case 'L':		/* place lots right */
X			placeline(obj, &row, &col, 0, 1, countinf);
X			break;
X		case 'Y':		/* place lots to upper left */
X			placeline(obj, &row, &col, -1, -1, countinf);
X			break;
X		case 'U':		/* place lots to upper right */
X			placeline(obj, &row, &col, -1, 1, countinf);
X			break;
X		case 'B':		/* place lots to lower left */
X			placeline(obj, &row, &col, 1, -1, countinf);
X			break;
X		case 'N':		/* place lots to lower right */
X			placeline(obj, &row, &col, 1, 1, countinf);
X			break;
X		case 's':		/* start to play */
X			if (editflag || checksetup()) {
X				beep();
X				break;
X			}
X			if (yesno("Ready to start playing? ")) return;
X			break;
X		case 'q':		/* want to quit */
X			if (yesno("Want to quit? ")) quit(0);
X			break;
X		case 'w':		/* write the setup */
X			str = ask("Write setup named: ");
X			if (str == NULL) break;
X			if (writesetup(setupfile, str)) beep();
X			break;
X		case 'r':		/* read a setup */
X			str = ask("Read setup named: ");
X			if (str == NULL) break;
X			if (readsetup(setupfile, str)) beep();
X			break;
X		case 'c':		/* clear board */
X			if (yesno("Want to clear the board? ")) clearboard();
X			break;
X		default:
X			beep();
X			break;
X	}
X	scanabort();			/* go back up for next command */
X}
X
X
X/*
X * Ask a question and see if the answer is yes.
X * Returns nonzero if so.
X */
Xyesno(str)
X	register char	*str;		/* string to ask, and answer */
X{
X	str = ask(str);
X	if (str == NULL) return(0);
X	return((strcmp(str, "y") == 0) || (strcmp(str, "yes") == 0)
X		|| (strcmp(str, "Y") == 0) || (strcmp(str, "YES") == 0));
X}
X
X
X/*
X * Ask the player a question and get an answer.
X * Leading and trailing spaces are removed from the answer.
X * If an escape or a null answer is given, a null pointer is returned.
X */
Xchar *
Xask(str)
X{
X	register char	*cp;		/* current character */
X	static	char	buf[100];	/* buffer */
X
X	didescape = 0;
X	scanreset();
X	dpywindow(-1, -1, 2, COLS);
X	cp = buf;
X	cp += dpyread(str, getaskchar, cp, sizeof(buf));
X	if (cp < buf) cp = buf;
X	*cp-- = '\0';
X	while ((cp >= buf) && ((*cp == ' ') || (*cp == '\t'))) *cp-- = '\0';
X	for (cp = buf; ((*cp == ' ') || (*cp == '\t')); cp++) ;
X	dpyhome();
X	dpyclrwindow();
X	dpywindow(0, -1, 1, COLS);
X	if (didescape || (*cp == '\0')) cp = NULL;
X	return(cp);
X}
X
X
X/* Routine called by dpyread to get characters */
Xgetaskchar()
X{
X	char	ch;
X
X	if ((read(STDIN, &ch, 1) != 1) || (ch == '\033')) {
X		didescape = 1;
X		return(-1);
X	}
X	if (ch == '\n') return(-1);
X	return((int)(ch & 0x7f));
X}
X
X
X/*
X * Place a run of an object in a certain direction until it is illegal
X * or until the count runs out.  The new row and column are returned
X * through pointers.
X */
Xplaceline(obj, rowptr, colptr, rowsign, colsign, count)
X	register struct	object	*obj;	/* object to place */
X	unsigned int	*rowptr;	/* current row */
X	unsigned int	*colptr;	/* current column */
X	register int	rowsign;	/* changes to make in rows */
X	register int	colsign;	/* changes to make in columns */
X{
X	register unsigned int	row;	/* current row */
X	register unsigned int	col;	/* current column */
X
X	row = *rowptr;
X	col = *colptr;
X	while (count-- > 0) {
X		if (placeobject(obj, row, col)) break;
X		row += rowsign;
X		col += colsign;
X		if ((row > HOMEROW) || (col >= COLS)) {
X			row -= rowsign;
X			col -= colsign;
X			break;
X		}
X		if (board[row][col].c_obj) break;
X	}
X	*rowptr = row;
X	*colptr = col;
X}
X
X
X/*
X * Clear everything from the home area of the board.
X */
Xclearboard()
X{
X	register struct	cell *cc;	/* current cell */
X
X	for (cc = homecell; cc; cc = cc->c_next) {
X		if (cc->c_obj) removeobject(cc->c_row, cc->c_col);
X	}
X}
X
X
X/*
X * Flip the board left to right during the initialization phase
X */
Xflipboard()
X{
X	register int	row;		/* current row */
X	register struct	cell	*lc;	/* left cell */
X	register struct	cell	*rc;	/* right cell */
X	register struct	object	*lobj;	/* left object */
X	register struct	object	*robj;	/* right object */
X
X	for (row = 0; row <= HOMEROW; row++) {
X		lc = &board[row][0];
X		rc = &board[row][COLS-1];
X		for (; lc < rc; lc = lc->c_right, rc = rc->c_left) {
X			lobj = lc->c_obj;
X			robj = rc->c_obj;
X			if (lobj == robj) continue;
X			lc->c_obj = robj;
X			rc->c_obj = lobj;
X			if (lobj && (lobj->o_max <= 1)) lobj->o_cell = rc;
X			if (robj && (robj->o_max <= 1)) robj->o_cell = lc;
X			if (lobj) dpyplace(lc->c_row, lc->c_col, ' ');
X			if (robj) dpyplace(rc->c_row, rc->c_col, ' ');
X			if (lobj) dpyplace(rc->c_row, rc->c_col, lobj->o_ownch);
X			if (robj) dpyplace(lc->c_row, lc->c_col, robj->o_ownch);
X		}
X	}
X}
X
X
X/*
X * Check the setup to make sure we are using all the men we are supposed to.
X * Returns nonzero if some men are missing.
X */
Xchecksetup()
X{
X	register struct	object	*obj;	/* current object */
X
X	for (obj = objects; obj < endobjects; obj++) {
X		if (obj->o_side != myside) continue;
X		if (obj->o_count < obj->o_min) return(1);
X	}
X	return(0);
X}
X
X
X/*
X * Determine if an object just placed on the board leaves a legal layout.
X * This means that each board location is reachable by some path which does
X * not need to cross objects which can be multiply-placed (such as walls).
X * Returns nonzero if the layout is illegal.
X */
Xcheckboard(nc)
X	register struct	cell *nc;	/* new cell just placed */
X{
X	register struct	cell *cc;	/* current cell */
X	register struct	cell *rc;	/* beginning of row cell */
X	register struct	cell **ptr;	/* pointer to adjacent cells */
X
X	if ((nc == NULL) || (nc->c_obj == NULL)) panic("checkboard");
X	if (((nc->c_obj->o_flags&F_WALL) == 0) || (nc->c_obj->o_side != myside))
X		return(0);
X	/*
X	 * See if the placing of the object is trivially known to be legal.
X	 * This is true if nothing surrounds the object, or if a single
X	 * object lies next to it which isn't surrounded.  We must search
X	 * in all eight directions for this check.
X	 */
X	cc = NULL;
X	for (ptr = nc->c_dirs; ptr < &nc->c_dirs[8]; ptr++) {
X		rc = *ptr;
X		if ((rc->c_obj == NULL) || ((rc->c_obj->o_flags & F_WALL) == 0))
X			continue;
X		if (cc) goto hard;
X		cc = rc;
X	}
X	if (cc == NULL) return(0);
X	for (ptr = cc->c_dirs; ptr < &cc->c_dirs[8]; ptr++) {
X		rc = *ptr;
X		if ((rc->c_obj == NULL) || ((rc->c_obj->o_flags & F_WALL) == 0))
X			return(0);
X	}
X	/*
X	 * At least two other objects lie next to the new one.
X	 * We must do the brute force check now.
X	 */
Xhard:	rc = &board[HOMEROW+1][0];
X	rc->c_checkcount = ++checkcount;
X	checkboardloop(rc);
X	for (rc = homecell; rc; rc = rc->c_next)
X		if (rc->c_checkcount != checkcount) return(1);
X	return(0);
X}
X
X
X/*
X * Recursive subroutine used for marking accessible locations on the board.
X * Call ourself for each new cell in the four orthagonal directions from
X * the given cell.
X */
Xcheckboardloop(cc)
X	register struct	cell	*cc;	/* current cell to check */
X{
X	register struct	cell	*tc;	/* temporary cell */
X	register struct	cell	**ptr;	/* cell pointer */
X	register int	count;		/* count of cells to check */
X	struct	cell	*celltab[4];	/* table of cells to check */
X
X	while (1) {
X		count = 0;
X		for (ptr = cc->c_dirs; ptr < &cc->c_dirs[4]; ptr++) {
X			tc = *ptr;
X			if (tc->c_obj == &edgeobj) continue;
X			if (tc->c_row > (HOMEROW + 1)) continue;
X			if (tc->c_checkcount == checkcount) continue;
X			tc->c_checkcount = checkcount;
X			if (tc->c_obj && (tc->c_obj->o_flags & F_WALL))
X				continue;
X			celltab[count++] = tc;
X		}
X		if (count != 1) break;
X		cc = celltab[0];	/* only one, no recursion needed */
X	}
X	while (count > 0) checkboardloop(celltab[--count]);
X}
X
X
X/*
X * Initialize the cells of the board
X */
Xboardinit()
X{
X	register struct	cell *cc;	/* current cell */
X	register int	row;		/* current row */
X	register int	col;		/* current column */
X	register int	ch;		/* character */
X
X	cc = &edge;
X	cc->c_row = -1;
X	cc->c_col = -1;
X	cc->c_up = cc;
X	cc->c_down = cc;
X	cc->c_left = cc;
X	cc->c_right = cc;
X	cc->c_ul = cc;
X	cc->c_ur = cc;
X	cc->c_ll = cc;
X	cc->c_lr = cc;
X	cc->c_obj = &edgeobj;
X	edgeobj.o_ownch = '*';
X	edgeobj.o_altch = '*';
X	edgeobj.o_side = -1;
X	edgeobj.o_flags = (F_WALL|F_EDGE);
X	for (row = 0; row < ROWS; row++) {
X		for (col = 0; col < COLS; col++) {
X			cc = &board[row][col];
X			cc->c_row = row;
X			cc->c_col = col;
X			cc->c_up = &board[row-1][col];
X			cc->c_down = &board[row+1][col];
X			cc->c_left = &board[row][col-1];
X			cc->c_right = &board[row][col+1];
X			cc->c_ul = &board[row-1][col-1];
X			cc->c_ur = &board[row-1][col+1];
X			cc->c_ll = &board[row+1][col-1];
X			cc->c_lr = &board[row+1][col+1];
X			cc->c_next = cc->c_left;
X			cc->c_obj = NULL;
X		}
X		cc = &board[row][0];
X		cc->c_left = &edge;
X		cc->c_ul = &edge;
X		cc->c_ll = &edge;
X		cc->c_next = &board[row-1][COLS-1];
X		cc = &board[row][COLS-1];
X		cc->c_right = &edge;
X		cc->c_ur = &edge;
X		cc->c_lr = &edge;
X	}
X	for (col = 0; col < COLS; col++) {
X		cc = &board[0][col];
X		cc->c_up = &edge;
X		cc->c_ul = &edge;
X		cc->c_ur = &edge;
X		cc = &board[ROWS-1][col];
X		cc->c_down = &edge;
X		cc->c_ll = &edge;
X		cc->c_lr = &edge;
X	}
X	board[0][0].c_next = NULL;
X	firstcell = &board[ROWS-1][COLS-1];
X	homecell = &board[HOMEROW][COLS-1];
X	/*
X	 * Initialize the looks of the board
X	 */
X	for (row = 0; row < ROWS; row++) {
X		ch = '|';
X		if ((row == HOMEROW) || (row == (ROWS - HOMEROW - 1))) ch = '+';
X		dpychar(ch);
X		for (col = 0; col < COLS; col++) dpychar(' ');
X		dpychar(ch);
X		dpychar('\n');
X	}
X	dpywindow(0, -1, 1, COLS);
X}
X
X
X/*
X * Clear the board and read in a setup from a board file.
X * If a null board name is specified, a default one in the user's home
X * directory is used.  Returns nonzero if the file cannot be found.
X */
Xreadsetup(setupfile, setupname)
X	char	*setupfile;		/* filename for board setups */
X	register char	*setupname;	/* name of setup */
X{
X	register char	*cp;		/* current character */
X	register int	row;		/* current row */
X	register int	col;		/* current column */
X	register FILE	*fd;		/* file handle */
X	char	buf[200];		/* line of the file */
X
X	if (setupname == NULL) return(0);
X	/*
X	 * Open file and search for setup name
X	 */
X	fd = opensetupfile(setupfile, "r");
X	if (fd == NULL) return(1);
X	do {
X		cp = buf;
X		if (fgets(cp, sizeof(buf), fd) == NULL) {
X			fclose(fd);
X			return(1);
X		}
X		if (*cp++ != '"') continue;
X		while (*cp && (*cp != '"') && (*cp != '\n')) cp++;
X		*cp = '\0';
X	} while (strcmp(setupname, &buf[1]));
X	/*
X	 * Found the setup, clear the board and read it in
X	 */
X	clearboard();
X	for (row = 0; row <= HOMEROW; row++) {
X		cp = buf;
X		if (fgets(cp, sizeof(buf), fd) == NULL) break;
X		if (*cp == '"') break;
X		for (col = 0; (col < COLS) && *cp; col++, cp++) {
X			if (*cp == '\n') break;
X			if (*cp == '\t') col |= 7;
X			if ((*cp <= ' ') || (*cp == 0177)) continue;
X			placeobject(findobject(*cp), row, col);
X		}
X	}
X	fclose(fd);
X	return(0);
X}
X
X
X/*
X * Append the current setup to the specified file.
X * Existing setups by the same name are not removed (which is a bug).
X * This is to make the algorithm easy.
X */
Xwritesetup(setupfile, setupname)
X	char	*setupfile;		/* filename for board setups */
X	char	*setupname;		/* name of setup */
X{
X	register struct	cell	*rc;	/* cell at front of row */
X	register struct	cell	*cc;	/* current cell in row */
X	register char	*cp;		/* current character */
X	register FILE	*fd;		/* file handle */
X	int	error;			/* error flag */
X	char	buf[COLS+2];		/* data to write */
X
X	if (setupname == NULL) return(1);
X	fd = opensetupfile(setupfile, "a");
X	if (fd == NULL) return(1);
X	fprintf(fd, "\"%s\"\n", setupname);
X	for (rc = &board[0][0]; rc->c_row <= HOMEROW; rc = rc->c_down) {
X		for (cc = rc, cp = buf; cc != &edge; cc = cc->c_right) {
X			*cp++ = cc->c_obj ? cc->c_obj->o_ownch : ' ';
X		}
X		cp = &buf[COLS-1];
X		while ((cp >= buf) && (*cp == ' ')) cp--;
X		*++cp = '\n';
X		*++cp = '\0';
X		fputs(buf, fd);
X	}
X	error = ferror(fd);
X	fclose(fd);
X	return(error);
X}
X
X
X/*
X * Open the given setup file name, defaulting it if necessary.
X * Returns a stdio FILE pointer if successfull, or NULL on an error.
X */
XFILE *
Xopensetupfile(name, mode)
X	register char	*name;		/* file name, or NULL for default */
X	char	*mode;			/* mode to open file in */
X{
X	char	buf[200];		/* buffer for default name */
X
X	if ((name == NULL) || (*name == '\0')) {
X		name = (char *) getenv("HOME");
X		if (name == NULL) return(NULL);
X		sprintf(buf, "%s/%s", name, SETUPFILE);
X		name = buf;
X	}
X	return(fopen(name, mode));
X}
X
X
X/*
X * Read in the parameters for the objects from the specified file.
X * If a null name is given, the default file is used.
X * Doesn't return if an error is encountered.
X */
Xreadobjects(name)
X	register char	*name;		/* filename for objects */
X{
X	register FILE	*fd;		/* file variable */
X	register struct	object	*obj;	/* current object */
X	register int	line;		/* line number */
X	register int	err;		/* error occurred */
X	register int	count;		/* token count */
X	char	flags[21];		/* flag characters */
X	char	seen[3];		/* characters seen as */
X	char	eol[2];			/* end of line character */
X	int	life;			/* life of the object */
X	int	view;			/* viewing range of the object */
X	int	min;			/* minimum number of objects */
X	int	max;			/* maximum number of objects */
X	char	buf[200];		/* data buffer */
X	char	altname[200];		/* alternate name */
X
X	if ((name == NULL) || (*name == '\0')) name = OBJECTFILE;
X	fd = fopen(name, "r");
X	if (fd == NULL) {
X		if (index(name, '/') == 0) {
X			sprintf(altname, "%s/%s", LIBDIR, name);
X			name = altname;
X			fd = fopen(name, "r");
X		}
X		if (fd == NULL) {
X			perror(name);
X			quit(1);
X		}
X	}
X	for (endobjects = objects, err = 0, line = 1; ; line++) {
X		fgets(buf, sizeof(buf), fd);
X		if (feof(fd) || ferror(fd)) break;
X		if (buf[0] == '#') continue;		/* comment line */
X		eol[0] = '#';
X		count = sscanf(buf, "%20s%2s%d%d%d%d%1s\n",
X			flags, seen, &life, &view, &min, &max, eol);
X		if ((count < 6) || (seen[0] < ' ') || (seen[0] == 0177)
X			|| (view < 0) || (min < 0) || (max < min)
X			|| (eol[0] != '#')) {
X				fprintf(stderr, "%s, line %d: bad object\n",
X					name, line);
X				err = 1;
X				continue;
X		}
X		if (endobjects >= &objects[OBJS-1]) {
X			fprintf(stderr, "%s: Too many objects\n", name);
X			quit(1);
X		}
X		obj = endobjects++;		/* allocate object */
X		obj->o_side = 0;
X		obj->o_id = (line * 2);
X		obj->o_ownch = seen[0];
X		obj->o_altch = seen[1];
X		if ((obj->o_altch < ' ') || (obj->o_altch == 0177))
X			obj->o_altch = ' ';
X		obj->o_view = view;
X		obj->o_life = life;
X		obj->o_min = min;
X		obj->o_max = max;
X		obj->o_count = 0;
X		obj->o_cell = NULL;
X		if (setflags(obj, flags)) {
X			fprintf(stderr, "%s, line %d: bad flag bits\n",
X				name, line);
X			err = 1;
X		}
X		setcmds(obj, "");		/* set to fight */
X		if ((obj->o_life == 0) && (obj->o_flags & F_GOAL))
X			obj->o_life = 1;
X		if (obj->o_max > 1) {
X			obj->o_flags |= F_IMMOB;
X			obj->o_flags &= ~(F_FIGHT|F_BLAST);
X		}
X		*endobjects = *obj;		/* duplicate for other side */
X		endobjects->o_side = 1;
X		endobjects->o_id++;
X		endobjects++;
X	}
X	if (err) quit(1);
X	if (ferror(fd)) {
X		perror(name);
X		quit(1);
X	}
X	fclose(fd);
X	myside = 1;
X}
X
X
X/*
X * Parse the flag string for an object and set the appropriate flags.
X * Returns nonzero if they were illegal.
X */
Xsetflags(obj, str)
X	register struct	object	*obj;	/* object to set flags for */
X	register char	*str;		/* string */
X{
X	register int	flags;		/* flag bits */
X
X	flags = 0;
X	while (*str) switch (*str++) {
X		case 'b':	flags |= F_BLAST; break;
X		case 'f':	flags |= F_FIGHT; break;
X		case 'g':	flags |= F_GOAL; break;
X		case 'i':	flags |= F_IMMOB; break;
X		case 't':	flags |= F_TRANS; break;
X		case 'v':	flags |= F_VIS; break;
X		case 'w':	flags |= (F_WALL|F_IMMOB); break;
X		case 'x':	flags |= F_XRAY; break;
X		case '-':	break;
X		default:	return(1);
X	}
X	obj->o_flags = flags;
X	return(0);
X}
//E*O*F init.c//
echo x - main.c
sed -e 's/^X//' > "main.c" << '//E*O*F main.c//'
Xstatic char *sccsid = "@(#)main.c	1.2	4/5/85";
Xstatic char *cpyrid = "@(#)Copyright (C) 1985 by D Bell";
X
X/*
X * Two player war game played in mazes (by David I. Bell).
X * These war game sources are in the public domain, and can be copied
X * or used as desired, with the following restrictions:
X * 1.	All copyright notices (and this notice) must be preserved.
X * 2.	The war game sources (even if modified) cannot be sold for profit.
X * 3.	If any sources are modified, a sentence must exist by the
X *	copyright notice of each modified source file which says that
X *	the file has been modified.
X */
X
X#include "war.h"
X
Xint	sigint();		/* interrupt signal catcher */
X
Xmain(argc, argv)
X	register int	argc;		/* argument count */
X	register char	**argv;		/* argument name */
X{
X	register char	*errstr;	/* reason for option error */
X
X	while (argc-- > 1) {
X		argv++;
X		if (**argv != '-') {		/* not option, must be name */
X			if (enemyname) usage();
X			enemyname = *argv;
X			if ((argc > 1) && (argv[1][0] != '-')) {
X				enemytty = *++argv;
X				argc--;
X			}
X			continue;
X		}
X		errstr = NULL;
X		switch (argv[0][1]) {
X		case 'e':			/* just editing */
X			editflag = 1;
X			break;
X		case 'f':			/* setup file */
X			errstr = "setup file";
X			setupfile = *++argv;
X			argc--;
X			break;
X		case 'o':			/* object file */
X			errstr = "object file";
X			objectfile = *++argv;
X			argc--;
X			break;
X		case 's':			/* setup name */
X			errstr = "setup";
X			setupname = *++argv;
X			argc--;
X			break;
X		default:			/* errors */
X			usage();
X		}
X		if (errstr && ((*argv == NULL) || (**argv == '-'))) {
X			fprintf(stderr, "missing %s name\n", errstr);
X			exit(1);
X		}
X	}
X	if ((enemyname == NULL) && (editflag == 0)) usage();
X	if (editflag == 0) findplayer(enemyname, enemytty);
X	signal(SIGINT, sigint);
X	if (dpyinit(NULL, NULL)) exit(1);
X	scaninit(ttychar, ttyjmp);
X	boardinit();				/* build the board */
X	if (readobjects(objectfile)) {		/* read in objects */
X		fprintf(stderr, "bad object file\n");
X		exit(1);
X	}
X	if (readsetup(setupfile, setupname)) {	/* read in setup */
X		fprintf(stderr, "cannot get setup\n");
X		exit(1);
X	}
X	userinit();
X	dpymove(0, 0);
X	talkinit();				/* set up talking with enemy */
X	while (1) {
X		readinfo();			/* get enemy's changes */
X		view();				/* show current board */
X		sleep(1);			/* wait a bit */
X		docommands();			/* read and execute commands */
X	}
X}
X
X
X/*
X * Tell how to run the game and exit
X */
Xusage()
X{
X	fprintf(stderr, "\
Xusage:  war [-f setupfile] [-s setupname] [-o objectfile] username [ttyname]\n\
X   or:  war -e [-f setupfile] [-s setupname] [-o objectfile]\n");
X	exit(1);
X}
X
X
X/*
X * Find the object of ours with the given character.
X * Lower case letters are converted to upper case if reasonable.
X * Returns NULL if the object cannot be found.
X */
Xstruct object *
Xfindobject(ch)
X{
X	register struct	object	*obj;		/* current object */
X
X	for (obj = objects; obj < endobjects; obj++) {
X		if ((obj->o_ownch == ch) && (obj->o_side == myside)
X			&& ((playing == 0) || obj->o_count))
X				return(obj);
X	}
X	if ((ch >= 'a') && (ch <= 'z'))		/* try upper case */
X		return(findobject(ch - 'a' + 'A'));
X	return(NULL);
X}
X
X
X/*
X * Find the object which has the given id.
X * Fails if the object could not be found.
X */
Xstruct object *
Xfindid(id)
X{
X	register struct	object	*obj;	/* found object */
X
X	for (obj = objects; obj < endobjects; obj++) {
X		if (obj->o_id == id)
X			return(obj);
X	}
X	panic("unknown object");
X}
X
X
X/*
X * Place an object at a particular location.  If the object is incapable
X * of being multiply placed, it is removed from its old location.
X * If another object is currently at this location, it is removed.
X * If the object can be multiply placed then a check is made to insure
X * that all multiply paced objects can be reached from the common area.
X * Returns nonzero if placement cannot be done.
X * The screen database is also updated.
X */
Xplaceobject(obj, row, col)
X	register struct	object	*obj;	/* object to place */
X	unsigned int	row;		/* row to place object at */
X	unsigned int	col;		/* column to place object at */
X{
X	register struct	cell	*cc;	/* cell location */
X	register struct	object	*oldobj;/* old object at this location */
X
X	if (obj == NULL) return(0);
X	if ((row >= ROWS) || (col >= COLS)) panic("placeloc");
X	/*
X	 * Remove any object already at this location
X	 */
X	oldobj = board[row][col].c_obj;
X	if (oldobj) {
X		if (oldobj == obj) return(0);	/* already here */
X		removeobject(row, col);
X	}
X	/*
X	 * If we are elsewhere on the board, remove us from there
X	 */
X	cc = obj->o_cell;
X	if (cc) {
X		if (cc->c_obj != obj) panic("badcell");
X		removeobject(cc->c_row, cc->c_col);
X	}
X	/*
X	 * Put object at the current location
X	 */
X	cc = &board[row][col];
X	cc->c_obj = obj;
X	cc->c_seen = ((obj->o_side == myside) || (obj->o_flags & F_VIS));
X	if (obj->o_max <= 1) obj->o_cell = cc;
X	if (cc->c_seen) dpyplace(row, col, objectchar(obj));
X	obj->o_count++;
X	adjustdata(obj, 1);
X	/*
X	 * Verify that this placement is legal
X	 */
X	if (obj->o_side != myside) return(0);
X	if ((obj->o_count > obj->o_max) || checkboard(cc)) {
X		removeobject(row, col);
X		placeobject(oldobj, row, col);
X		return(1);
X	}
X	return(0);
X}
X
X
X/*
X * Remove an object from the board at a given coordinate.
X * The screen database is also updated.
X */
Xremoveobject(row, col)
X	unsigned int	row;		/* row to remove object from */
X	unsigned int	col;		/* column to remove object from */
X{
X	register struct	cell	*cc;	/* cell location */
X	register struct	object	*obj;	/* object to remove */
X
X	if ((row >= ROWS) || (col >= COLS)) panic("removeloc");
X	cc = &board[row][col];
X	obj = cc->c_obj;
X	if (obj == NULL) return;
X	adjustdata(obj, -1);
X	obj->o_count--;
X	obj->o_cell = NULL;
X	cc->c_obj = NULL;
X	dpyplace(row, col, ' ');
X}
X
X
X/*
X * Adjust the data counts as necessary when an object is added or removed.
X * Delta is 1 if the object is being added, and -1 if the object is
X * being removed.
X */
Xadjustdata(obj, delta)
X	register struct	object	*obj;	/* object being added/removed */
X	register int	delta;		/* change to be made */
X{
X	register struct	data	*dp;	/* pointer to appropriate data */
X	register int	flags;		/* flag bits */
X	register int	deltalife;	/* total life change for object */
X
X	dp = &hisdata;
X	if (obj->o_side == myside) dp = &mydata;
X	flags = obj->o_flags;
X	deltalife = (obj->o_life * delta);
X	if ((flags & F_IMMOB) == 0) {		/* movable men */
X		dp->d_movemen += delta;
X		dp->d_movelife += deltalife;
X	}
X	if (flags & F_FIGHT) {			/* fighting men */
X		dp->d_fightmen += delta;
X		dp->d_fightlife += deltalife;
X	}
X	if (flags & F_BLAST) {			/* blasting men */
X		dp->d_blastmen += delta;
X		dp->d_blastlife += deltalife;
X	}
X	if (flags & F_GOAL) {			/* goals */
X		dp->d_goalmen += delta;
X		dp->d_goallife += deltalife;
X	}
X	if ((flags & F_WALL) == 0) {		/* total men */
X		dp->d_totalmen += delta;
X		dp->d_totallife += deltalife;
X	}
X	if (flags & F_WALL) {			/* walls */
X		dp->d_totalwalls += delta;
X	}
X	newstat = 1;
X}
X
X
X/*
X * Pick one of a specified set of directions from a cell.
X * Each applicable direction is tested using a supplied routine.
X * The routine returns nonzero if the cell in a direction is acceptable.
X * Returns the cell in the picked direction.
X */
Xstruct cell *
Xpickdir(cc, dir, routine)
X	register struct	cell	*cc;	/* cell to move from */
X	int	dir;			/* direction to pick */
X	int	(*routine)();		/* routine to decide */
X{
X	register struct	cell	*tc;	/* test cell */
X	register int	count;		/* number of enemies */
X	register int	i;		/* index */
X	struct	cell	*table[4];	/* possible directions */
X
X	switch (dir) {
X		case 'h':	tc = cc->c_left; break;
X		case 'j':	tc = cc->c_down; break;
X		case 'k':	tc = cc->c_up; break;
X		case 'l':	tc = cc->c_right; break;
X		case 'a':	tc = NULL; break;
X		default:	return(NULL);
X	}
X	if (tc) {				/* try specified direction */
X		if (routine(tc) == 0) tc = NULL;
X		return(tc);
X	}
X	for (count = 0, i = 0; i < 4; i++) {	/* search all directions */
X		tc = cc->c_dirs[i];
X		if (routine(tc)) table[count++] = tc;
X	}
X	if (count == 0) return(NULL);
X	if (count > 1) count = (random() % count) + 1;
X	return(table[count - 1]);
X}
X
X
X/*
X * Remove one hit point from an object at a given location.
X * If it goes to zero, kill the object.
X */
Xhitobject(row, col)
X{
X	register struct	object	*obj;
X
X	obj = board[row][col].c_obj;
X	if (obj == NULL) return;
X	adjustdata(obj, -1);
X	obj->o_life--;
X	adjustdata(obj, 1);
X	if (obj->o_life <= 0) removeobject(row, col);
X	newstat = 1;
X}
X
X
X/*
X * Write a single bell to warn the user of an error
X */
Xbeep()
X{
X	write(STDERR, "\7", 1);
X}
X
X
X/*
X * Fatal error routine
X */
Xpanic(str)
X	char	*str;
X{
X	dpyclose();
X	fprintf(stderr, "Fatal error: %s\n", str);
X	exit(1);
X}
X
X
X/*
X * Reset the terminal modes and exit
X */
Xquit(status)
X{
X	dpyclose();
X	exit(status);
X}
X
X
X/*
X * Here on an interrupt to quit.
X */
Xsigint()
X{
X	dpyclose();
X	exit(0);
X}
//E*O*F main.c//
echo x - scan.c
sed -e 's/^X//' > "scan.c" << '//E*O*F scan.c//'
X#ifdef SCCS
Xstatic char *sccsid = "@(#)scan.c	1.1	4/5/85";
Xstatic char *cpyrid = "@(#)Copyright (C) 1985 by D Bell";
X#endif
X
X/* Module to read self-terminating input while allowing editing of the input */
X
X#include "war.h"
X
Xjmp_buf	*scanjumpbuf;			/* jump buffer to use in scanchar */
Xchar	scanbuffer[SCAN_SIZE+1];	/* storage for characters */
Xchar	*scanreadptr;			/* current read pointer */
Xchar	*scanwriteptr;			/* current write pointer */
Xchar	(*scanroutine)();		/* routine to read characters */
Xstatic	char	rubchar;		/* erase letter character */
Xstatic	char	rubword;		/* erase word character */
Xstatic	char	rubline;		/* erase line character */
Xstatic	char	litchar;		/* literal input */
X
X
X/*
X * Initialize for later calls to scanchar.
X */
Xscaninit(routine, jumpbuf)
X	char	(*routine)();		/* routine to get characters */
X	jmp_buf	*jumpbuf;		/* jump buffer to use later */
X{
X	struct	sgttyb	sgbuf;		/* basic tty structure */
X	struct	ltchars	ltbuf;		/* local tty structure */
X
X	scanroutine = routine;		/* init static variables */
X	scanjumpbuf = jumpbuf;
X	scanwriteptr = scanbuffer;
X	scanreadptr = scanbuffer;
X	sgbuf.sg_erase = CERASE;	/* set defaults in case ioctls fail */
X	sgbuf.sg_kill = CKILL;
X	ltbuf.t_werasc = CWERASE;
X	ltbuf.t_lnextc = CLNEXT;
X	ioctl(STDIN, TIOCGETP, &sgbuf);	/* get and save editing characters */
X	ioctl(STDIN, TIOCGLTC, &ltbuf);
X	rubchar = sgbuf.sg_erase;
X	rubline = sgbuf.sg_kill;
X	rubword = ltbuf.t_werasc;
X	litchar = ltbuf.t_lnextc;
X}
X
X
X/*
X * Read the next input character.  If it is an editing character,
X * abort the current context and longjmp back to the last setjmp.
X * NOTE: for proper results, the caller should not alter the global
X * state until the full command has been read in.  This includes such
X * things as prompting for input or saving values.  Otherwise, improper
X * results will occur if the user edits the command.
X */
Xscanchar()
X{
X	register int	ch;			/* current character */
X
Xloop:	if (scanreadptr < scanwriteptr)		/* get saved char if have any */
X		return(*scanreadptr++);
X	ch = scanroutine() & 0x7f;		/* get new character */
X	if (ch == litchar) {			/* literal input */
X		ch = scanroutine() & 0x7f;
X		goto store;
X	}
X	if (ch == rubchar) {			/* character erase */
X		if (scanwriteptr <= scanbuffer) {
X			write(STDERR, "\007", 1);
X			goto loop;
X		}
X		scanwriteptr--;
X		scanreadptr = scanbuffer;
X		longjmp(scanjumpbuf, SCAN_EDIT);
X	}
X	if (ch == rubword) {			/* word erase */
X		if (scanwriteptr <= scanbuffer) goto loop;
X		while ((--scanwriteptr >= scanbuffer) &&
X			((*scanwriteptr == ' ') || (*scanwriteptr == '\t'))) ;
X		scanwriteptr++;
X		while ((--scanwriteptr >= scanbuffer) &&
X			((*scanwriteptr != ' ') && (*scanwriteptr != '\t'))) ;
X		scanwriteptr++;
X		scanreadptr = scanbuffer;
X		longjmp(scanjumpbuf, SCAN_EDIT);
X	}
X	if (ch == rubline) {			/* line erase */
X		if (scanwriteptr <= scanbuffer) goto loop;
X		scanwriteptr = scanbuffer;
X		scanreadptr = scanbuffer;
X		longjmp(scanjumpbuf, SCAN_EDIT);
X	}
X
Xstore:	if (scanwriteptr >= scanbuffer + SCAN_SIZE) {
X		write(STDERR, "\007", 1);
X		goto loop;
X	}
X	*scanwriteptr++ = ch;
X	return(*scanreadptr++);
X}
X
X
X/* Abort reading of the current command */
Xscanabort()
X{
X	scanreadptr = scanbuffer;
X	scanwriteptr = scanbuffer;
X	longjmp(scanjumpbuf, SCAN_ABORT);
X}
X
X
X/* Indicate no more characters ready yet */
Xscaneof()
X{
X	scanreadptr = scanbuffer;
X	longjmp(scanjumpbuf, SCAN_EOF);
X}
X
X
X/* Simply reset input and output pointers without longjmping */
Xscanreset()
X{
X	scanreadptr = scanbuffer;
X	scanwriteptr = scanbuffer;
X}
//E*O*F scan.c//
echo x - talk.c
sed -e 's/^X//' > "talk.c" << '//E*O*F talk.c//'
X#ifdef SCCS
Xstatic char *sccsid = "@(#)talk.c	1.1	4/5/85";
Xstatic char *cpyrid = "@(#)Copyright (C) 1985 by D Bell";
X#endif
X
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <sys/socket.h>
X#include <utmp.h>
X#include "war.h"
X
X#define	NAMELEN	(sizeof(ud.ut_name))		/* length of name field */
X#define	LINELEN	(sizeof(ud.ut_line))		/* length of line field */
X#define	DEVLEN	(sizeof(dev) - 1)		/* length of device string */
X#define	FULLEN	(LINELEN + DEVLEN + 1)		/* length of full tty name */
X#define	INFO	20				/* number of messages */
X
Xchar	utmp[] = "/etc/utmp";	/* logged in user file */
Xchar	dev[] = "/dev/";	/* prefix for tty names */
Xint	sd;			/* descriptor for socket */
Xint	serverflag;		/* nonzero if we are the server */
Xstruct	sockaddr addr;		/* name of our socket */
Xint	infocount;		/* number of queued messages */
Xstruct	info	info[INFO];	/* messages queued for sending */
X
X
X/*
X * Set the socket address used for connecting to the other player.
X * We find the other player by his user name (and his tty name if necessary).
X * The address is generated using the device numbers of our two terminals.
X * Exits if an error is detected.  If successful, saves the socket address
X * and remembers whether we are to be the server or the client.
X */
Xfindplayer(user, tty)
X	register char	*user;		/* other player's user name */
X	register char	*tty;		/* other player's tty name, or NULL */
X{
X	register FILE	*fd;		/* file pointer */
X	register char	*shortname;	/* short tty name */
X	register int	count;		/* times user is logged in */
X	int	sawme;			/* true if saw myself */
X	int	dev1;			/* device of first tty */
X	int	dev2;			/* device of second tty */
X	struct	stat	sb;		/* file status */
X	struct	utmp	ud;		/* utmp entry */
X	char	fullname[FULLEN];	/* full tty name */
X
X	if (fstat(STDERR, &sb)) {	/* we aren't on a tty */
X		perror("stderr");
X		exit(1);
X	}
X	if ((sb.st_mode & S_IFMT) != S_IFCHR) {
X		fprintf(stderr, "stderr: not a character device\n");
X		exit(1);
X	}
X	dev1 = sb.st_rdev;
X	fd = fopen(utmp, "r");
X	if (fd == NULL) {
X		perror(utmp);
X		exit(1);
X	}
X	strcpy(fullname, dev);
X	shortname = &fullname[DEVLEN];
X	shortname[LINELEN] = '\0';
X	count = 0;
X	sawme = 0;
X	while (fread(&ud, sizeof(ud), 1, fd)) {
X		if (ud.ut_name[0] == '\0') continue;
X		if (strncmp(user, ud.ut_name, NAMELEN)) continue;
X		strncpy(shortname, ud.ut_line, LINELEN);
X		if (stat(fullname, &sb) < 0) continue;
X		if ((sb.st_mode & S_IFMT) != S_IFCHR) continue;
X		if (sb.st_rdev == dev1) {	/* see if this is myself */
X			sawme = 1;
X			continue;
X		}
X		dev2 = sb.st_rdev;
X		count++;
X		if ((tty == NULL) || strcmp(tty, shortname)) continue;
X		count = 1;			/* found user on tty */
X		break;
X	}
X	fclose(fd);
X	if (count <= 0) {
X		if (sawme)
X			fprintf(stderr, "you are not logged in elsewhere\n");
X		else
X			fprintf(stderr, "%s is not logged in\n", user);
X		exit(1);
X	}
X	if (tty && strcmp(tty, shortname)) {
X		fprintf(stderr, "%s is not logged in on %s\n", user, tty);
X		exit(1);
X	}
X	if (count > 1) {
X		fprintf(stderr, "%s is logged in more than once\n", user);
X		exit(1);
X	}
X	if (dev1 > dev2) {		/* order the devices */
X		count = dev1;
X		dev1 = dev2;
X		dev2 = count;
X		serverflag = 1;
X	}
X	sprintf(addr.sa_data, "WAR.%d.%d", dev1, dev2);
X}
X
X
X
X/*
X * Initialize to talk to the other player.
X */
Xtalkinit()
X{
X	if (chdir(GAMEDIR)) {		/* go to right place */
X		perror(GAMEDIR);
X		exit(1);
X	}
X	dpymove(-1, 1);
X	dpystr("Waiting for other player...");
X	dpyhome();
X	dpyupdate();
X	if (serverflag) {		/* we are the server */
X		serverinit();
X		dpymove(-1, 1);
X		dpyclrwindow();
X		sendboard();
X	} else {			/* we are the client */
X		clientinit();
X		dpymove(-1, 1);
X		dpyclrwindow();
X		readinfo();
X		sendboard();
X	}
X	beep();
X	playing = 1;
X	newstat = 1;
X}
X
X
X/*
X * Here if we are the server process, to create the binding.
X * Wait for a connection from the client.
X */
Xserverinit()
X{
X	register int	s;		/* socket descriptor */
X	int	dummylen;		/* dummy length */
X	struct	sockaddr dummyaddr;	/* name of our socket */
X
X	s = socket(AF_UNIX, SOCK_STREAM, PF_UNSPEC);	/* create socket */
X	if (s < 0) {
X		perror("socket");
X		exit(1);
X	}
X	unlink(addr.sa_data);			/* remove binding name */
X	if (bind(s, &addr, sizeof(addr)) < 0) {		/* declare ourself */
X		perror("bind");
X		exit(1);
X	}
X	if (listen(s, 1) < 0) {			/* allow one connection */
X		perror("listen");
X		exit(1);
X	}
X	dummylen = sizeof(dummyaddr);
X	sd = accept(s, &dummyaddr, &dummylen);
X	if (sd < 0) {
X		perror("accept");
X		exit(1);
X	}
X	close(s);
X}
X
X
X/*
X * Here if we are the client process, to connect to the binded address.
X * We just continuously try to connect to the address.
X */
Xclientinit()
X{
X	sd = socket(AF_UNIX, SOCK_STREAM, PF_UNSPEC); /* create socket */
X	if (sd < 0) {
X		perror("socket");
X		exit(1);
X	}
X	while (connect(sd, &addr, sizeof(addr)) < 0) {
X		if ((errno != ECONNREFUSED) && (errno != ENOENT)) {
X			perror("connect");
X			exit(1);
X		}
X		sleep(1);
X	}
X}
X
X
X/*
X * Send the initial board layout
X */
Xsendboard()
X{
X	register struct	cell *cc;	/* current cell within row */
X	register struct	object *obj;	/* object at this location */
X
X	for (cc = homecell; cc; cc = cc->c_next) {
X		obj = cc->c_obj;
X		if ((obj == NULL) || (obj->o_side != myside)) continue;
X		sendinfo('p', cc->c_row, cc->c_col, obj->o_id);
X	}
X	sendinfo('r', 0, 0, 0);
X}
X
X
X/*
X * Send an information message to the other player.
X * Successive message are buffered up and sent together in one write.
X * All pending messages are flushed when an 'r' message type is used.
X */
Xsendinfo(type, row, col, id)
X	register int	type;		/* message type */
X	unsigned int	row;
X	unsigned int	col;
X{
X	register struct	info	*ip;	/* pointer to current info block */
X
X	if ((row >= ROWS) || (col >= COLS)) panic("badsend");
X	ip = &info[infocount++];
X	ip->i_type = type;
X	ip->i_row = row;
X	ip->i_col = col;
X	ip->i_id = id;
X	if ((type != 'r') && (infocount < INFO))
X		return;			/* wait till later */
X	type = (infocount * sizeof(struct info));
X	if (write(sd, info, type) != type) {
X		dpyclose();
X		fprintf(stderr, "other player quit\n");
X		exit(0);
X	}
X	infocount = 0;
X}
X
X
X/*
X * Read and process commands from the other player until we are released
X * with a ready command.  We transform incoming id's and row numbers.
X */
Xreadinfo()
X{
X	register int	row;		/* row of interest */
X	register int	col;		/* column of interest */
X	register int	id;		/* id of object */
X	register struct	cell *cc;	/* current cell */
X	register struct	object *obj;	/* current object */
X	struct	info	info;		/* command being read */
X
X	while (1) {
X		id = read(sd, &info, sizeof(info));
X		if (id <= 0) {
X			dpyclose();
X			fprintf(stderr, "other player quit\n");
X			exit(0);
X		}
X		if (id != sizeof(info)) panic("short read from socket");
X		id = info.i_id ^ 1;		/* toggle id */
X		row = (ROWS - 1) - info.i_row;	/* and row number */
X		col = info.i_col;
X		if ((row >= ROWS) || (col >= COLS)) panic("bad coordinates");
X		cc = &board[row][col];
X		obj = cc->c_obj;
X
X		switch (info.i_type) {
X
X		case 'b':		/* wall got blasted */
X			if ((obj == NULL) || ((obj->o_flags & F_WALL) == 0))
X				panic("blasting non-wall");
X			removeobject(row, col);
X			break;
X
X		case 'h':		/* man got hit */
X			if ((obj == NULL) || (obj->o_side != myside))
X				panic("missed me");
X			hitobject(cc->c_row, cc->c_col);
X			break;
X
X		case 'p':		/* object got placed or moved */
X			if (obj) panic("nested objects");
X			obj = findid(id);
X			if (obj->o_side == myside) panic("read my own object"); 
X			placeobject(obj, row, col);
X			break;
X
X		case 'r':		/* ready for other player */
X			return;
X
X		default:
X			panic("bad command read from opponent\n");
X		}
X	}
X}
//E*O*F talk.c//
echo x - view.c
sed -e 's/^X//' > "view.c" << '//E*O*F view.c//'
X#ifdef SCCS
Xstatic char *sccsid = "@(#)view.c	1.1	4/5/85";
Xstatic char *cpyrid = "@(#)Copyright (C) 1985 by D Bell";
X#endif
X
X#include "war.h"
X
X/*
X * See what is currently visible by all of our men.
X */
Xview()
X{
X	register struct	cell	*cc;	/* current cell */
X	register struct	cell	*tc;	/* test cell */
X	register struct	object	*obj;	/* object doing viewing */
X	register int	row;		/* current row */
X	register int	col;		/* current column */
X	register int	view;		/* viewing distance */
X	int	objrow;			/* current row of object */
X	int	objcol;			/* current column of object */
X	int	minrow;			/* minimum row for search */
X	int	maxrow;			/* maximum row for search */
X	int	mincol;			/* minimum column for search */
X	int	maxcol;			/* maximum column for search */
X
X	/*
X	 * Remove visibility of movable objects
X	 */
X	for (obj = objects; obj < endobjects; obj++) {
X		if (obj->o_side == myside) continue;
X		if (obj->o_flags & (F_IMMOB|F_VIS)) continue;
X		cc = obj->o_cell;
X		if ((cc == NULL) || (cc->c_seen == 0)) continue;
X		cc->c_seen = 0;
X		dpyplace(cc->c_row, cc->c_col, ' ');
X	}
X	/*
X	 * See what is visible now
X	 */
X	for (cc = firstcell; cc; cc = cc->c_next) {
X		obj = cc->c_obj;
X		if ((obj == NULL) || (obj->o_side != myside)) continue;
X		view = obj->o_view;
X		if (view <= 0) continue;
X		objrow = cc->c_row;
X		objcol = cc->c_col;
X		minrow = objrow - view;
X		maxrow = objrow + view;
X		mincol = objcol - view;
X		maxcol = objcol + view;
X		if (minrow < 0) minrow = 0;
X		if (maxrow >= ROWS) maxrow = ROWS - 1;
X		if (mincol < 0) mincol = 0;
X		if (maxcol >= COLS) maxcol = COLS - 1;
X		for (row = minrow; row <= maxrow; row++) {
X			col = mincol;
X			tc = &board[row][col];
X			for (; col <= maxcol; col++, tc = tc->c_right) {
X				if (tc->c_obj == NULL) continue;
X				if (tc->c_seen) continue;
X				if (tc->c_obj->o_side == myside) continue;
X				if (((obj->o_flags & F_XRAY) == 0)
X					&& obstruct(objcol, objrow, col, row))
X						continue;
X				tc->c_seen = 1;
X				dpyplace(row, col, tc->c_obj->o_altch);
X			}
X		}
X	}
X	viewstats();
X	dpyupdate();
X}
X
X
X/*
X * Given a rectangle specified by its opposite corners (a,b) and (c,d).
X * Check whether there exists any object in the rectangle which is within
X * a distance of 1/2 from the line segment connecting the two corners (the
X * corners themselves are not tested).  The equation for those points (x,y)
X * which satisfy this distance requirement is:
X * 
X *	4 * square((b-d)x + (c-a)y + (ad-bc)) <= square(b-d) + square(c-a).
X *
X * By precomputing the constant parts of this equation, it becomes:
X *
X *	4 * square(Ax + By + C) <= D.
X *
X * Returns nonzero if some object is within this distance.
X */
Xobstruct(a, b, c, d)
X{
X	register int	x;		/* x coordinate of test point */
X	register int	y;		/* y coordinate of test point */
X	register int	i;		/* number being computed */
X	register struct	cell	*cc;	/* current cell being checked */
X	struct	cell	*endc1;		/* cell at first endpoint */
X	struct	cell	*endc2;		/* cell at last endpoint */
X	int	A;			/* (b - d) */
X	int	B;			/* (c - a) */
X	int	C;			/* (ad - bc) */
X	int	D;			/* (square(b-d) + square(c-a)) */
X	int	dy;			/* delta for y coordinate */
X
X	if (a > c) {			/* put first point at left */
X		i = c;
X		c = a;
X		a = i;
X	}
X	dy = 1;				/* see if line rises or falls */
X	if (d < b) dy = -1;
X	A = b - d;			/* compute constants */
X	B = c - a;
X	C = (a * d) - (b * c);
X	D = (A * A) + (B * B);
X	endc1 = &board[b][a];		/* get cells at endpoints */
X	endc2 = &board[d][c];
X	for (y = b; ; y += dy) {
X		for (x = a, cc = &board[y][x]; x <= c; x++, cc = cc->c_right) {
X			if (cc == endc2) return(0);
X			if ((cc->c_obj == NULL) || (cc == endc1)) continue;
X			if (cc->c_obj->o_flags & F_TRANS) continue;
X			i = (A * x) + (B * y) + C;
X			if ((i * i * 4) <= D) return(1);
X		}
X	}
X}
X
X
X/*
X * Update the status information if necessary
X */
Xviewstats()
X{
X
X	if (newstat == 0) return;
X	viewobjects();
X	viewdata();
X	dpywindow(0, -1, 1, INFOCOL - 2);
X	newstat = 0;
X}
X
X
X/*
X * Give comparisons between our men and the enemy
X */
Xviewdata()
X{
X	register struct	data	*md;	/* pointer to my data */
X	register struct	data	*hd;	/* pointer to his data */
X
X	dpywindow(DATAROW, -1, INFOCOL, -1);
X	md = &mydata;
X	hd = &hisdata;
X	dpyprintf("STATISTIC       YOU   ENEMY\n");
X	dpyprintf("fight men/life %2d/%-3d %2d/%d\n",
X		md->d_fightmen, md->d_fightlife,
X		hd->d_fightmen, hd->d_fightlife);
X	dpyprintf("move men/life  %2d/%-3d %2d/%d\n",
X		md->d_movemen, md->d_movelife,
X		hd->d_movemen, hd->d_movelife);
X	dpyprintf("blast men/life %2d/%-3d %2d/%d\n",
X		md->d_blastmen, md->d_blastlife,
X		hd->d_blastmen, hd->d_blastlife);
X	dpyprintf("goal men/life  %2d/%-3d %2d/%d\n",
X		md->d_goalmen, md->d_goallife,
X		hd->d_goalmen, hd->d_goallife);
X	dpyprintf("total men/life %2d/%-3d %2d/%d\n",
X		md->d_totalmen, md->d_totallife,
X		hd->d_totalmen, hd->d_totallife);
X	dpyprintf("total walls      %-3d    %-3d\n",
X		md->d_totalwalls, hd->d_totalwalls);
X	dpyclrwindow();
X}
X
X
X/*
X * Give statistics on our objects
X */
Xviewobjects()
X{
X	register struct	object	*obj;	/* current object */
X	register char	*cp;		/* current flag character */
X	register int	flags;		/* current flags */
X	char	flagchars[20];		/* flags for object */
X	char	count[20];		/* value for counts */
X
X	dpywindow(0, DATAROW - 2, INFOCOL, -1);
X	dpystr("OBJ LIFE VIEW FLAGS  COUNT\n");
X	for (obj = objects; obj < endobjects; obj++) {
X		if (obj->o_side != myside) continue;
X		if (playing && (obj->o_count == 0)) continue;
X		cp = flagchars;
X		flags = obj->o_flags;
X		if (flags & F_BLAST) *cp++ = 'b';
X		if (flags & F_FIGHT) *cp++ = 'f';
X		if (flags & F_GOAL) *cp++ = 'g';
X		if (flags & F_IMMOB) *cp++ = 'i';
X		if (flags & F_TRANS) *cp++ = 't';
X		if (flags & F_VIS) *cp++ = 'v';
X		if (flags & F_WALL) *cp++ = 'w';
X		if (flags & F_XRAY) *cp++ = 'x';
X		*cp = '\0';
X		cp = count;
X		if (playing)
X			sprintf(cp, "%d  ", obj->o_count);
X		else if (obj->o_min == obj->o_max)
X			sprintf(cp, "%d  ", obj->o_min);
X		else if (obj->o_max >= 1000)
X			sprintf(cp, "%d-INF", obj->o_min);
X		else
X			sprintf(cp, "%d-%d", obj->o_min, obj->o_max);
X		dpyprintf("%c %c %3d %4d   %-7s%s\n",
X			obj->o_ownch, obj->o_altch, obj->o_life,
X			obj->o_view, flagchars, count);
X	}
X	dpyclrwindow();
X}
//E*O*F view.c//
echo done