home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1994 March / Source_Code_CD-ROM_Walnut_Creek_March_1994.iso / compsrcs / games / volume15 / bt4 / part02 < prev    next >
Encoding:
Internet Message Format  |  1993-01-26  |  55.2 KB

  1. Path: uunet!charon.amdahl.com!pacbell.com!network.ucsd.edu!ogicse!zephyr.ens.tek.com!master!saab!billr
  2. From: billr@saab.CNA.TEK.COM (Bill Randle)
  3. Newsgroups: comp.sources.games
  4. Subject: v15i016:  bt4 - Broken Throne, multiplayer realtime conquest game (V2.03), Part02/03
  5. Message-ID: <3827@master.CNA.TEK.COM>
  6. Date: 29 Oct 92 01:42:25 GMT
  7. Article-I.D.: master.3827
  8. Sender: news@master.CNA.TEK.COM
  9. Lines: 1711
  10. Approved: billr@saab.CNA.TEK.COM
  11. Xref: uunet comp.sources.games:1515
  12.  
  13. Submitted-by: boutell@isis.cshl.org (Tom Boutell)
  14. Posting-number: Volume 15, Issue 16
  15. Archive-name: bt4/Part02
  16. Supersedes: bt3: Volume 12, Issue 52-53
  17. Environment: INET sockets, curses
  18.  
  19.  
  20.  
  21. #! /bin/sh
  22. # This is a shell archive.  Remove anything before this line, then unpack
  23. # it by saving it into a file and typing "sh file".  To overwrite existing
  24. # files, type "sh file -c".  You can also feed this as standard input via
  25. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  26. # will see the following message at the end:
  27. #        "End of archive 2 (of 3)."
  28. # Contents:  bt.doc btclient.c btclient.h btserver.h btserverrc changes
  29. # Wrapped by billr@saab on Wed Oct 28 17:38:00 1992
  30. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  31. if test -f 'bt.doc' -a "${1}" != "-c" ; then 
  32.   echo shar: Will not clobber existing file \"'bt.doc'\"
  33. else
  34. echo shar: Extracting \"'bt.doc'\" \(22442 characters\)
  35. sed "s/^X//" >'bt.doc' <<'END_OF_FILE'
  36. XThe Broken Throne
  37. X
  38. XA multiplayer, realtime game of conquest for the Unix operating system
  39. X
  40. XBy Tom Boutell (boutell@cshl.org), 11/14/90
  41. XLatest Revision (2.03) 10/20/92
  42. X
  43. XPermission granted to freely copy and alter the code, provided that new
  44. Xversions are cleared through me before release. I will be acting as a
  45. Xsource of information, and would appreciate copies of all changes.
  46. X
  47. XFIRST
  48. X
  49. XSee the readme file for instructions on building the game! This file (bt.doc)
  50. Xcovers the rules of the game, not installation. The readme file also
  51. Xcovers new features of each version and tips on getting it to compile
  52. Xif you don't seem to be having any luck.
  53. X
  54. XIf you have played older versions of the game, READ THIS AGAIN ANYWAY,
  55. Xbecause some major new rules have been added, most importantly
  56. Xautomatic recruitment and support. Also, the syntax for starting up the
  57. Xprograms has changed.
  58. X
  59. XTHE STORY
  60. X
  61. XEmperor Lettuce Prei of Squaria has died, leaving no heir. The nobles
  62. Xof the land scramble to reunite the pieces of the Broken Throne; do it
  63. Xfirst and become the next Emperor!
  64. X
  65. XABOUT THE GAME
  66. X
  67. XBroken Throne is a strategic wargame which runs in "real time." Every
  68. Xfew seconds, "action points" are transmitted to the players, which allow
  69. Xthem to do things- recruiting troops, moving troops, attacking, and
  70. Xconstructing and destroying cities. The object is to seize control of
  71. Xall of your opponents' cities; when a player controls no cities, that
  72. Xplayer is out of the game. Gameplay is quite rapid, and the map is
  73. Xcontinuously updated on the screen. All that is required to play is
  74. Xan ordinary terminal with curses screen control; any terminal that can
  75. Xrun a visual editor (like vi or emacs) will do fine. If the terminal
  76. Xis especially large, the game will take advantage of this, displaying
  77. Xmore space between locations and adding extra lines to the output window.
  78. XThis is handy if you are running Xwindows.
  79. X
  80. XThe map of Broken Throne is, by default, 16x16, consisting of square 
  81. X"hexes." Each hex may contain one of several types of terrain:
  82. X
  83. X.      Plains
  84. XT      Town         
  85. XM      Mountains (passable) 
  86. XF      Forest
  87. XC      City
  88. XS      Swamp
  89. X^      Crags (impassable mountains)
  90. X
  91. XOn the screen, a hex is displayed like this:
  92. X
  93. XaC05
  94. X
  95. XWhere the lowercase letter is the letter of the player who controls the
  96. Xhex, the capital letter (or . or ^ ) is the terrain type, and the number
  97. Xfollowing is the number of troops. Unowned territories have no lowercase
  98. Xletter, and territories without troops have no following number.
  99. X
  100. XSTARTING OUT
  101. X
  102. XTo launch the game, a server program must be started. This is
  103. Xdone by entering:
  104. X
  105. Xbtserver -n (# of players) -p &
  106. X# of players should be replaced by the actual number of combatants 
  107. Xand the & sign puts the server in the background.
  108. X
  109. X(Note that the use of standard - style options is a change from the
  110. Xprevious version of the game.)
  111. X
  112. XPutting the server in the background is important because it allows the
  113. Xplayer who runs the server to play in the game as well, even from
  114. Xan ASCII terminal.
  115. X
  116. XThere are other options for the server, and even more can be
  117. Xcontrolled in the .btserverrc file; more on that subject later.
  118. X
  119. XThen, the individual players each need to launch the client program. 
  120. XThis is done by entering:
  121. X
  122. Xbtclient 
  123. X
  124. XBy itself if the server is running on the SAME MACHINE, or:
  125. X
  126. Xbtclient -h machinename
  127. X
  128. XIf the server is running on another computer (a different workstation,
  129. Xfor instance, as opposed to just another text terminal).
  130. X
  131. XFor instance, in our local network, bt resides in a subdirectory of my
  132. Xown directory, so a player types:
  133. X
  134. Xcd ~boutell/bt    <-- Of course this should be replaced by the path of your
  135. X                      *own* Broken Throne directory. 
  136. Xbtserver -n 2 &   ... To launch the game for two players, then each
  137. X                  player moves to the bt directory and types:
  138. Xbtclient -h toffee   <-- Where "toffee" is the name of the machine the game
  139. X                  is running on. If the players and the server are on the
  140. X                                    same machine, specifying the host is optional.
  141. X                                    ONLY SERVER SHOULD BE STARTED!
  142. X
  143. XThe player who is running the server should start up his client last, in
  144. Xorder to keep his screen from being "munged" as the server announces
  145. Xplayers. This will generally keep the screen clean.
  146. X
  147. XALTERNATIVELY: if you know of a Broken Throne server that is already
  148. Xrunning and runs in "continuous mode", such as my own server, you
  149. Xcan contact it by just running the client:
  150. X
  151. Xbtclient -h hostname -p portnumber
  152. X
  153. XWhere hostname is the address of the server machine and port number is
  154. Xthe port advertised as correct to connect to it, anywhere on the Internet. 
  155. XA server running in "continuous mode" will put you into a wait queue if there 
  156. Xis currently a game in progress, and tell you how many players are needed 
  157. Xper game, to give you a rough idea of the wait. When a player is eliminated, 
  158. Xor the current game ends, you'll be updated. When you reach the front of the 
  159. Xqueue at the start of a new game, you'll be one of the players. 
  160. XYou can press CONTROL-C to quit waiting while in the queue; keep an eye 
  161. Xpeeled for messages from the server recommending other machines or port
  162. Xnumbers for other, less crowded games.
  163. X
  164. XFor instance, to connect to my server: 
  165. X
  166. Xbtclient -h athena.cshl.org -p 2727
  167. X
  168. XOTHER PORT NUMBERS
  169. X
  170. XIf you do not want to use the default port (2727), or if there is another
  171. XBT game in progress on the same machine (in which case you will notice
  172. Xproblems getting your game together), you will need to specify new port
  173. Xnumber. Port numbers should be between 1000 and 9999, the same on both
  174. Xclient and server. Enter the port number using the -p option on the
  175. Xcommand line for both client and server.
  176. X
  177. XROBOT PLAYERS
  178. X
  179. XIf you can't find opponents or want an added challenge, use the robot
  180. Xplayer! It's launched just like the client, but plays by itself, so
  181. Xput it in background so you can launch your own client and take it on.
  182. XFor example:
  183. X
  184. Xbtrobot -h toffee &
  185. X
  186. XIt also accepts port numbers if needed. You can launch as many robots
  187. Xas you wish; INCLUDE THEM IN THE PLAYER COUNT when launching the
  188. Xserver. The server can't tell them from human players. 
  189. X
  190. XIMPORTANT NOTE ON USING ROBOTS: If all human players leave a game in which
  191. Xrobots are still playing, they'll keep going, and they never quit, even
  192. Xif one of them wins! So the person who launched them should type "jobs" to 
  193. Xlist the programs running, and kill each robot: "kill %n" where n was the 
  194. Xlisted job number of the robot. When all the robots are dead, the server will 
  195. Xsay "Ending," letting you know that the game is completely over (unless
  196. Xit is running in continuous mode, in which case it will start a new game
  197. Xor wait for player connections).
  198. X
  199. XThe source is in the file btrobot.c. Have fun modifying it; it plays well
  200. Xearly on but has a rotten endgame, or rather, *no* real endgame. It's
  201. Xintended to be a jumping-off point for other programmers.
  202. X
  203. XONCE THE GAME STARTS:
  204. X
  205. XOnce the client is launched, a blank map will be displayed. Within a moment
  206. Xor two it should be replaced by the full map; be patient if there are
  207. Xseveral players.
  208. X
  209. XWhen the game first begins, the player is told which lowercase letter
  210. Xrepresents them on the map. Initially the player controls one city and
  211. Xwill immediately see a number of troops recruited. A cursor will appear 
  212. Xat the city's location. This cursor can be moved with the standard vi/ 
  213. Xmoria/ hack movement keys:
  214. X
  215. X          k
  216. X          |
  217. X        h-*-l
  218. X          |
  219. X          j
  220. X
  221. XIt is now possible to fan out and claim control of towns. The m (move)
  222. Xcommand is used to do this. The player should press m, then move the cursor
  223. Xto the source of the troops, initially the first city, and press the space
  224. Xbar. Then the cursor should be moved to the destination of the troops,
  225. Xwhich can be no more than one hex away; diagonal moves are acceptable.
  226. XAfter the space bar is pressed again, you will be asked how many troops
  227. Xto move; typically you'll want to move 1 army at a time at the beginning,
  228. Xsince you have a limited number of action points (see below). Type
  229. Xin 1 and press RETURN. The troops will be moved. Continue to move in this
  230. Xmanner to occupy territory, giving particular emphasis to taking
  231. Xtowns. (Other territories, particularly forests (F), mountains (M)
  232. Xand plains (.), are valuable to hold also.)
  233. X
  234. XRecruitment of new armies takes place AUTOMATICALLY, every so often in 
  235. Xany given town or city. (This is a major change from the previous
  236. Xversion of the game.) Note that when a city or town is first taken over,
  237. Xneutral whether from another player, it is now necessary to wait the
  238. Xfull recruitment period before new troops can be recruited there.
  239. X
  240. XThe amount of time required for recruitment can be changed in the
  241. X.btserverrc file, to be described in detail later.
  242. X
  243. XYou can also recruit more aggressively by using the 'R' key and then
  244. Xselecting a city or town; keep in mind that the odds of success depend
  245. Xon how long it's been since the last recruitment.
  246. X
  247. XThe number of troops recruited is a function of population, not including
  248. Xother towns' population, so towns and cities surrounded by farmland will
  249. Xtend to recruit the most troops, towns surrounded by swamp very few.
  250. XNote that you must *OWN* surrounding territories to gain the benefit
  251. Xof their population. (This is also a change from the previous version.)
  252. XYou own a territory if your lowercase identifying letter appears at
  253. Xits left. Having an army continuously present is not necessary.
  254. XNeutral territories belong to and benefit no one, so take
  255. Xvaluable surrounding territory early. 
  256. X
  257. XAction points are required to move troops! The player will soon find
  258. Xthemselves running low on them while exploring the area. This is because
  259. Xmovement in neutral or enemy territory costs 4 action points per army moved.
  260. XMovement in the player's own territory costs only 2 action points per
  261. Xarmy moved. When each tick takes place, the player will receive 2 action
  262. Xpoints, PLUS 1 action point for each city under the player's control.
  263. XThis becomes important later in the game.
  264. X
  265. XAction points are displayed for each player at the bottom of the screen:
  266. X
  267. XA: 6,13
  268. X
  269. XThis denotes that player A has six hexes (territories) and thirteen
  270. Xaction points.
  271. X
  272. XThe cursor can be moved freely while considering what command to enter
  273. Xnext. The only time you cannot move the cursor is during the entry of a
  274. Xnumber of troops, or a text message to another player.
  275. X
  276. XCOMBAT
  277. X
  278. XCombat in the Broken Throne is simple; the player simply moves troops into
  279. Xenemy territory, and the server resolves the conflict. Each round of battle,
  280. Xthere is a 50% chance the attacker will lose an army, and a 50% chance
  281. Xthe defender will; this continues until one side has no more troops.
  282. XUsually, however, the odds of combat are modified by the terrain of the
  283. Xtwo hexes, as detailed below. If the attacker seizes an opponent's last
  284. Xcity, that opponent is out of the game!
  285. X
  286. XArmies are broken down into units in battle, three units representing
  287. Xeach army. This makes the odds of battle somewhat more realistic while
  288. Xstill allowing for occasional freak outcomes.
  289. X
  290. XSUPPORT
  291. X
  292. XAfter automatic recruitment, support is the second most major change
  293. Xin version 2.0 of the game. To avoid starvation, ALL ARMIES MUST
  294. XHAVE SUPPORT. Support is determined by the rule: a CONTIGUOUS
  295. Xgroup of territories under a player's control can support ONE ARMY
  296. XFOR EVERY 100 POPULATION. (Plains have a population of 100, towns
  297. Xmore, other territories typically less. See TERRAIN, below.) For
  298. Xthis reason it is important to maintain a continuous empire! 
  299. XDiagonal connections DO count, however, and you can restore support
  300. Xif it is cut by reestablishing a continuous region of territory
  301. X(retaking what was cut out of the middle). If a region can't
  302. Xsupport the forces within it, it will lose an army to starvation
  303. Xeach tick until the number of troops left can be supported or
  304. Xsupport is reestablished. Note that an army by itself in a
  305. Xforest, mountain or swamp will starve if it is not connected to
  306. Xa larger region! Remember, you DON'T have to have an army in
  307. Xeach territory to own it and maintain support; you just have to
  308. Xhave your identifying letter in each territory. In other words,
  309. Xthe last player to have marched through a territory owns that
  310. Xterritory.
  311. X
  312. XTERRAIN
  313. X
  314. XThe different terrain types have different roles in the game. Plains
  315. Xhexes (' . ') have no effect on combat, but provide substantial population.
  316. XThe number of troops a town or city can recruit depends on the surrounding
  317. Xpopulation controlled by the town or city's owner. Plains hexes have
  318. Xa population of 100.
  319. X
  320. XTown hexes (' T ') are the basic source of troops in the game, since
  321. Xrecruitment can only take place here and in cities. They have a strong
  322. Xdefensive advantage, 10%, and also a 10% offensive advantage.
  323. XTown hexes have a population of 300.
  324. X
  325. XMountain hexes (' M ') are difficult to move through. When moving troops
  326. Xinto them, some troops will usually be balked by conditions in the passes,
  327. Xand the action points will still be spent. However, a player in the mountains
  328. Xenjoys a 10% advantage, both defending against attack and when striking
  329. Xdown out of the mountains. Mountain hexes have a population of 50.
  330. X
  331. XForest hexes (' F ') hide enemy troops from view. Another player can only tell
  332. Xwho the forest is controlled by; the exact numbers of troops are hidden.
  333. XForces in a forest enjoy a 5% advantage when attacking out of the forest.
  334. XForces in the forest have a 10% defensive advantage as well. Forest hexes
  335. Xhave a population of 75.
  336. X
  337. XCity hexes (' C ') are the strongest hexes in the game, with a 15% defensive
  338. Xadvantage, though only a 5% offensive advantage. The player can recruit
  339. Xtroops at cities, and cities also provide an extra action point per tick;
  340. Xthe more cities the player controls, the more resources they have, and
  341. Xthe more quickly they can move armies. Only towns can be built into 
  342. Xcities, at a cost of typically 90 action points! This value can be set
  343. Xin the .btserverrc file. If a player loses control of all cities, that player 
  344. Xis out of the game, and their existing troops disband, leaving their territory 
  345. Xfree for the taking! Although building cities is expensive, the action points 
  346. Xthey provide can quickly make up for the cost. It is very inexpensive to 
  347. Xdestroy a city, however, so they should be built in safe areas. City
  348. Xhexes have a population of 400.
  349. X
  350. XSwamp hexes (' S ') are the armpit of Broken Throne! Troops are very
  351. Xunhappy here; the swamp will often gobble one army of the advancing
  352. Xforce. Swamps have no population, and unless in the player's way (or
  353. Xneeded to maintain support) aren't worth slogging through. Troops in a
  354. Xswamp are at a 10% DISadvantage, both attacking and defending! Swamps
  355. Xhave a population of only 10.
  356. X
  357. XCrag hexes (' ^ ') are impassable mountains. It is not possible to
  358. Xenter these hexes, and so they do not affect combat. They have
  359. Xno population.
  360. X
  361. XCOMMAND REFERENCE (INCLUDING SOME COMMANDS NOT DESCRIBED ABOVE!)
  362. X
  363. Xc (location): construct a city. This requires, usually, 90 action points,
  364. X  unless the value has been changed in the .btserverrc file.
  365. X  Can only be used in towns.
  366. Xd (location): destroy a city OR TOWN. Usually costs 10 action points.
  367. Xq: quit the game. You must confirm this.
  368. Xt (player letter) (message): Tell a player something. Type their player
  369. X  letter (case is not important) after striking t, then your message to
  370. X  them (one line). The message will appear in their output window. 
  371. Xt # (message): If you type a '#' sign instead of a letter, the message
  372. X  will go to all players, including yourself.
  373. Xm (location) (location) (# of troops): move troops. The game will prompt
  374. X  for the locations from which and to which troops are to be moved, then
  375. X  for the number of troops.
  376. Xr (location): recruit troops. See notes above on the rules of recruitment.
  377. X    Remember, recruitment is automatically done as often as it is safe,
  378. X    but the r key can be used to force the pace.
  379. XM (capitalized): redraw a Messy screen. This is useful after a talk
  380. X  request (or line noise) messes up your display.
  381. XESC (Escape key): Cancels *whatever* you are in the process of doing.
  382. X  Very useful for retaining your sanity.
  383. X
  384. XWINNING & LOSING
  385. X
  386. XYou win the game when no other players remain. You lose the game if you
  387. Xcontrol no cities. If your last city is taken (towns don't count), it
  388. Xdoesn't matter how many troops you still have, so be careful to defend
  389. Xthem! When a player is eliminated, any remaining troops are dissolved
  390. Xand territories are free for the taking.
  391. X
  392. XIf you have lost the game, you can continue to watch the game for as long
  393. Xas you like, before striking 'q' to actually leave the program. 
  394. X
  395. XIf you win, the game will not automatically halt itself. You need to type
  396. X'q' to quit the game as the last player. This allows the game to be launched
  397. Xby one person on one terminal to test it out or learn the rules.
  398. X(Continuous-mode servers may throw all players off the server when
  399. Xone player is left alive.)
  400. X
  401. XThe player who launched the server should see the message "Ending" after
  402. Xthe last player quits. If not, that player should do a ps -aux command
  403. Xand look for a lingering copy of the server, then kill that copy of the
  404. Xserver. (I have not seen such problems recently, but it's possible.
  405. XA good command to find a lost server: ps -aux | grep btserver will
  406. Xusually locate the process number.)
  407. X
  408. XIF YOU ABORT THE PROGRAM
  409. X
  410. XIf you control-c a client, the other players will continue, but you will be
  411. Xout of the game.  This no longer confuses your terminal as in previous
  412. Xversions; wait a second or two, hit return a few times and you'll
  413. Xhave a normal Unix prompt back.
  414. X
  415. XMORE SERVER OPTIONS:
  416. XMAP SIZE
  417. X
  418. XYou can start up a game with a different map size, as long as your
  419. Xclient terminals are large enough to support it, using the -s
  420. X(size) option to the server. For instance:
  421. X
  422. Xbtserver -n 2 -s 12x12 &
  423. X
  424. XStarts a two - player, 12x12 - map game and puts it into the 
  425. Xbackground. 
  426. X
  427. XCUSTOMIZED MAPS
  428. X
  429. XIf you get tired of playing on random maps, try designing your own map!
  430. XAll you have to do is create a file arranged like this:
  431. X
  432. X(w should be replaced by the width of the map, here 16, and h by the 
  433. Xheight of the map, also 16 in this example. You do NOT need to
  434. Xspecify the -s option to use a map file with a nonstandard size, because
  435. Xthe size is in the map file. This is a change from the previous version,
  436. Xso old map files will need to have a size inserted at the beginning.
  437. XNote the x between the width and height.)
  438. X
  439. Xwxh
  440. XMMMMFMMFMFMMMMTT
  441. XMMFF....TMMMFFMM 
  442. XMMMMMMMM.6..MTMM ^
  443. XMMMM..T..MMMMMMM ^
  444. XMMFFFM..T..TMMMM 
  445. XMMMMMMMTMMMMMMTM h
  446. XMMM..1....5...MM
  447. XMMMMMMTMMMMMMMMM d
  448. XMMMMMM...MTMMMMM o
  449. XMMMTMMMMT4MMM3TM w
  450. XMM.....MMMMMMMMM n
  451. XMMMMMMTMTMTMMFFM
  452. XMT..T..MMMMMMMMM v
  453. XMMMMMMMMFFFMTMMM v
  454. XMMMTMMMMTMM.2.MM
  455. XMMMMMMMMMMMMMMMM
  456. X << w across>>
  457. X
  458. X(Don't put in the text "w across" and "h down," of course.)
  459. X
  460. XThe same terrain letters you see during the game should be used here in
  461. Xthe map file. (You may use spaces between symbols if you wish for
  462. Xmore readability.)  In addition, in the locations at which players should 
  463. Xenter the game, place the numbers one through the highest number of
  464. Xplayers you think should play on the map. You should designate a 
  465. Xlocation for all possible players. If you play with fewer players, the 
  466. Xunused starting locations will become farmland ('.'); otherwise, they become 
  467. Xcities, and the designated players enter the game there. 
  468. X
  469. XTo load the map, give its filename last on the command line when starting
  470. Xthe server:
  471. X
  472. Xbtserver -n 4 -m map.txt &
  473. X
  474. XStarts a four- player game, using the file map.txt.
  475. X
  476. Xbtserver -n 4 -p 2000 -m map.txt &
  477. X
  478. XStarts a four- player game on port 2000, using the file map.txt.
  479. X
  480. XA sample map, map.txt, is provided to help you get the hang of the map
  481. Xfile system; you can edit it to produce a new map if you wish.
  482. X
  483. XIF YOU'VE DESIGNED GOOD MAPS...
  484. X
  485. XMail them to me at boutell@cshl.org! I may include a selection
  486. Xof good ones in a future release, and/or run them on my server.
  487. X
  488. XABOUT THE .BTSERVERRC FILE
  489. X
  490. XTo customize your server, or to set up a continuously - running
  491. Xserver that maintains a wait queue and doesn't need to be restarted
  492. Xto begin a new game, you'll need a .btserverrc file. One is provided
  493. Xin the standard release. btserver, when run, looks first in the
  494. Xcurrent directory, then in your home directory, for a .btserverrc
  495. Xfile; if it doesn't find one, it uses a reasonable set of
  496. Xbuilt-in defaults. You can specify an alternate filename
  497. Xusing the -d option.
  498. X
  499. XLook at the provided .btserverrc file for complete comments on 
  500. Xwhat settings are available and what each does. The "continuous" setting 
  501. Xis of particular interest. One can also control the default number
  502. Xof players, the default size of the map, the number of ticks between
  503. Xrecruitments, the number of seconds per tick, and so on.
  504. X
  505. XRUNNING ON LARGE TERMINALS& UNDER X-WINDOWS
  506. X
  507. XUnder X- Windows and on other systems providing large terminals the game
  508. Xwill take advantage of the terminal's size to provide more spacing and
  509. Xmore room for output messages. The wider and deeper, the better. The game
  510. Xwill *not* take notice if you resize the window while it is running, however.
  511. XYou probably don't want a window wider than about 120 characters, though,
  512. Xsince it becomes difficult to see which locations are next to each other,
  513. Xunless you're using an expanded map via the -s option.
  514. X
  515. XA file titled xbt is provided in the distribution. If you chmod this
  516. Xfile to make it executable, you can use it in place of btclient when
  517. Xrunning Xwindows, in order to automatically launch btclient in a
  518. Xnew window using a larger font. (it's just a shell script that
  519. Xstarts up btclient. You don't need to use it to play under X, btclient
  520. Xalone works fine.) I find this file convenient, but it doesn't make BT a true 
  521. XX program by any stretch of the imagination.
  522. XAn example of its use:
  523. X
  524. Xxbt -h maplenut
  525. X
  526. X... To connect to a game on the machine "maplenut," exactly as one would
  527. Xby entering "btclient -h maplenut."
  528. X
  529. XSUNVIEW
  530. X
  531. XPerformance under Sunview is a mixed thing, apparently. On our somewhat
  532. Xolder Sunview, there are problems with Curses that prevent it from running
  533. Xproperly, but under Sunview 3.0 I'm told it works fine.
  534. X
  535. XFINDING OPPONENTS
  536. X
  537. XThe easiest way to find a game is to connect to my server:
  538. X
  539. Xbtclient -h athena.cshl.org
  540. X
  541. XYou can also send me email at boutell@cshl.org, which is also the
  542. Xcorrect address for bug reports. I'm not the best player, incidentally;
  543. Xthat honor belongs to a friend of mine at the University of Delaware.
  544. XHE'S the one you should worry about.
  545. X
  546. END_OF_FILE
  547. if test 22442 -ne `wc -c <'bt.doc'`; then
  548.     echo shar: \"'bt.doc'\" unpacked with wrong size!
  549. fi
  550. # end of 'bt.doc'
  551. fi
  552. if test -f 'btclient.c' -a "${1}" != "-c" ; then 
  553.   echo shar: Will not clobber existing file \"'btclient.c'\"
  554. else
  555. echo shar: Extracting \"'btclient.c'\" \(20630 characters\)
  556. sed "s/^X//" >'btclient.c' <<'END_OF_FILE'
  557. X/*
  558. X * Broken Throne client program, by Tom Boutell 10/90- 12/90 Thanks to Marc
  559. X * Cygnus for my socket education
  560. X */
  561. X
  562. X#include <stdio.h>
  563. X#include <curses.h>
  564. X#include <sys/types.h>
  565. X#include <sys/socket.h>
  566. X#ifdef _AIX
  567. X#include <sys/select.h>
  568. X#endif
  569. X#include <netinet/in.h>
  570. X#include <netdb.h>
  571. X#include <signal.h>
  572. X#include <sys/time.h>
  573. X#include <fcntl.h>
  574. X#include <errno.h>
  575. X#include <ctype.h>
  576. X
  577. X#ifdef BSD_CURSES
  578. X#define cbreak crmode
  579. X#define nocbreak nocrmode
  580. X#endif
  581. X
  582. X#ifdef mips
  583. Xextern int      errno;
  584. X#endif
  585. X
  586. X#include "bt.h"
  587. X#include "btpack.h"
  588. X#include "btclient.h"
  589. X#include "msleep.h"
  590. X
  591. Xstatic int      gamemapsizex;
  592. Xstatic int      gamemapsizey;
  593. Xstatic int      videosetup = 0;
  594. Xstatic int      connected = 0;
  595. X
  596. Xmain(argc, argv)
  597. X    int             argc;
  598. X    char           *argv[];
  599. X{
  600. X    WINDOW         *help;
  601. X    int             inputchar;
  602. X    int             inputpos;
  603. X    char            inputline[200];
  604. X    char            recvdline[200];
  605. X    static char     hostname[81];
  606. X    int             done;
  607. X    char            messagetype;
  608. X    location        at;
  609. X    location        from, to;
  610. X    int             theplayer;
  611. X    int             x, y;
  612. X    int             offset;
  613. X    int             count;
  614. X    int             i;
  615. X    int             gotmessage;
  616. X    port = 2727;
  617. X    gethostname(hostname, 80);
  618. X    host = hostname;
  619. X    for (i = 1; (i < argc); i++) {
  620. X        if (strcmp(argv[i], "-h") == 0) {
  621. X            if ((i + 1) < argc) {
  622. X                host = argv[++i];
  623. X            } else {
  624. X                fprintf(stderr, "btclient: hostname missing for -h option.\n");
  625. X                exit(-1);
  626. X            }
  627. X        } else if (strcmp(argv[i], "-p") == 0) {
  628. X            if ((i + 1) < argc) {
  629. X                port = atoi(argv[++i]);
  630. X            } else {
  631. X                fprintf(stderr, "btclient: port # missing for -p option.\n");
  632. X                exit(-1);
  633. X            }
  634. X        } else {
  635. X            fprintf(stderr, "Unknown option.\n");
  636. X            fprintf(stderr, "Usage: btclient -h hostname [-p port#]\n");
  637. X            exit(-1);
  638. X        }
  639. X    }
  640. X    if (connectstream() == 0) {
  641. X        perror("Server unreachable.\n");
  642. X        exit(1);
  643. X    }
  644. X    printf("Waiting for players to join...\n");
  645. X    promptoffset = 0;
  646. X    signal(SIGTERM, endtrap);
  647. X    signal(SIGQUIT, endtrap);
  648. X    signal(SIGINT, endtrap);
  649. X    signal(SIGPIPE, endtrap);
  650. X    done = 0;
  651. X    inputpos = 0;
  652. X    specific = specificspace;
  653. X    cursorx = (-1);
  654. X    cursory = (-1);
  655. X    /* Get YOUARE message & any other pregame messages */
  656. X    do {
  657. X        waitevent();
  658. X        getmessage(&messagetype, specific);
  659. X        if (messagetype == _END) {
  660. X            endprogram();
  661. X            exit(-1);
  662. X        } else if (messagetype == _BUSY) {
  663. X            printf("Game is full.\n");
  664. X            endprogram();
  665. X            exit(-1);
  666. X        } else if (messagetype == _TEXT) {
  667. X            printf("%s\n", (char *) specific);
  668. X        }
  669. X    } while (messagetype != _YOUARE);
  670. X    playernumber = specific[0] - 64;
  671. X    /* Get STARTUP message */
  672. X    connected = 1;
  673. X    do {
  674. X        waitevent();
  675. X        getmessage(&messagetype, specific);
  676. X        if (messagetype == _END) {
  677. X            printf("Server died.\n");
  678. X            endprogram();
  679. X            exit(-1);
  680. X        }
  681. X    } while (messagetype != _STARTUP);
  682. X    totalplayers = specific[0] - 64;
  683. X    gamemapsizex = specific[1] - 64;
  684. X    gamemapsizey = specific[2] - 64;
  685. X    initscr();
  686. X    cbreak();
  687. X    noecho();
  688. X    inputfd = fileno(stdin);
  689. X    FD_SET(inputfd, &events);
  690. X    fflush(stdin);
  691. X    videosetup = 1;
  692. X    inputstate = _CURSORCOMMAND;
  693. X    /* Now we know enough to setup the display */
  694. X    clear();
  695. X    refresh();
  696. X    xsize = COLS / (gamemapsizex + 1);
  697. X    if (xsize < 4) {
  698. X        fprintf(stderr,
  699. X            "The window is too narrow. Run the server with a narrower map.");
  700. X        endprogram();
  701. X        exit(-1);
  702. X    }
  703. X    if ((LINES - gamemapsizey) < 4) {
  704. X        fprintf(stderr,
  705. X            "The window is too short. Run the server with a shorter map.\n");
  706. X        endprogram();
  707. X        exit(-1);
  708. X    }
  709. X    output = newwin(LINES - gamemapsizey - 4, COLS - 1, gamemapsizey + 4, 0);
  710. X    info = newwin(2, COLS -1, gamemapsizey + 2, 0);
  711. X    scrollok(output, 1);
  712. X    setupmap();
  713. X    signal(SIGCONT, restorescreen);
  714. X    for (count = 1; (count <= totalplayers); count++)
  715. X        players[count].live = 1;
  716. X    sprintf(inputline, "We are player %c.\n", playernumber + 64);
  717. X    tellplayer(inputline);
  718. X    sprintf(inputline, "? summons help.\n");
  719. X    tellplayer(inputline);
  720. X    promptplayer("Command:");
  721. X    while (done != 2) {
  722. X        gotmessage = 0;
  723. X        while (1) {
  724. X            if (!waitshort(playerFd))
  725. X                break;
  726. X            if (!getmessage(&messagetype, specific))
  727. X                break;
  728. X            gotmessage = 1;
  729. X            offset = 0;
  730. X            switch ((int) messagetype) {
  731. X            case _HEXSTATUS:
  732. X                striplocation(&at, specific, &offset);
  733. X                map[at.x][at.y].terrain = specific[offset] - 64;
  734. X                offset++;
  735. X                stripint(&map[at.x][at.y].population, specific, &offset);
  736. X                stripint(&map[at.x][at.y].lastuse, specific, &offset);
  737. X                stripint(&map[at.x][at.y].troops, specific, &offset);
  738. X                map[at.x][at.y].owner = specific[offset] - 64;
  739. X                offset++;
  740. X                displayhex(at.x, at.y);
  741. X                break;
  742. X            case _PLAYERSTATUS:
  743. X                theplayer = specific[offset] - 64;
  744. X                offset++;
  745. X                stripint(&players[theplayer].action, specific, &offset);
  746. X                stripint(&players[theplayer].hexes, specific, &offset);
  747. X                stripint(&players[theplayer].troops, specific, &offset);
  748. X                stripint(&players[theplayer].population, specific, &offset);
  749. X                stripint(&players[theplayer].citadels, specific, &offset);
  750. X                striplocation(&players[theplayer].start, specific, &offset);
  751. X                displayplayer(theplayer);
  752. X                if ((theplayer == playernumber) && (cursorx == (-1))) {
  753. X                    cursorx = players[theplayer].start.x;
  754. X                    cursory = players[theplayer].start.y;
  755. X                }
  756. X                break;
  757. X            case _PLAYERDEAD:
  758. X                theplayer = specific[0] - 64;
  759. X                players[theplayer].live = 0;
  760. X                for (y = 0; (y < gamemapsizey); y++) {
  761. X                    for (x = 0; (x < gamemapsizex); x++) {
  762. X                        if (map[x][y].owner == theplayer) {
  763. X                            map[x][y].troops = 0;
  764. X                            map[x][y].owner = 0;
  765. X                            displayhex(x, y);
  766. X                        }
  767. X                    }
  768. X                }
  769. X                displayplayer(theplayer);
  770. X                refresh();
  771. X                if (theplayer == playernumber) {
  772. X                    tellplayer("We are out of the game.\n");
  773. X                    if (done == 3)
  774. X                        done = 2;
  775. X                    else {
  776. X                        done = 1;
  777. X                        tellplayer("Press q to exit.\n");
  778. X                    }
  779. X                }
  780. X                break;
  781. X            case _TEXT:
  782. X                sprintf(recvdline, "%s\n", specific);
  783. X                tellplayer(recvdline);
  784. X                break;
  785. X            case _ACTION:
  786. X                for (theplayer = 1; (theplayer <= totalplayers); theplayer++) {
  787. X                    if (players[theplayer].live == 1) {
  788. X                        players[theplayer].action += (2 + players[theplayer].citadels);
  789. X                        displayplayer(theplayer);
  790. X                    }
  791. X                }
  792. X                break;
  793. X            case _END:
  794. X                tellplayer("The game is over.\n");
  795. X                tellplayer("Press q to exit.\n");
  796. X                done = 1;
  797. X                break;
  798. X            case _LOOK:
  799. X                striplocation(&at, specific, &offset);
  800. X                flashhex(at.x, at.y);
  801. X            }
  802. X        }
  803. X        if (done != 0) {
  804. X            if (inputwaiting(inputfd)) {
  805. X                inputchar = getch();
  806. X                if (inputchar == 'q')
  807. X                    done = 2;
  808. X            }
  809. X        } else {
  810. X            if (inputwaiting(inputfd)) {
  811. X                inputchar = getch();
  812. X                switch (inputstate) {
  813. X                case _CURSORM1:
  814. X                case _CURSORM2:
  815. X                case _CURSORRECRUIT:
  816. X                case _CURSORCONSTRUCT:
  817. X                case _CURSORDESTROY:
  818. X                case _CURSORCOMMAND:
  819. X                    switch (inputchar) {
  820. X                    case mescape:
  821. X                        if (inputstate != _CURSORCOMMAND) {
  822. X                            tellplayer("Cancelled.\n");
  823. X                            promptplayer("Command:");
  824. X                            inputstate = _CURSORCOMMAND;
  825. X                        }
  826. X                        break;
  827. X                    case 'h':
  828. X                        if (cursorx > 0) {
  829. X                            cursorx--;
  830. X                            displayhex(cursorx + 1, cursory);
  831. X                            displayhex(cursorx, cursory);
  832. X                        }
  833. X                        break;
  834. X                    case 'l':
  835. X                        if (cursorx < gamemapsizex - 1) {
  836. X                            cursorx++;
  837. X                            displayhex(cursorx - 1, cursory);
  838. X                            displayhex(cursorx, cursory);
  839. X                        }
  840. X                        break;
  841. X                    case 'k':
  842. X                        if (cursory > 0) {
  843. X                            cursory--;
  844. X                            displayhex(cursorx, cursory + 1);
  845. X                            displayhex(cursorx, cursory);
  846. X                        }
  847. X                        break;
  848. X                    case 'j':
  849. X                        if (cursory < gamemapsizey - 1) {
  850. X                            cursory++;
  851. X                            displayhex(cursorx, cursory - 1);
  852. X                            displayhex(cursorx, cursory);
  853. X                        }
  854. X                        break;
  855. X                    case ' ':
  856. X                    case '\n':
  857. X                        switch (inputstate) {
  858. X                        case _CURSORM1:
  859. X                            from.x = cursorx;
  860. X                            from.y = cursory;
  861. X                            promptplayer("To? ");
  862. X                            inputstate = _CURSORM2;
  863. X                            break;
  864. X                        case _CURSORM2:
  865. X                            to.x = cursorx;
  866. X                            to.y = cursory;
  867. X                            promptplayer("Troops?");
  868. X                            inputstate = _CURSORM3;
  869. X                            break;
  870. X                        case _CURSORRECRUIT:
  871. X                        case _CURSORCONSTRUCT:
  872. X                        case _CURSORDESTROY:
  873. X                            from.x = cursorx;
  874. X                            from.y = cursory;
  875. X                            outputline[1] = from.x + 64;
  876. X                            outputline[2] = from.y + 64;
  877. X                            outputline[3] = NULL;
  878. X                            switch (inputstate) {
  879. X                            case _CURSORRECRUIT:
  880. X                                outputline[0] = _RECRUIT;
  881. X                                break;
  882. X                            case _CURSORCONSTRUCT:
  883. X                                outputline[0] = _CONSTRUCT;
  884. X                                break;
  885. X                            case _CURSORDESTROY:
  886. X                                outputline[0] = _DESTROY;
  887. X                                break;
  888. X                            }
  889. X                            write(playerFd, outputline, strlen(outputline) + 1);
  890. X                            inputstate = _CURSORCOMMAND;
  891. X                            promptplayer("Command:");
  892. X                            break;
  893. X                        case _CURSORCOMMAND:
  894. X                            tellplayer("Huh?\n");
  895. X                        }
  896. X                        break;
  897. X                    }
  898. X                    if (inputstate == _CURSORCOMMAND) {
  899. X                        switch (inputchar) {
  900. X                        case '/':
  901. X                        case '?':
  902. X                            help = newwin(17, 65, 1, 5);
  903. X                            werase(help);
  904. X                            box(help, '|', '-');
  905. X                            wmove(help, 0, 0);
  906. X                            mvwprintw(help, 1, 1, "Broken Throne Help");
  907. X                            mvwprintw(help, 2, 1, "m: move troops (from) (to).");
  908. X                            mvwprintw(help, 3, 1, " Attacking is accomplished by moving into enemy territory.");
  909. X                            mvwprintw(help, 4, 1, "r: recruit troops (location): location must be town or city");
  910. X                            mvwprintw(help, 5, 1, "c: construct city (location): location must be town");
  911. X                            mvwprintw(help, 6, 1, "d: destroy (location): location must be city or town");
  912. X                            mvwprintw(help, 7, 1, "q: quit game.");
  913. X                            mvwprintw(help, 8, 1, "Where parentheses appear above, you are expected to move");
  914. X                            mvwprintw(help, 9, 1, "the cursor with the standard hack/ vi/ moria movement keys:");
  915. X                            mvwprintw(help, 10, 1, "h left, l right, j down, k up.");
  916. X                            mvwprintw(help, 11, 1, "Select the location by pressing the space bar or RETURN.");
  917. X                            mvwprintw(help, 12, 1, "t: tell player (player letter) followed by message.");
  918. X                            mvwprintw(help, 13, 1, "Enter a '#' sign instead of a letter to tell all.");
  919. X                            mvwprintw(help, 14, 1, "M: (capitalized): redraw Messy screen.");
  920. X                            mvwprintw(help, 15, 1, "Press any key now to continue play.");
  921. X                            wrefresh(help);
  922. X                            inputchar = getch();
  923. X                            werase(help);
  924. X                            delwin(help);
  925. X                            touchwin(mapw);
  926. X                            touchwin(output);
  927. X                            touchwin(info);
  928. X                            wrefresh(mapw);
  929. X                            wrefresh(output);
  930. X                            wrefresh(info);
  931. X                            break;
  932. X                        case 'M':
  933. X                            restorescreen();
  934. X                            break;
  935. X                        case 'm':
  936. X                            promptplayer("Move: From? ");
  937. X                            inputstate = _CURSORM1;
  938. X                            displayhex(cursorx, cursory);
  939. X                            break;
  940. X                        case 'r':
  941. X                            promptplayer("Recruit: at what location? ");
  942. X                            inputstate = _CURSORRECRUIT;
  943. X                            displayhex(cursorx, cursory);
  944. X                            break;
  945. X                        case 'c':
  946. X                            promptplayer("Construct city: at what location? ");
  947. X                            inputstate = _CURSORCONSTRUCT;
  948. X                            displayhex(cursorx, cursory);
  949. X                            break;
  950. X                        case 'd':
  951. X                            promptplayer("Destroy city or town: at what location? ");
  952. X                            inputstate = _CURSORDESTROY;
  953. X                            displayhex(cursorx, cursory);
  954. X                            break;
  955. X                        case 'q':
  956. X                            outputline[0] = _QUIT;
  957. X                            outputline[1] = NULL;
  958. X                            if (players[playernumber].live) {
  959. X                                promptplayer("Quit: confirm (y/n)? ");
  960. X                                inputchar = getch();
  961. X                                if ((inputchar != 'y') && (inputchar != 'Y')) {
  962. X                                    tellplayer("Cancelled.\n");
  963. X                                    break;
  964. X                                }
  965. X                            }
  966. X                            done = 3;
  967. X                            write(playerFd, outputline, strlen(outputline) + 1);
  968. X                            break;
  969. X                        case 't':
  970. X                            promptplayer("Tell: whom and what? ");
  971. X                            inputstate = _CURSORTELL;
  972. X                        }
  973. X                    }
  974. X                    break;
  975. X                case _CURSORM3:
  976. X                case _CURSORTELL:
  977. X                    switch (inputchar) {
  978. X                    case mbackspace:
  979. X                    case mdelete:
  980. X                        if (inputpos > 0) {
  981. X                            inputpos--;
  982. X                            wmove(info, 1, promptoffset + inputpos);
  983. X                            waddch(info, ' ');
  984. X                            wmove(info, 1, promptoffset + inputpos);
  985. X                            wrefresh(info);
  986. X                        }
  987. X                        break;
  988. X                    case mescape:
  989. X                        tellplayer("");
  990. X                        tellplayer("Cancelled.\n");
  991. X                        promptplayer("Command:");
  992. X                        inputstate = _CURSORCOMMAND;
  993. X                        break;
  994. X                    case '\n':
  995. X                        inputline[inputpos] = NULL;
  996. X                        wmove(info, 1, 0);
  997. X                        wrefresh(info);
  998. X                        switch (inputstate) {
  999. X                        case _CURSORTELL:
  1000. X                            inputpos = 0;
  1001. X                            if (inputline[0] == '#') {
  1002. X                                outputline[0] = _TELLALL;
  1003. X                                strcpy(&outputline[1], &inputline[1]);
  1004. X                                write(playerFd, outputline, strlen(outputline) + 1);
  1005. X                            } else {
  1006. X                                outputline[0] = _PRIVATE;
  1007. X                                if (inputline[0] > 96)
  1008. X                                    inputline[0] -= 32;
  1009. X                                if (isalpha(inputline[0])) {
  1010. X                                    outputline[1] = inputline[0];
  1011. X                                    if (!inrange(outputline[1] - 64, 1, totalplayers)) {
  1012. X                                        tellplayer("Huh?\n");
  1013. X                                    } else {
  1014. X                                        strcpy(&outputline[2], &inputline[1]);
  1015. X                                        write(playerFd, outputline, strlen(outputline) + 1);
  1016. X                                    }
  1017. X                                } else
  1018. X                                    tellplayer("Huh?\n");
  1019. X                            }
  1020. X                            inputstate = _CURSORCOMMAND;
  1021. X                            promptplayer("Command:");
  1022. X                            break;
  1023. X                        case _CURSORM3:
  1024. X                            outputline[0] = _MOVE;
  1025. X                            outputline[1] = from.x + 64;
  1026. X                            outputline[2] = from.y + 64;
  1027. X                            outputline[3] = to.x + 64;
  1028. X                            outputline[4] = to.y + 64;
  1029. X                            if (inputpos == 0)
  1030. X                                packint(5, map[from.x][from.y].troops);
  1031. X                            else
  1032. X                                packint(5, atoi(inputline));
  1033. X                            inputpos = 0;
  1034. X                            outputline[8] = NULL;
  1035. X                            write(playerFd, outputline, strlen(outputline) + 1);
  1036. X                            inputstate = _CURSORCOMMAND;
  1037. X                            promptplayer("Command:");
  1038. X                            break;
  1039. X                        }
  1040. X                    default:
  1041. X                        if ((inputpos + promptoffset < (COLS - 1)) && (!iscntrl(inputchar))) {
  1042. X                            inputline[inputpos] = inputchar;
  1043. X                            wmove(info, 1, promptoffset + inputpos);
  1044. X                            if (inputpos == 0)
  1045. X                                wclrtoeol(info);
  1046. X                            waddch(info, inputchar);
  1047. X                            wrefresh(info);
  1048. X                            inputpos++;
  1049. X                        }
  1050. X                    }
  1051. X                }
  1052. X            }
  1053. X        }
  1054. X        if (gotmessage == 1) {
  1055. X            wrefresh(mapw);
  1056. X            refresh();
  1057. X        }
  1058. X    }
  1059. X    /* Terminating code - needs to be called on CTRLC also. */
  1060. X    endprogram();
  1061. X    return 0;
  1062. X}
  1063. X
  1064. Xvoid
  1065. Xwaitevent()
  1066. X{
  1067. X    bcopy((char *) &events, (char *) &realevents, sizeof(events));
  1068. X    if (select(FD_SETSIZE, &realevents, NULL, NULL, NULL) < 0) {
  1069. X        if (errno != EINTR) {
  1070. X            perror("select");
  1071. X            exit(1);
  1072. X        }
  1073. X    }
  1074. X}
  1075. X
  1076. Xint
  1077. Xwaitshort(thisfd)
  1078. X    int             thisfd;
  1079. X{
  1080. X    struct timeval  waitTime;
  1081. X    waitTime.tv_sec = SECONDSLIMIT;
  1082. X    waitTime.tv_usec = MICROSECONDSLIMIT;
  1083. X    bcopy((char *) &events, (char *) &realevents, sizeof(events));
  1084. X    if (select(FD_SETSIZE, &realevents, NULL, NULL, &waitTime) < 0) {
  1085. X        if (errno != EINTR) {
  1086. X            perror("select");
  1087. X            exit(1);
  1088. X        }
  1089. X    }
  1090. X    if (FD_ISSET(thisfd, &realevents))
  1091. X        return 1;
  1092. X    return 0;
  1093. X}
  1094. X
  1095. X
  1096. Xvoid
  1097. Xpromptplayer(prompt)
  1098. X    char           *prompt;
  1099. X{
  1100. X    wmove(info, 1, 0);
  1101. X    wclrtoeol(info);
  1102. X    waddstr(info, prompt);
  1103. X    promptoffset = strlen(prompt) + 1;
  1104. X    wrefresh(info);
  1105. X}
  1106. X
  1107. Xvoid
  1108. Xrestorescreen()
  1109. X{
  1110. X    wrefresh(curscr);
  1111. X}
  1112. X
  1113. Xvoid
  1114. Xendtrap()
  1115. X{
  1116. X    endprogram();
  1117. X    exit(-1);
  1118. X}
  1119. X
  1120. Xvoid
  1121. Xendprogram()
  1122. X{
  1123. X    int             x;
  1124. X    if (connected) {
  1125. X        outputline[0] = _DISCONNECT;
  1126. X        outputline[1] = NULL;
  1127. X        write(playerFd, outputline, strlen(outputline) + 1);
  1128. X        /*
  1129. X         * Let any messages about our death go through without a
  1130. X         * SIGPIPE
  1131. X         */
  1132. X        sleep(2);
  1133. X        close(playerFd);
  1134. X    }
  1135. X    if (videosetup) {
  1136. X        if (output) {
  1137. X            wclear(output);
  1138. X            delwin(output);
  1139. X        }
  1140. X        clear();
  1141. X        refresh();
  1142. X        endwin();
  1143. X        echo();
  1144. X        nocbreak();
  1145. X    }
  1146. X    printf("Client shut down.\n");
  1147. X    for (x = 0; (x < gamemapsizex); x++) {
  1148. X        free(map[x]);
  1149. X    }
  1150. X    free(map);
  1151. X}
  1152. X
  1153. Xint
  1154. Xinrange(x, low, high)
  1155. X    int             x;
  1156. X    int             low;
  1157. X    int             high;
  1158. X{
  1159. X    if (x < low || x > high)
  1160. X        return 0;
  1161. X    return 1;
  1162. X}
  1163. X
  1164. Xint
  1165. Xgetmessage(messagetype, specific)
  1166. X    char           *messagetype;
  1167. X    char           *specific;
  1168. X{
  1169. X    *messagetype = 0;
  1170. X    *specific = 0;
  1171. X    if (!inputwaiting(playerFd)) {
  1172. X        return 0;
  1173. X    }
  1174. X    read(playerFd, messagetype, 1);
  1175. X    readnullterm(playerFd, specific);
  1176. X    if (*messagetype == 0)
  1177. X        return 0;
  1178. X    else
  1179. X        return 1;
  1180. X}
  1181. X
  1182. Xvoid
  1183. Xreadnullterm(fd, specific)
  1184. X    int             fd;
  1185. X    char           *specific;
  1186. X{
  1187. X    int             done;
  1188. X    char           *current;
  1189. X    current = specific;
  1190. X    done = 0;
  1191. X    while (done == 0) {
  1192. X        while (1) {
  1193. X            waitevent();
  1194. X            if (inputwaiting(fd))
  1195. X                break;
  1196. X        }
  1197. X        read(fd, current, 1);
  1198. X        if (*current == 0)
  1199. X            done = 1;
  1200. X        current++;
  1201. X    }
  1202. X}
  1203. X
  1204. Xvoid
  1205. Xsetupmap(x, y)
  1206. X    int             x;
  1207. X    int             y;
  1208. X{
  1209. X    for (y = 0; (y < gamemapsizey); y++) {
  1210. X        move(y + 1, 0);
  1211. X        addch(48 + ((y / 10) % 10));
  1212. X        move(y + 1, 1);
  1213. X        addch(48 + (y % 10));
  1214. X    }
  1215. X    for (x = 0; (x < gamemapsizex); x++) {
  1216. X        move(0, x * xsize + 5);
  1217. X        addch(65 + x);
  1218. X    }
  1219. X    mapw = newwin(gamemapsizey + 1, 0, 1, 4);
  1220. X    map = (hex **) malloc(sizeof(hex *) * gamemapsizex);
  1221. X    for (x = 0; (x < gamemapsizex); x++) {
  1222. X        map[x] = (hex *) malloc(sizeof(hex) * gamemapsizey);
  1223. X    }
  1224. X    for (y = 0; (y < gamemapsizey); y++) {
  1225. X        for (x = 0; (x < gamemapsizex); x++) {
  1226. X            map[x][y].terrain = 7;
  1227. X            map[x][y].troops = 0;
  1228. X            map[x][y].population = 0;
  1229. X            map[x][y].owner = 0;
  1230. X            map[x][y].lastuse = 0;
  1231. X            displayhex(x, y);
  1232. X        }
  1233. X    }
  1234. X    refresh();
  1235. X    wrefresh(mapw);
  1236. X}
  1237. X
  1238. Xvoid
  1239. Xflashhex(x, y)
  1240. X    int             x;
  1241. X    int             y;
  1242. X{
  1243. X    wmove(mapw, y, x * xsize + 1);
  1244. X    waddch(mapw, "*");
  1245. X    wrefresh(mapw);
  1246. X    msleep(1000);
  1247. X    wmove(mapw, y, x * xsize + 1);
  1248. X    waddch(mapw, terrainimage[map[x][y].terrain]);
  1249. X}
  1250. X
  1251. Xvoid
  1252. Xdisplayhex(x, y)
  1253. X    int             x;
  1254. X    int             y;
  1255. X{
  1256. X    int             redraw;
  1257. X    redraw = 0;
  1258. X    if ((x == cursorx) && (y == cursory)) {
  1259. X        wstandout(mapw);
  1260. X        redraw = 1;
  1261. X    } else
  1262. X        wstandend(mapw);
  1263. X    wmove(mapw, y, x * xsize);
  1264. X    if (map[x][y].owner > 0)
  1265. X        waddch(mapw, 96 + map[x][y].owner);
  1266. X    else
  1267. X        waddch(mapw, ' ');
  1268. X    wmove(mapw, y, x * xsize + 1);
  1269. X    waddch(mapw, terrainimage[map[x][y].terrain]);
  1270. X    if (((map[x][y].terrain != 4) || map[x][y].owner == playernumber)
  1271. X        && (map[x][y].troops > 0)) {
  1272. X        wmove(mapw, y, x * xsize + 2);
  1273. X        waddch(mapw, 48 + ((map[x][y].troops / 10) % 10));
  1274. X        wmove(mapw, y, x * xsize + 3);
  1275. X        waddch(mapw, 48 + (map[x][y].troops % 10));
  1276. X    } else {
  1277. X        wmove(mapw, y, x * xsize + 2);
  1278. X        waddstr(mapw, "  ");
  1279. X    }
  1280. X    wstandend(mapw);
  1281. X    if (redraw == 1)
  1282. X        wrefresh(mapw);
  1283. X    wmove(mapw, y, x * xsize + 1);
  1284. X}
  1285. X
  1286. Xint
  1287. Xconnectstream()
  1288. X{
  1289. X    int             s;
  1290. X    struct sockaddr_in saddr;
  1291. X    struct in_addr  host_address;
  1292. X    if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
  1293. X        perror("socket create");
  1294. X        return (0);
  1295. X    }
  1296. X    saddr.sin_family = AF_INET;
  1297. X    if (!get_host_address(host, &host_address)) {
  1298. X        printf("Bad or missing server address.\n");
  1299. X        exit(1);
  1300. X    }
  1301. X    bcopy(&host_address, &saddr.sin_addr, sizeof(struct in_addr));
  1302. X    /* saddr.sin_addr.s_addr = htonl(inet_addr(host)); Old method */
  1303. X    saddr.sin_port = htons(port);
  1304. X
  1305. X    if (connect(s, (struct sockaddr *) & saddr, sizeof(struct sockaddr_in)) < 0) {
  1306. X        perror("connect");
  1307. X        return (0);
  1308. X    }
  1309. X    FD_SET(s, &events);
  1310. X    playerFd = s;
  1311. X    return (1);
  1312. X}
  1313. X
  1314. X
  1315. X
  1316. Xint
  1317. Xinputwaiting(sfd)
  1318. X    int             sfd;
  1319. X{
  1320. X    if (FD_ISSET(sfd, &realevents))
  1321. X        return 1;
  1322. X    else
  1323. X        return 0;
  1324. X}
  1325. X
  1326. X/*
  1327. X * int errrec; fd_set readfds; struct timeval waitTime;
  1328. X * waitTime.tv_sec=SECONDSLIMIT; waitTime.tv_usec=MICROSECONDSLIMIT;
  1329. X * bcopy((char *) sfdset, (char *) &readfds,sizeof(*sfdset)); if
  1330. X * (select(FD_SETSIZE,&readfds,NULL,NULL,&waitTime)<0) { if (errno!=EINTR) {
  1331. X * perror("select"); } return 0; } if (FD_ISSET(sfd,&readfds)) return 1; else
  1332. X * return 0; }
  1333. X * 
  1334. X */
  1335. X
  1336. Xvoid
  1337. Xtellplayer(this)
  1338. X    char           *this;
  1339. X{
  1340. X    wprintw(output, this);
  1341. X    wrefresh(output);
  1342. X}
  1343. X
  1344. Xvoid
  1345. Xdisplayplayer(theplayer)
  1346. X    int             theplayer;
  1347. X{
  1348. X    static char     outputline[100];
  1349. X    wmove(info, 0, theplayer * 12 - 11);
  1350. X    if (players[theplayer].live == 1)
  1351. X        waddstr(info, sprintf(outputline, "%c:%4d,%3d", theplayer + 64,
  1352. X              players[theplayer].hexes, players[theplayer].action));
  1353. X    else
  1354. X        waddstr(info, sprintf(outputline, "%c: Dead   ", theplayer + 64));
  1355. X    wrefresh(info);
  1356. X}
  1357. X
  1358. Xvoid
  1359. Xdisplaysignal(smessage)
  1360. X    char           *smessage;
  1361. X{
  1362. X    wmove(info, 0, COLS - 4);
  1363. X    waddstr(info, smessage);
  1364. X}
  1365. X
  1366. X/*
  1367. X * get_host_address: borrowed with appreciation from Tinytalk. Does a nice
  1368. X * job of getting around the various stupidities of the inetaddr routine, et
  1369. X * cetera.
  1370. X */
  1371. X
  1372. Xint
  1373. Xget_host_address(name, addr)    /* Get a host address. */
  1374. X    register char  *name;
  1375. X    register struct in_addr *addr;
  1376. X{
  1377. X    struct hostent *blob;
  1378. X    union {            /* %#@!%!@%#!@ idiot who designed */
  1379. X        long            signed_thingy;    /* the inetaddr routine.... */
  1380. X        unsigned long   unsigned_thingy;
  1381. X    }               thingy;
  1382. X
  1383. X    if (*name == '\0') {
  1384. X        fprintf(stderr, "%% No host address specified.\n");
  1385. X        return (0);
  1386. X    }
  1387. X    if ((*name >= '0') && (*name <= '9')) {    /* IP address. */
  1388. X        addr->s_addr = inet_addr(name);
  1389. X        thingy.unsigned_thingy = addr->s_addr;
  1390. X        if (thingy.signed_thingy == -1) {
  1391. X            fprintf(stderr, "%% Couldn't find host %s .\n", name);
  1392. X            return (0);
  1393. X        }
  1394. X    } else {        /* Host name. */
  1395. X        blob = gethostbyname(name);
  1396. X
  1397. X        if (blob == NULL) {
  1398. X            fprintf(stderr, "%% Couldn't find host %s .\n", name);
  1399. X            return (0);
  1400. X        }
  1401. X        bcopy(blob->h_addr, addr, sizeof(struct in_addr));
  1402. X    }
  1403. X
  1404. X    return (1);        /* Success. */
  1405. X}
  1406. END_OF_FILE
  1407. if test 20630 -ne `wc -c <'btclient.c'`; then
  1408.     echo shar: \"'btclient.c'\" unpacked with wrong size!
  1409. fi
  1410. # end of 'btclient.c'
  1411. fi
  1412. if test -f 'btclient.h' -a "${1}" != "-c" ; then 
  1413.   echo shar: Will not clobber existing file \"'btclient.h'\"
  1414. else
  1415. echo shar: Extracting \"'btclient.h'\" \(1965 characters\)
  1416. sed "s/^X//" >'btclient.h' <<'END_OF_FILE'
  1417. X/*
  1418. X * btclient.h: function declarations for the Broken Throne Client By Tom
  1419. X * Boutell, 11/90- 2/91. Do As Thou Wilt, Only Please Send Me Copies!
  1420. X * 
  1421. X * The three constants below can be changed if necessary to support unusal codes
  1422. X * or non- ASCII machines. Both mdelete and mbackspace are accepted as a
  1423. X * backspace key in an effort to improve compatibility with brain- dead
  1424. X * Cursi. (Well, what ELSE is the plural of Curses? Cursesi?)
  1425. X */
  1426. X
  1427. X#define mbackspace 8
  1428. X#define mdelete 127
  1429. X#define mescape 27
  1430. X
  1431. X#define _CURSORM1 1
  1432. X#define _CURSORM2 2
  1433. X#define _CURSORM3 3
  1434. X#define _CURSORRECRUIT 4
  1435. X#define _CURSORTELL 5
  1436. X#define _CURSORCOMMAND 6
  1437. X#define _CURSORCONSTRUCT 7
  1438. X#define _CURSORDESTROY 8
  1439. X
  1440. X#define SECONDSLIMIT 0L
  1441. X#define MICROSECONDSLIMIT 100000L
  1442. X
  1443. Xhex           **map = 0;
  1444. Xplayer          players[_MAXPLAYERS];
  1445. Xint             playernumber;
  1446. Xint             totalplayers;
  1447. Xint             currenttime;
  1448. Xint             living;
  1449. Xint             port;
  1450. Xint             playerFd;
  1451. Xint             promptoffset;
  1452. Xfd_set          events;
  1453. Xfd_set          realevents;
  1454. Xint             inputstate;
  1455. Xchar           *host;
  1456. X
  1457. Xint             connectstream();
  1458. Xint             inputwaiting();
  1459. Xvoid            waitevent();
  1460. Xint             waitshort();
  1461. Xvoid            setupmap();
  1462. Xvoid            displayhex();
  1463. Xvoid            flashhex();
  1464. Xvoid            tellplayer();
  1465. Xvoid            displayplayer();
  1466. Xvoid            readnullterm();
  1467. Xvoid            endprogram();
  1468. Xvoid            restorescreen();
  1469. Xvoid            displaysignal();
  1470. Xvoid            endtrap();
  1471. Xint             inrange();
  1472. Xvoid            promptplayer();
  1473. Xint             cursorx;
  1474. Xint             cursory;
  1475. XWINDOW         *info = 0;
  1476. XWINDOW         *output = 0;
  1477. XWINDOW         *mapw;
  1478. Xint             getmessage();
  1479. Xint             terrainimage[] = {' ', '.', 'T', 'M', 'F', 'C', 'S', '^'};
  1480. Xchar            specificspace[256];
  1481. Xchar            outputline[256];
  1482. Xchar           *specific;
  1483. Xint             inputfd;
  1484. Xint             xsize;
  1485. END_OF_FILE
  1486. if test 1965 -ne `wc -c <'btclient.h'`; then
  1487.     echo shar: \"'btclient.h'\" unpacked with wrong size!
  1488. fi
  1489. # end of 'btclient.h'
  1490. fi
  1491. if test -f 'btserver.h' -a "${1}" != "-c" ; then 
  1492.   echo shar: Will not clobber existing file \"'btserver.h'\"
  1493. else
  1494. echo shar: Extracting \"'btserver.h'\" \(3048 characters\)
  1495. sed "s/^X//" >'btserver.h' <<'END_OF_FILE'
  1496. X/*
  1497. X * btserver.h- declarations and controlling constants for Broken Throne.
  1498. X * Copyright 1990 by Tom Boutell.
  1499. X */
  1500. X
  1501. X#define _DEFAULTMAPSIZEX 16
  1502. X#define _DEFAULTMAPSIZEY 16
  1503. X#define _DEFAULTCITYCOST 90
  1504. X#define _DEFAULTDESTROYCOST 10
  1505. X#define _DEFAULTRECRUITTIME 60
  1506. X#define _DEFAULTSPEED 5
  1507. X
  1508. X/* Upper limits (due to use of char in transfer) */
  1509. X#define _MAXMAPSIZEX 128
  1510. X#define _MAXMAPSIZEY 128
  1511. X
  1512. X#define _HEXSTATUS 65
  1513. X#define _PLAYERSTATUS 66
  1514. X#define _PLAYERDEAD 67
  1515. X#define _ACTION 68
  1516. X#define _TEXT 69
  1517. X#define _STARTUP 70
  1518. X#define _END 71
  1519. X#define _YOUARE 72
  1520. X
  1521. X#define _MOVE 65
  1522. X#define _RECRUIT 66
  1523. X#define _QUIT 67
  1524. X#define _PRIVATE 68
  1525. X#define _TELLALL 69
  1526. X#define _DISCONNECT 70
  1527. X#define _CONSTRUCT 71
  1528. X#define _DESTROY 72
  1529. X
  1530. Xextern hex    **map;
  1531. Xextern player   players[_MAXPLAYERS];
  1532. Xextern int      currentplayer;
  1533. Xextern int      totalplayers;
  1534. Xextern int      currenttime;
  1535. Xextern int      living;
  1536. Xextern int      gamecontinuous;
  1537. X
  1538. X/* Beware terrain type zero... existing to keep C happy. */
  1539. X
  1540. Xvoid            move();        /* location from,location to,int number */
  1541. Xvoid            tellcurrentplayer();    /* char* fmt, variable args */
  1542. Xvoid            setupmap();
  1543. Xvoid            recruit();    /* location at */
  1544. Xint             legal();    /* int x,int y */
  1545. Xvoid            killplayer();    /* int player */
  1546. Xchar           *namelocation();    /* location at */
  1547. Xchar           *nameplayer();    /* int player */
  1548. Xint             cost();        /* int x,y */
  1549. Xvoid            destroy();    /* location at */
  1550. Xvoid            construct();    /* location at */
  1551. Xvoid            endprogram();
  1552. Xvoid            stopgame();    /* char* message */
  1553. Xvoid            recruitenqueue();    /* enqueue an x,y location for
  1554. X                     * recruitment */
  1555. Xvoid            recruitdequeue();    /* Remove the first item from the
  1556. X                     * queue */
  1557. Xvoid            recruitall();    /* Recruit at any ready, enqueued towns and
  1558. X                 * cities */
  1559. Xvoid            recruitqueueremove();    /* Remove location x,y if in recruit
  1560. X                     * queue */
  1561. Xvoid            regionupdate();    /* Takes location, region taking, player
  1562. X                 * taking */
  1563. Xvoid            regionchange();    /* Takes location, new region number to paint
  1564. X                 * with */
  1565. Xvoid            regionnew();    /* Takes location, paints with new / recycled
  1566. X                 * region # */
  1567. Xvoid            regionget();    /* Takes location, array of locations,
  1568. X                 * pointer to int; fills in array with set of
  1569. X                 * locations in the same region as first
  1570. X                 * location (only if truly contiguous), and
  1571. X                 * sets *int to total number of locations. */
  1572. Xvoid            support();    /* Takes no arguments, checks for support of
  1573. X                 * forces, starves troops if not supported */
  1574. Xvoid            exiterror();    /* Exits with error code; frees memory if
  1575. X                 * allocated */
  1576. Xvoid            freememory();    /* Frees allocated space */
  1577. Xvoid            endtrap();    /* Signal handler for server death */
  1578. Xvoid            waitenqueue();    /* Enqueues given fd for future game */
  1579. Xvoid            broadcastwaitqueue();    /* Sends message to all wait queue
  1580. X                     * members */
  1581. Xvoid            fetchplayers();    /* Get players for new round of game */
  1582. END_OF_FILE
  1583. if test 3048 -ne `wc -c <'btserver.h'`; then
  1584.     echo shar: \"'btserver.h'\" unpacked with wrong size!
  1585. fi
  1586. # end of 'btserver.h'
  1587. fi
  1588. if test -f 'btserverrc' -a "${1}" != "-c" ; then 
  1589.   echo shar: Will not clobber existing file \"'btserverrc'\"
  1590. else
  1591. echo shar: Extracting \"'btserverrc'\" \(2623 characters\)
  1592. sed "s/^X//" >'btserverrc' <<'END_OF_FILE'
  1593. X; A sample .btserverrc file.
  1594. X; Lines beginning with ; or # are comments. Blank lines are
  1595. X; permitted.
  1596. X
  1597. X; Command - line arguments always override .btserverrc settings.
  1598. X; A .btserverrc in the current directory is looked for first; 
  1599. X; if none is found there, the user's home directory is checked.
  1600. X; (Alternatively any default file can be specified with the
  1601. X; -d option.)
  1602. X
  1603. X; There are reasonable hard - coded defaults for all the options below
  1604. X; except for size and players. Players is usually set on the command
  1605. X; line; a good size to set here if players will be using typical 
  1606. X; 80x25 ascii terminals is 16x16.
  1607. X
  1608. X; players sets a default number of players. Not commonly used
  1609. X; since the number of players varies a lot with who's available.
  1610. X; set to zero here so the server will give an error if the
  1611. X; number of players isn't given on the command line (-n option).
  1612. X
  1613. Xplayers 0
  1614. X
  1615. X; sizex and sizey set the x and y dimensions of the map
  1616. X; respectively. (Commented out here.)
  1617. X; sizes specified here *or* on the command line are overridden if
  1618. X; a map file is used by the size given on the first line of the
  1619. X; map file.
  1620. X
  1621. Xsizex 16
  1622. Xsizey 16
  1623. X
  1624. X; alternatively, size takes the same widthxheight format as
  1625. X; the -s command - line argument.
  1626. X
  1627. Xsize 12x12
  1628. X
  1629. X; speed sets the number of seconds between game ticks.
  1630. X
  1631. Xspeed 4
  1632. X
  1633. X; citycost sets the number of action points required to
  1634. X; turn a city into a town.
  1635. X
  1636. Xcitycost 80
  1637. X
  1638. X; destroycost sets the number of action points required to
  1639. X; destroy a city or town.
  1640. X
  1641. Xdestroycost 10
  1642. X
  1643. X; recruittime sets the number of ticks (not seconds) between
  1644. X; safe recruitments at a given city or town.
  1645. X
  1646. Xrecruittime 40
  1647. X
  1648. X; mapfile sets the name of a map file to be read by default.
  1649. X; The -r option can be used on the command line to specify that
  1650. X; the map be randomly generated anyway. Commented out in this
  1651. X; example.
  1652. X
  1653. X;mapfile map.txt
  1654. X
  1655. X; specifies the default port on which the game takes place.
  1656. X; Separate groups of players will want to set separate ports
  1657. X; (between 1000 and 9999) since only one server can run on
  1658. X; a given port number. 2727 is the default if no .btserverrc
  1659. X; file is present and the -p option is not used.
  1660. X
  1661. Xport 2727
  1662. X
  1663. X; continuous is a switch. If turned "on", then the game will recycle
  1664. X; and restart after all players have quit, and players who were
  1665. X; unable to get into the first game will be brought in from a
  1666. X; wait queue. If turned "off", "late" players will be told the
  1667. X; game is busy, and the server will shut down after one game.
  1668. X; off is the default.
  1669. X
  1670. Xcontinuous on
  1671. X
  1672. X; THINGS THAT PROBABLY SHOULD BE CONFIGURABLE, BUT AREN'T:
  1673. X
  1674. X; Distribution of terrain types on random maps.
  1675. X; What else?
  1676. X
  1677. END_OF_FILE
  1678. if test 2623 -ne `wc -c <'btserverrc'`; then
  1679.     echo shar: \"'btserverrc'\" unpacked with wrong size!
  1680. fi
  1681. # end of 'btserverrc'
  1682. fi
  1683. if test -f 'changes' -a "${1}" != "-c" ; then 
  1684.   echo shar: Will not clobber existing file \"'changes'\"
  1685. else
  1686. echo shar: Extracting \"'changes'\" \(502 characters\)
  1687. sed "s/^X//" >'changes' <<'END_OF_FILE'
  1688. XSee the readme file for a description of changes in version 2.0.
  1689. XThere have been several fundamental changes.
  1690. X
  1691. XNOTE: an apology to folks who sent me diff's way back shortly after
  1692. Xversion 1.12; in the intervening year and a half since I ceased
  1693. Xwork on the game to complete my undergraduate career sanely, I have
  1694. Xlost your many nifty patches. ): I will quickly incorporate patches
  1695. Xmade to version 2.0.
  1696. X
  1697. X(Sure enough, version 2.03, which you are looking at,
  1698. Xincorporates several fixes. See the readme.)
  1699. X
  1700. X
  1701. END_OF_FILE
  1702. if test 502 -ne `wc -c <'changes'`; then
  1703.     echo shar: \"'changes'\" unpacked with wrong size!
  1704. fi
  1705. # end of 'changes'
  1706. fi
  1707. echo shar: End of archive 2 \(of 3\).
  1708. cp /dev/null ark2isdone
  1709. MISSING=""
  1710. for I in 1 2 3 ; do
  1711.     if test ! -f ark${I}isdone ; then
  1712.     MISSING="${MISSING} ${I}"
  1713.     fi
  1714. done
  1715. if test "${MISSING}" = "" ; then
  1716.     echo You have unpacked all 3 archives.
  1717.     rm -f ark[1-9]isdone
  1718. else
  1719.     echo You still need to unpack the following archives:
  1720.     echo "        " ${MISSING}
  1721. fi
  1722. ##  End of shell archive.
  1723. exit 0
  1724.