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, <buf);
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