[comp.sources.games] v12i022: bt2 - Broken Throne, multiplayer realtime conquest game, Part01/02

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

Submitted-by: Tom Boutell <boutell@freezer.it.udel.edu>
Posting-number: Volume 12, Issue 22
Archive-name: bt2/Part01
Supersedes: bt: Volume 11, Issue 75-77
Environment: INET sockets, curses

	[This is a repost, rather than a patch as the names of
	 several files have changed.  This is version 1.11 of bt.
	 If you are running an older version of Unix that does not
	 have the FD_XXX macros, I have a patch file that you can
	 apply. Send email for details.  -br]

[From the author...]
[[The Broken Throne is a multiplayer, realtime game of conquest for Unix
that requires only standard Curses terminal support. The game is played
in a fairly brief time frame and can be played between machines in
reasonable proximity to each other.]]

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 1 (of 2)."
# Contents:  README MANIFEST Makefile bt.h btclient.c btclient.h
#   btserver.c btserver.h changes interface.h msleep.c msleep.h pack.c
#   pack.h
# Wrapped by billr@saab on Wed Feb  6 17:28:31 1991
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(5898 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
XTHE BROKEN THRONE
X
XBy Tom Boutell, 11/20/90- 2/2/91. Do whatever you want with it, provided
Xchanged versions are not released without clearing them through me first.
XI'd like to act as a switchboard for new versions of the game.
X
XTO INSTALL THE GAME
X
XType "make btserver" to make the server, then type "make btclient" to make
Xthe client. Third, enter "make btrobot" to make the robot player. Alternatively
Xyou can simply type "make all" particularly if you have built the game before
Xand know it's fairly compatible with your machine, so errors will be few.
X
XIF IT WORKS
X
XPat yourself on the back and read "bt.doc" next to learn how to start
Xup and play the game!
X
XIF IT DOESN'T WORK
X
XIf "make btserver" fails, I will be rather surprised. Please mail me
Xthe details (boutell@freezer.it.udel.edu). It's worth noting that I wrote
Xa hopefully portable routine, msleep(), to sleep for n microseconds
Xby way of select, but my method may get "optimized out of existence"
Xby your compiler. This won't stop the game from running, though, just
Xmake it use up more processor than it should. If you have the routine
Xusleep() you can use it instead. Plain sleep() only works in increments
Xof seconds, though (much too long).
X
X"make btclient" can be more problematic, since I have to use Curses, and
Xcurses has portability problems here and there. But keyboard I/O is now
Xhandled in a way that's just as portable as the socket stuff, so if the
Xserver compiles, that shouldn't be a problem either.
X
X"make btrobot" should have no problems, especially if you have no problem
Xmaking the client, since it's a hacked client without the ugly user
Xinterface stuff that's so difficult to port.
X
XOne easy fix you may have to make: if your compiler complains that errno
Xis undefined, insert "int errno;" at main level in btclient.c. This really
Xshouldn't be necessary, but sometimes it is.
X
XCONTACT SERVICE
X
XContact me at boutell@freezer.it.udel.edu if you want to play the author
Xor be put in touch with other players! The game works fine across the Internet.
XI also accept bug reports and comments at this address of course.
X
XACKNOWLEDGEMENTS
X
XI'd like to thank the net for many, many, *many* useful suggestions and
Xcorrections. It's due to all of you that I now have such a solid package.
X
XCHANGES IN VERSION 1.11
X
X- Replaced a forgotten call to usleep() in btclient with a call to my
X  msleep() function.
X
XCHANGES IN VERSION 1.10
X
X- Many folks have reported arguments between their compiler and my
X  use of a void pointer named "details" in interface.c. I've unrolled
X  this code a little in hopes of placating unfriendly compilers. If you've
X  had this problem and been able to solve it, please let me know! Folks
X  would like the solution.
X- The server now uses a little routine named msleep() to sleep for n
X  microseconds, instead of usleep() (a wonderful but nonportable SunOS
X  routine). I should thank the net for many suggestions and code examples
X  leading to this.
X- A viable robot player is now included! See the bt.doc file for details
X  on its use. 
X- The usleep() call has been eliminated. The client program now simply
X  selects until either a keyboard event or server message arrives. Very
X  polite; no nasty demand on the processor. Also much more portable.
X- The M command (redraw map) now works properly.
X- Problems due to which the game could crash after folks disconnected
X  have been resolved. This is a major improvement; I haven't been able
X  to get the server to crash since.
X- There are no more strange delays in which another keypress is needed
X  to make the last one "go through." The same change that eliminated the
X  call to usleep() fixed this.
X- The quit command now has a confirmation that accepts only 'y' or 'Y'
X  to prevent accidental disconnection.
X- The code is somewhat cleaned up, though the haste with which I wrote the
X  game still shows!
X- THE INFAMOUS ACTION POINT BUG IS DEAD! Hurrah! With some help from folks
X  on rec.games.programmer, I discovered that when the client was through
X  handling a "move" message from the server, it would "fall through" and
X  process an "action" message, producing an incorrect AP count. AP counts
X  should now be displayed correctly at all times, and movement costs
X  will no longer appear random. (They never were, but after movement, the
X  correct number of APs was broadcast, understandably confusing the player
X  pretty badly.)
X- *** A change affecting the rules: army units now fight as sets of
X  five units. This is invisible to the player, but results in a more
X  even distribution of losses, so it's much less common to take out four
X  armies with one by luck, although still possible.
X
XCHANGES IN VERSION 1.01
X
X- Input is now handled in a way that should be generic BSD.
X- The 'h' key no longer summons help; this is now done with the ? key,
X  shifted or not ('/').
X- Certain strange compiler errors in interface.c are resolved. (Some
X  compilers were treating =* as meaning something other than "equal to
X  the dereferenced value of." One even treated =-1 as meaning something
X  other than equal to -1!) Parentheses in judicious places solved these.
X- You can now move the cursor while "thinking" about what command to enter.
X  The only time you can't move the cursor is while entering a text
X  message or typing a number of troops.
X- It is now easier to tell whether you are in command mode or entering
X  a location.
X- You can now specify a different port number. This is given as a second
X  argument to btserver, and also to btclient. This permits multiple games
X  on one machine.
X- 'M' (capitalized) now redraws a Messy screen after a talk message, etc.
X- There is now an opening message while the map is being transmitted.
X- The number of players must be inputted on the command line. An old
X  undocumented feature of the server was the ability to prompt for this,
X  removed to make the new port number question unambiguous.
END_OF_FILE
if test 5898 -ne `wc -c <'README'`; then
    echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'MANIFEST' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'MANIFEST'\"
else
echo shar: Extracting \"'MANIFEST'\" \(641 characters\)
sed "s/^X//" >'MANIFEST' <<'END_OF_FILE'
X   File Name		Archive #	Description
X-----------------------------------------------------------
X MANIFEST                   1	This shipping list
X Makefile                   1	
X README                     1	
X bt.doc                     2	
X bt.h                       1	
X btclient.c                 1	
X btclient.h                 1	
X btserver.c                 1	
X btserver.h                 1	
X changes                    1	
X interface.c                2	
X interface.h                1	
X msleep.c                   1	
X msleep.h                   1	
X pack.c                     1	
X pack.h                     1	
X robot.c                    2	
END_OF_FILE
if test 641 -ne `wc -c <'MANIFEST'`; then
    echo shar: \"'MANIFEST'\" unpacked with wrong size!
fi
# end of 'MANIFEST'
fi
if test -f 'Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Makefile'\"
else
echo shar: Extracting \"'Makefile'\" \(598 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
Xall: btclient btserver btrobot
X
Xbtclient: btclient.o pack.o sleep.o
X	cc btclient.o pack.o msleep.o -o btclient -ltermcap -lcurses -ltermlib
X
Xbtserver: btserver.o pack.o interface.o msleep.o
X	cc btserver.o pack.o interface.o msleep.o -o btserver -g
X
Xbtrobot: robot.o pack.o
X	cc robot.o pack.o -o btrobot
X
Xrobot.o: robot.c bt.h
X	cc -c robot.c
Xbtclient.o: btclient.c btclient.h bt.h
X	cc -c btclient.c
Xpack.o: pack.c pack.h
X	cc -c pack.c
Xinterface.o: interface.c interface.h
X	cc -c interface.c -g
Xbtserver.o: btserver.c btserver.h bt.h
X	cc -c btserver.c -g
Xmsleep.o: msleep.c msleep.h
X	cc -c msleep.c
X
END_OF_FILE
if test 598 -ne `wc -c <'Makefile'`; then
    echo shar: \"'Makefile'\" unpacked with wrong size!
fi
# end of 'Makefile'
fi
if test -f 'bt.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'bt.h'\"
else
echo shar: Extracting \"'bt.h'\" \(803 characters\)
sed "s/^X//" >'bt.h' <<'END_OF_FILE'
Xtypedef struct {
X  char terrain;
X  int population;
X  int lastuse;
X  int troops;
X  int owner;
X} hex;
X
Xtypedef struct {
X  int x;
X  int y;
X} location;
X
Xtypedef struct {
X  int action;
X  int hexes;
X  int troops;
X  int population;
X  int citadels;
X  int live;
X  location start;
X} player;
X
X/* Dimensions of Map
X   Can be changed if your display hardware permits. */ 
X
X#define _MAPX 16
X#define _MAPY 16
X
X/* Messages from server to clients: */
X#define _HEXSTATUS 65
X#define _PLAYERSTATUS 66
X#define _PLAYERDEAD 67
X#define _ACTION 68
X#define _TEXT 69
X#define _STARTUP 70
X#define _END 71
X#define _YOUARE 72
X#define _LOOK 73
X
X/* From clients to server: */
X#define _MOVE 65
X#define _RECRUIT 66
X#define _QUIT 67
X#define _PRIVATE 68
X#define _TELLALL 69
X#define _DISCONNECT 70
X#define _CONSTRUCT 71
X#define _DESTROY 72
X
END_OF_FILE
if test 803 -ne `wc -c <'bt.h'`; then
    echo shar: \"'bt.h'\" unpacked with wrong size!
fi
# end of 'bt.h'
fi
if test -f 'btclient.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'btclient.c'\"
else
echo shar: Extracting \"'btclient.c'\" \(17664 characters\)
sed "s/^X//" >'btclient.c' <<'END_OF_FILE'
X/* Broken Throne client program, by Tom Boutell 10/90- 12/90
X   Thanks to Marc Cygnus for my socket education */
X
X#include <stdio.h>
X#include <curses.h>
X#include <sys/types.h>
X#include <sys/socket.h>
X#include <netinet/in.h>
X#include <netdb.h>
X#include <signal.h>
X#include <sys/time.h>
X#include <fcntl.h>
X#include <errno.h>
X#include <ctype.h>
X
X#include "bt.h"
X#include "pack.h"
X#include "btclient.h"
X#include "msleep.h"
X
X/* Remember to call refresh() when changes need to be visible; right now
X   displayhex doesn't do so, in order to be efficient. */
Xmain(argc,argv)
X int argc;
X char* argv[];
X {
X WINDOW* help;
X int inputchar;
X int inputpos;
X char inputline[200];
X int done;
X char messagetype;
X location at;
X location from,to;
X int theplayer;
X int x,y,roll;
X int offset;
X int count;
X int gotmessage;
X if (argc>1) {
X   host=argv[1];
X   if (argc>2) 
X     port=atoi(argv[2]);
X   else
X     port=2727;
X }
X else {
X   printf("Usage: btclient hostaddress portnumber\n");
X   exit(1);
X }
X
X if (connectstream()==0) {
X   perror("Socket unopenable.\n");
X   exit(1);
X }
X initscr();
X cbreak();
X noecho();
X inputstate=_CURSORCOMMAND;
X promptplayer("Command:");
X xsize=COLS/(_MAPX+1);
X promptoffset=0;
X if (xsize<4) {
X   printf("The window is too narrow.");
X   exit(1);
X }
X output=newwin(LINES-_MAPY-4,COLS-1,20,0);
X scrollok(output,1);
X refresh();
X setupmap();
X signal(SIGTERM,endprogram);
X signal(SIGCONT,restorescreen);
X inputfd=fileno(stdin);
X FD_SET(inputfd,&events);
X fflush(stdin);
X done=0;
X inputpos=0;
X specific=specificspace;
X cursorx=-1;
X cursory=-1;
X while (1) {
X   waitevent();
X   if (getmessage(&messagetype,specific))
X     break;
X }
X playernumber=specific[0]-64;
X sprintf(inputline,"We are player %c.\n",playernumber+64);
X tellplayer(inputline);
X sprintf(inputline,"? summons help.\n");
X tellplayer(inputline);
X while (1) {
X   waitevent();
X   if (getmessage(&messagetype,specific))
X     break;
X }
X totalplayers=specific[0]-64;
X for (count=1; (count<=totalplayers); count++) 
X   players[count].live=1;
X
X while (done!=2) {
X   gotmessage=0;
X   while (1) {
X     if (!waitshort(playerFd))
X       break;
X     if (!getmessage(&messagetype,specific))
X       break;
X     gotmessage=1;
X     offset=0;
X     switch ((int) messagetype) {
X       case _HEXSTATUS:
X         striplocation(&at,specific,&offset);
X         map[at.x][at.y].terrain=specific[offset]-64;
X         offset++;
X         stripint(&map[at.x][at.y].population,specific,&offset);
X         stripint(&map[at.x][at.y].lastuse,specific,&offset);
X         stripint(&map[at.x][at.y].troops,specific,&offset);
X         map[at.x][at.y].owner=specific[offset]-64;
X         offset++;
X         displayhex(at.x,at.y);
X         break;
X       case _PLAYERSTATUS:
X         theplayer=specific[offset]-64;
X         offset++;
X         stripint(&players[theplayer].action,specific,&offset);
X         stripint(&players[theplayer].hexes,specific,&offset);
X         stripint(&players[theplayer].troops,specific,&offset);
X         stripint(&players[theplayer].population,specific,&offset);
X         stripint(&players[theplayer].citadels,specific,&offset);
X         striplocation(&players[theplayer].start,specific,&offset);
X         displayplayer(theplayer);
X         if ((theplayer==playernumber) && (cursorx==-1)) {
X           cursorx=players[theplayer].start.x;
X           cursory=players[theplayer].start.y;
X         }
X         break;
X       case _PLAYERDEAD:
X         theplayer=specific[0]-64;
X         players[theplayer].live=0;
X         for (y=0; (y<_MAPY); y++) {
X           for (x=0; (x<_MAPX); x++) {
X             if (map[x][y].owner==theplayer) {
X               map[x][y].troops=0;
X               map[x][y].owner=0;
X               displayhex(x,y);
X             }
X           }
X         }
X         displayplayer(theplayer);
X         refresh();
X         if (theplayer==playernumber) {
X           tellplayer("We are out of the game.\n");
X           if (done==3)
X             done=2;
X           else {
X             done=1;
X             tellplayer("Press q to exit.\n");
X           }
X         }
X         break;
X       case _TEXT:
X         tellplayer(sprintf(inputline,"%s\n",specific));
X         break;
X       case _ACTION:
X         for (theplayer=1; (theplayer<=totalplayers); theplayer++) {
X           if (players[theplayer].live==1) {
X             players[theplayer].action+=(2+players[theplayer].citadels);
X             displayplayer(theplayer);
X           }
X         }
X         break;
X       case _END:
X         tellplayer("The game is over.\n");
X         tellplayer("Press q to exit.\n");
X         done=1;
X         break;
X       case _LOOK:
X         striplocation(&at,specific,&offset); 
X         flashhex(at.x,at.y);
X     }
X   }
X  if (done!=0) {
X   if (inputwaiting(inputfd)) {
X     inputchar=getch();
X     if (inputchar=='q')
X       done=2;
X   }
X  }
X  else {
X   if (inputwaiting(inputfd)) {
X    inputchar=getch();
X    switch (inputstate) {
X    case _CURSORM1:
X    case _CURSORM2:
X    case _CURSORRECRUIT:
X    case _CURSORCONSTRUCT:
X    case _CURSORDESTROY:
X    case _CURSORCOMMAND:
X     switch(inputchar) {
X      case 'h':
X      if (cursorx>0) {
X        cursorx--;
X        displayhex(cursorx+1,cursory);
X        displayhex(cursorx,cursory);
X      }
X      break;
X      case 'l':
X      if (cursorx<_MAPX-1) {
X        cursorx++;
X        displayhex(cursorx-1,cursory);
X        displayhex(cursorx,cursory);
X      }
X      break;
X      case 'k':
X      if (cursory>0) {
X        cursory--;
X        displayhex(cursorx,cursory+1);
X        displayhex(cursorx,cursory);
X      }
X      break;
X      case 'j':
X      if (cursory<_MAPY-1) {
X        cursory++;
X        displayhex(cursorx,cursory-1);
X        displayhex(cursorx,cursory);
X      }
X      break;
X      case ' ':
X      case '\n':
X      switch(inputstate) {
X       case _CURSORM1:
X       from.x=cursorx;
X       from.y=cursory;
X       promptplayer("To? ");
X       inputstate=_CURSORM2;
X       break;
X       case _CURSORM2:
X       to.x=cursorx;
X       to.y=cursory;
X       promptplayer("Troops?");
X       inputstate=_CURSORM3; 
X       break;
X       case _CURSORRECRUIT:
X       case _CURSORCONSTRUCT:
X       case _CURSORDESTROY:
X       from.x=cursorx;
X       from.y=cursory;
X       outputline[1]=from.x+64;
X       outputline[2]=from.y+64;
X       outputline[3]=NULL;
X       switch (inputstate) {
X        case _CURSORRECRUIT:
X        outputline[0]=_RECRUIT;
X        break;
X        case _CURSORCONSTRUCT:
X        outputline[0]=_CONSTRUCT;
X        break;
X        case _CURSORDESTROY:
X        outputline[0]=_DESTROY;
X        break;
X       }
X       write(playerFd,outputline,strlen(outputline)+1);
X       inputstate=_CURSORCOMMAND;
X       promptplayer("Command:");
X       break;
X       case _CURSORCOMMAND:
X       tellplayer("Huh?\n");
X      }
X      break;
X     }
X    if (inputstate==_CURSORCOMMAND) {
X     switch(inputchar) {
X      case '/':
X      case '?':
X      help=newwin(17,65,1,5);
X      werase(help);
X      box(help,'|','-');
X      wmove(help,0,0);
X      mvwprintw(help,1,1,"Broken Throne Help");
X      mvwprintw(help,2,1,"m: move troops (from) (to).");
X      mvwprintw(help,3,1," Attacking is accomplished by moving into enemy territory.");
X      mvwprintw(help,4,1,"r: recruit troops (location): location must be town or city");
X      mvwprintw(help,5,1,"c: construct city (location): location must be town");
X      mvwprintw(help,6,1,"d: destroy (location): location must be city or town");
X      mvwprintw(help,7,1,"q: quit game.");
X      mvwprintw(help,8,1,"Where parentheses appear above, you are expected to move");
X      mvwprintw(help,9,1,"the cursor with the standard hack/ vi/ moria movement keys:");
X      mvwprintw(help,10,1,"h left, l right, j down, k up.");
X      mvwprintw(help,11,1,"Select the location by pressing the space bar or RETURN.");
X      mvwprintw(help,12,1,"t: tell player (player letter) followed by message.");
X      mvwprintw(help,13,1,"Enter a '#' sign instead of a letter to tell all.");
X      mvwprintw(help,14,1,"M: (capitalized): redraw Messy screen.");
X      mvwprintw(help,15,1,"Press any key now to continue play.");
X      wrefresh(help);
X      inputchar=getch();
X      werase(help);
X      delwin(help);
X      touchwin(mapw);
X      wrefresh(mapw);
X      break; 
X      case 'M':
X      restorescreen();
X      break;
X      case 'm':
X      promptplayer("Move: From? ");
X      inputstate=_CURSORM1;
X      displayhex(cursorx,cursory);
X      break;
X      case 'r':
X      promptplayer("Recruit: at what location? ");
X      inputstate=_CURSORRECRUIT;
X      displayhex(cursorx,cursory);
X      break;
X      case 'c':
X      promptplayer("Construct city: at what location? ");
X      inputstate=_CURSORCONSTRUCT;
X      displayhex(cursorx,cursory);
X      break;
X      case 'd':
X      promptplayer("Destroy city or town: at what location? ");
X      inputstate=_CURSORDESTROY;
X      displayhex(cursorx,cursory);
X      break;
X      case 'q':
X      outputline[0]=_QUIT;
X      outputline[1]=NULL;
X      if (players[playernumber].live) {
X        promptplayer("Quit: confirm (y/n)? ");
X        inputchar=getch();
X        if ((inputchar!='y') && (inputchar!='Y')) {
X          tellplayer("Cancelled.\n");
X          break;
X        }
X      }
X      done=3;
X      write(playerFd,outputline,strlen(outputline)+1);
X      break;
X      case 't':
X      promptplayer("Tell: whom and what? ");
X      inputstate=_CURSORTELL;
X     }
X    }
X    break;
X    case _CURSORM3:
X    case _CURSORTELL:
X     switch(inputchar) { 
X      case '\b':
X        if (inputpos>0) {
X          inputpos--;
X          move(19,promptoffset+inputpos);
X          addch(' ');
X          move(19,promptoffset+inputpos);
X          refresh();
X        }
X        break;
X      case '\n':
X        inputline[inputpos]=NULL;
X        move(19,0);
X        inputpos=0;
X        refresh();
X        tellplayer("\n");
X        switch(inputstate) {
X         case _CURSORTELL:
X         if (inputline[0]=='#') {
X           outputline[0]=_TELLALL;
X           strcpy(&outputline[1],&inputline[1]);
X           write(playerFd,outputline,strlen(outputline)+1);
X         }
X         else {
X           outputline[0]=_PRIVATE;
X           if (inputline[0]>96)
X             inputline[0]-=32;
X           if (isalpha(inputline[0])) {
X             outputline[1]=inputline[0];
X             if (!inrange(outputline[1]-64,1,totalplayers)) {
X               tellplayer("Huh?\n");
X             }
X             else {
X               strcpy(&outputline[2],&inputline[1]);
X               write(playerFd,outputline,strlen(outputline)+1);
X             }
X           }
X           else
X             tellplayer("Huh?\n"); 
X         }
X         inputstate=_CURSORCOMMAND;
X         promptplayer("Command:");
X         break;
X         case _CURSORM3:
X         outputline[0]=_MOVE;
X         outputline[1]=from.x+64;
X         outputline[2]=from.y+64;
X         outputline[3]=to.x+64;
X         outputline[4]=to.y+64;
X         packint(5,atoi(inputline)); 
X         outputline[8]=NULL;
X         write(playerFd,outputline,strlen(outputline)+1);
X         inputstate=_CURSORCOMMAND;
X         promptplayer("Command:");
X         break;
X        }  
X      default:
X        if ((inputpos+promptoffset<(COLS-1)) && (!iscntrl(inputchar))) {
X          inputline[inputpos]=inputchar;
X          move(19,promptoffset+inputpos);
X          if (inputpos==0)
X            clrtoeol();
X          addch(inputchar);
X          refresh();
X          inputpos++;
X        }
X     }
X    }
X   }
X  }
X  if (gotmessage==1) {
X    wrefresh(mapw);
X    refresh();
X  }
X } 
X /* Terminating code - needs to be called on CTRLC also. */
X endprogram();
X}
X
Xvoid waitevent()
X{
X  bcopy((char *) &events, (char *) &realevents, sizeof(events));
X  if (select(FD_SETSIZE,&realevents,NULL,NULL,NULL)<0) {
X    if (errno!=EINTR) {
X      perror("select");
X      exit(1);
X    }
X  }
X}
X
Xint waitshort(thisfd)
X  int thisfd;
X{
X  struct timeval waitTime;
X  waitTime.tv_sec=SECONDSLIMIT;
X  waitTime.tv_usec=MICROSECONDSLIMIT;
X  bcopy((char *) &events, (char *) &realevents, sizeof(events));
X  if (select(FD_SETSIZE,&realevents,NULL,NULL,&waitTime)<0) {
X    if (errno!=EINTR) {
X      perror("select");
X      exit(1);
X    }
X  }
X  if (FD_ISSET(thisfd,&realevents))
X    return 1;
X  return 0;
X}
X 
X
Xvoid promptplayer(prompt) 
X  char* prompt;
X{
X  move(19,0);
X  clrtoeol();
X  addstr(prompt);
X  promptoffset=strlen(prompt)+1;
X  refresh();
X}
X
Xvoid restorescreen() {
X  touchwin(mapw);
X  touchwin(output);
X  wrefresh(mapw);
X  wrefresh(output);
X}
X
Xvoid endprogram() {
X  outputline[0]=_DISCONNECT;
X  outputline[1]=NULL;
X  write(playerFd,outputline,strlen(outputline)+1); 
X  close(playerFd);
X  wclear(output);
X  delwin(output);
X  clear();
X  refresh();
X  endwin();
X  echo();
X  nocbreak();
X}
X
Xint inrange(x,low,high)
X  int x;
X  int low;
X  int high;
X{
X  if (x<low || x>high)
X    return 0;
X  return 1;
X}
X       
Xint getmessage(messagetype,specific)
X  char* messagetype;
X  char* specific;
X{
X  char in;
X  int pos;
X  *messagetype=0;
X  *specific=0;
X  if (!inputwaiting(playerFd)) {
X    return 0;
X  }
X  read(playerFd,messagetype,1);
X  pos=0;
X  readnullterm(playerFd,specific);
X  if (*messagetype==0)
X    return 0;
X  else
X    return 1;
X}
X
Xvoid readnullterm(fd,specific)
X  int fd;
X  char* specific;
X{
X  int done;
X  char* current;
X  current=specific;
X  done=0;
X  while (done==0) {
X    while (1) {
X      waitevent();
X      if (inputwaiting(playerFd))
X        break;
X    }
X    read(playerFd,current,1);
X    if (*current==0)
X      done=1;
X    current++;
X  }
X} 
X
Xvoid setupmap(x,y) 
X  int x;
X  int y;
X{
X  for (y=0; (y<_MAPY); y++) {
X    move(y+1,0);
X    addch(48+((y/10) % 10));
X    move(y+1,1);
X    addch(48+(y % 10));
X  }
X  for (x=0; (x<_MAPX); x++) {
X    move(0,x*xsize+5);
X    addch(65+x);
X  }
X  mapw=newwin(_MAPY+1,0,1,4);
X  for (y=0; (y<_MAPY); y++) {
X    for (x=0; (x<_MAPX); x++) {
X      map[x][y].terrain=7;
X      map[x][y].troops=0;
X      map[x][y].population=0;
X      map[x][y].owner=0;
X      map[x][y].lastuse=0;
X      displayhex(x,y);
X    }
X  }
X  refresh();
X  wrefresh(mapw);
X}
X
Xvoid flashhex(x,y)
X  int x;
X  int y;
X{
X  wmove(mapw,y,x*xsize+1);
X  waddch(mapw,"*");
X  wrefresh(mapw);
X  msleep(1000);
X  wmove(mapw,y,x*xsize+1);
X  waddch(mapw,terrainimage[map[x][y].terrain]);
X} 
X
Xvoid displayhex(x,y)
X  int x;
X  int y;
X{
X  int redraw;
X  redraw=0;
X  if ((x==cursorx) && (y==cursory))  {
X    wstandout(mapw);
X    redraw=1;
X  }
X  else
X    wstandend(mapw);
X  wmove(mapw,y,x*xsize);
X  if (map[x][y].owner>0) 
X    waddch(mapw,96+map[x][y].owner);
X  else 
X    waddch(mapw,' ');
X  wmove(mapw,y,x*xsize+1);
X  waddch(mapw,terrainimage[map[x][y].terrain]);
X  if (((map[x][y].terrain!=4) || map[x][y].owner==playernumber)
X      && (map[x][y].troops>0)) {
X    wmove(mapw,y,x*xsize+2);
X    waddch(mapw,48+((map[x][y].troops/10) % 10));
X    wmove(mapw,y,x*xsize+3);
X    waddch(mapw,48+(map[x][y].troops % 10)); 
X  }
X  else {
X    wmove(mapw,y,x*xsize+2);
X    waddstr(mapw,"  ");
X  }
X  wstandend(mapw);
X  if (redraw==1)
X    wrefresh(mapw);
X  wmove(mapw,y,x*xsize+1);
X}
X  
Xint connectstream()
X{
X  int s;
X  struct sockaddr_in saddr;
X  struct in_addr host_address;
X  if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
X    perror("socket create");
X    return(0);
X  }
X
X  saddr.sin_family = AF_INET;
X  if (!get_host_address(host,&host_address)) {
X    printf("Bad or missing server address.\n");
X    exit(1);
X  }
X  bcopy(&host_address,&saddr.sin_addr,sizeof(struct in_addr));
X  /* saddr.sin_addr.s_addr = htonl(inet_addr(host)); Old method */
X  saddr.sin_port = htons(port);
X
X  if (connect(s, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in)) < 0) {
X    perror("connect");
X    return(0);
X  }
X  FD_SET(s,&events);
X  playerFd=s;
X  return(1);
X}
X
X
X
Xint inputwaiting(sfd) 
X  int sfd;
X{
X  if (FD_ISSET(sfd,&realevents))
X    return 1;
X  else
X    return 0;
X}
X 
X/*  int errrec;
X  fd_set readfds;
X  struct timeval waitTime;
X  waitTime.tv_sec=SECONDSLIMIT;
X  waitTime.tv_usec=MICROSECONDSLIMIT;
X  bcopy((char *) sfdset, (char *) &readfds,sizeof(*sfdset));
X  if (select(FD_SETSIZE,&readfds,NULL,NULL,&waitTime)<0) {
X    if (errno!=EINTR) { 
X    perror("select");
X    }
X    return 0;
X  }
X  if (FD_ISSET(sfd,&readfds))
X    return 1;
X  else
X    return 0;
X}
X
X*/
X
Xvoid tellplayer(this)
X  char* this;
X{
X  wprintw(output,this);
X  wrefresh(output);  
X}
X
Xvoid displayplayer(theplayer)
X  int theplayer;
X{
X  static char outputline[100];
X  move(18,theplayer*10-9);
X  if (players[theplayer].live==1) 
X    addstr(sprintf(outputline,"%c:%d ,%d   ",theplayer+64,
X                   players[theplayer].hexes,players[theplayer].action));
X  else addstr(sprintf(outputline,"%c: Dead  ",theplayer+64));
X}
X
X/* get_host_address: borrowed with appreciation from Tinytalk. Does a nice
X   job of getting around the various stupidities of the inetaddr routine,
X   et cetera. */
X
Xint get_host_address(name, addr)        /* Get a host address. */
X  register char *name;
X  register struct in_addr *addr;
X{
X  struct hostent *blob;
X  union {                               /* %#@!%!@%#!@ idiot who designed */
X    long signed_thingy;                 /* the inetaddr routine.... */
X    unsigned long unsigned_thingy;
X  } thingy;
X
X  if (*name == '\0') {
X    fprintf(stderr, "%% No host address specified.\n");
X    return (0);
X  }
X
X  if ((*name >= '0') && (*name <= '9')) {       /* IP address. */
X    addr->s_addr = inet_addr(name);
X    thingy.unsigned_thingy = addr->s_addr;
X    if (thingy.signed_thingy == -1) {
X      fprintf(stderr, "%% Couldn't find host %s .\n", name);
X      return (0);
X    }
X  }
X  else {                                /* Host name. */
X    blob = gethostbyname(name);
X
X    if (blob == NULL) {
X      fprintf(stderr, "%% Couldn't find host %s .\n", name);
X      return (0);
X    }
X
X    bcopy(blob->h_addr, addr, sizeof(struct in_addr));
X  }
X
X  return (1);                           /* Success. */
X}
X
X
END_OF_FILE
if test 17664 -ne `wc -c <'btclient.c'`; then
    echo shar: \"'btclient.c'\" unpacked with wrong size!
fi
# end of 'btclient.c'
fi
if test -f 'btclient.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'btclient.h'\"
else
echo shar: Extracting \"'btclient.h'\" \(984 characters\)
sed "s/^X//" >'btclient.h' <<'END_OF_FILE'
X#define _CURSORM1 1
X#define _CURSORM2 2
X#define _CURSORM3 3
X#define _CURSORRECRUIT 4
X#define _CURSORTELL 5
X#define _CURSORCOMMAND 6
X#define _CURSORCONSTRUCT 7
X#define _CURSORDESTROY 8
X
X#define SECONDSLIMIT 0L
X#define MICROSECONDSLIMIT 100000L
X
Xhex map[_MAPX][_MAPY];
Xplayer players[20];
Xint playernumber;
Xint totalplayers;
Xint currenttime;
Xint living;
Xint port;
Xint playerFd;
Xint promptoffset;
Xfd_set events;
Xfd_set realevents;
Xint inputstate;
Xchar* host;
X
Xint connectstream();
Xint inputwaiting();
Xvoid waitevent();
Xint waitshort();
Xvoid setupmap();
Xvoid displayhex();
Xvoid flashhex();
Xvoid tellplayer();
Xvoid displayplayer();
Xvoid readnullterm();
Xvoid handlecommand();
Xvoid endprogram();
Xvoid inputfork();
Xvoid restorescreen();
Xint inrange();
Xvoid promptplayer();
Xint cursorx;
Xint cursory;
XWINDOW* output;
XWINDOW* mapw;
Xint getmessage();
Xint terrainimage[]= { ' ','.','T','M','F','C','S','^' };
Xchar specificspace[256];
Xchar outputline[256];
Xchar* specific;
Xint inputfd;
Xint xsize;
X
END_OF_FILE
if test 984 -ne `wc -c <'btclient.h'`; then
    echo shar: \"'btclient.h'\" unpacked with wrong size!
fi
# end of 'btclient.h'
fi
if test -f 'btserver.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'btserver.c'\"
else
echo shar: Extracting \"'btserver.c'\" \(17782 characters\)
sed "s/^X//" >'btserver.c' <<'END_OF_FILE'
X#include <math.h>
X#include <stdio.h>
X#include <varargs.h>
X#include <sys/types.h>
X#include <sys/time.h>
X#include <signal.h>
X
X#include "bt.h"
X#include "btserver.h"
X#include "interface.h"
X#include "pack.h"
X#include "msleep.h"
X
Xint terrainfrom[] = { 0, 0, 10, 10,  5,  5, -10, 0 };
Xint terrainto[]=    { 0, 0,-10,-10,-10,-15, 10, 0};
Xint terrainpopulation[] = { 0, 100, 300, 50, 75, 400, 0, 0};
X
Xint main(argc,argv)
X  int argc;
X  char* argv[];
X{
X  char requesttype;
X  location from,to;
X  int number;
X  int x,y;
X  int flag;
X  int portnumber;
X  long lasttime;
X  long newtime;
X  char specificspace[256];
X  char *specific;
X  int offset;
X  printf("Broken Throne server program launching...\n");
X  portnumber=2727;
X  if (argc>1) {
X    totalplayers=atoi(argv[1]);
X    if (argc>2) {
X      portnumber=atoi(argv[2]);
X      if (portnumber<1000 || portnumber>9999) {
X        printf("Port number must be between 1000 and 9999.\n");
X        exit(1);
X      }
X    }
X  }
X  else {
X    printf("Usage: btserver #ofplayers <port# (optional)>\n");
X    exit(1);
X  }
X  if (totalplayers<1 || totalplayers>_MAXPLAYERS) {
X     printf("Number of players should be between 1 and %d.\n",_MAXPLAYERS);
X     exit(1);
X  }
X  currenttime=100;
X  living=totalplayers;
X  specific=specificspace;
X  time(&lasttime);
X  srand(lasttime % 1024); /* Get a new random # seed */
X  setupinterface(portnumber);
X  signal(SIGTERM,endprogram);
X  broadcast(_STARTUP,(void*)NULL);
X  setupmap();
X  while (living>0) {
X    msleep(100000);
X    while (getrequest(&currentplayer,&requesttype,specific)) {
X      offset=0;
X      switch ((int) requesttype) {
X        case _DISCONNECT:
X          if (players[currentplayer].live==1) 
X            killplayer(currentplayer);
X          players[currentplayer].live=0;
X          disconnect(currentplayer);
X          break;  
X        case _MOVE:
X          striplocation(&from,specific,&offset);
X          striplocation(&to,specific,&offset);
X          stripint(&number,specific,&offset);
X          move(from,to,number);
X          break;
X        case _RECRUIT:
X          striplocation(&from,specific,&offset);
X          recruit(from);
X          break;
X        case _CONSTRUCT:
X          striplocation(&from,specific,&offset);
X          construct(from);
X          break;
X        case _DESTROY:
X          striplocation(&from,specific,&offset);
X          destroy(from);
X          break;
X        case _QUIT:
X          killplayer(currentplayer);
X          break;
X        case _PRIVATE:
X          sprintf(playertext,"%c: %s",currentplayer+64,&specific[1]);
X          tellplayer(specific[0]-64); 
X          break;
X        case _TELLALL:
X          sprintf(playertext,"%c to ALL: %s",currentplayer+64,specific);
X          broadcast(_TEXT,(void*) playertext);
X      }
X    }
X    time(&newtime);
X    if (newtime-lasttime>=_SPEED) {
X      currenttime++;
X      broadcast(_ACTION,(void*)NULL);
X      for (number=1; (number<=totalplayers); number++) 
X        players[number].action+=(2+players[number].citadels);
X      lasttime=newtime;
X    }
X  }     
X  printf("Ending\n");
X  endprogram();
X  /* printf("Broken Throne: Game Over.\n"); */
X  }
X
Xvoid endprogram() {
X  broadcast(_END,(void*)NULL);
X  shutdowninterface();
X  exit(0);
X}
X
Xint cost (x,y)
X  int x;
X  int y;
X{
X  if (map[x][y].owner==currentplayer) 
X    return 2;
X  else
X    return 4;
X}
X  
Xvoid move(from,to,number) 
X  location from;
X  location to;
X  int number;
X{
X  int owner;
X  int enemystrength;
X  int unitsa;
X  int unitsb;
X  int numberold;
X  int stragglers;
X  int marchers;
X  int weight;
X  int x,y;
X  if (number==0) {
X    sprintf(playertext,"Ignored (no troops moved).");
X    tellcurrentplayer();
X    return;
X  }
X  if (abs(from.x-to.x)>1 || abs(from.y-to.y)>1) {
X    sprintf(playertext,"That destination is out of range.\n");
X    tellcurrentplayer();
X    return;
X  }
X  if (map[from.x][from.y].owner==currentplayer)
X    if (players[currentplayer].action>=number*cost(to.x,to.y))
X      if (map[from.x][from.y].troops>=number)
X        map[from.x][from.y].troops-=number;
X      else {
X        sprintf(playertext,"You do not have enough troops in that hex.\n");
X        tellcurrentplayer();
X        return; }
X    else {
X      sprintf(playertext,"You don't have enough action points.\n");
X      tellcurrentplayer();
X      return; }
X  else {
X    sprintf(playertext,"You don't control that hex!\n");
X    tellcurrentplayer();
X    return; }
X    players[currentplayer].action-=number*cost(to.x,to.y);
X  switch (map[to.x][to.y].terrain) {
X    case 1:
X      break;
X    case 2:
X      break;
X    case 3: 
X      stragglers=0;
X      for (marchers=1; (marchers<=number); marchers++) {
X        if ((rand() & 512)==0)
X          stragglers++;
X      }
X      map[from.x][from.y].troops+=stragglers;
X      if (stragglers==number) {
X        sprintf(playertext,"No troops made it through the mountains.");
X        tellcurrentplayer();
X        broadcast(_PLAYERSTATUS,(void*)&currentplayer);
X        return; }
X      else if (stragglers>=0) {
X        sprintf(playertext,"%d of your armies have not crossed.",stragglers);
X        tellcurrentplayer();
X        number-=stragglers;
X      }
X      
X      break;
X    case 4:
X      break;
X    case 5:
X      break;
X    case 6:
X      if ((rand() & 512)==0) {
X        players[currentplayer].troops--;
X        if (number==1) {
X          sprintf(playertext,"Your army was sucked into the swamp.");
X          tellcurrentplayer();
X          number=0;
X          break; 
X        }
X        else {
X          sprintf(playertext,"One of your units was lost in the swamp.");
X          tellcurrentplayer();
X          number--;
X          break;
X        }
X      }
X      break;
X    case 7: 
X      sprintf(playertext,"Crags are impassable.");
X      tellcurrentplayer();
X      map[from.x][from.y].troops+=number;
X      players[currentplayer].action+=number*cost(to.x,to.y);
X      return;
X  }
X  owner=map[to.x][to.y].owner;
X  if (owner!=currentplayer) {
X    if (owner!=0)
X      broadcast(_LOOK,(void*)&to);
X    enemystrength=map[to.x][to.y].troops;
X    if (enemystrength==0) {
X      sprintf(playertext,"The territory was overrun."); 
X      tellcurrentplayer();
X      if (map[to.x][to.y].terrain==5) {
X        players[owner].citadels--;
X        if (players[owner].citadels==0) {
X          sprintf(playertext,"Your last citadel was seized!");
X          tellplayer(owner);
X          killplayer(owner);
X        }
X        players[currentplayer].citadels++;
X      }
X      map[to.x][to.y].owner=currentplayer;
X      map[to.x][to.y].troops=number;
X      players[currentplayer].population+=map[to.x][to.y].population;
X      players[currentplayer].hexes++;
X      if (owner>0) {
X        players[owner].hexes--;
X        broadcast(_PLAYERSTATUS,(void*)&owner);
X      } 
X      broadcast(_PLAYERSTATUS,(void*)&currentplayer);
X      broadcast(_HEXSTATUS,(void*)&to);
X      broadcast(_HEXSTATUS,(void*)&from);
X    } 
X    else {
X      sprintf(playertext,"You have been attacked by %s at %s with %d troops.",
X                 nameplayer(currentplayer),
X                 namelocation(to),number);
X      tellplayer(owner);
X      weight=50;
X      weight+=terrainfrom[map[from.x][from.y].terrain];
X      weight+=terrainto[map[to.x][to.y].terrain];
X      unitsa=number*5;
X      unitsb=enemystrength*5;
X      while ((unitsa>0) && (unitsb>0)) {
X      if (weight>((rand()/32) % 100)) { /* In search of good random #s! */
X        unitsa--;
X        }
X      else {
X        unitsb--;
X      }
X    }
X    numberold=number;
X    number=((unitsa+4)/5);
X    enemystrength=((unitsb+4)/5);
X    players[currentplayer].troops=numberold-number;
X      if (unitsa==0) {
X        sprintf(playertext,"Your forces were destroyed.");
X        tellcurrentplayer();
X        sprintf(playertext,
X          "You destroyed the enemy forces. %d of your armies survived.",
X          enemystrength);
X        tellplayer(owner);
X        map[to.x][to.y].troops=enemystrength;
X        broadcast(_HEXSTATUS,(void*)&from);
X        broadcast(_HEXSTATUS,(void*)&to);
X        broadcast(_PLAYERSTATUS,(void*)&currentplayer);
X        broadcast(_PLAYERSTATUS,(void*)&owner);
X        }
X      else {
X        sprintf(playertext,"You liberated the territory.");
X        tellcurrentplayer();
X        if (owner!=0) {
X          sprintf(playertext,"The territory was seized. %d armies occupied the territory.",number);
X          tellplayer(owner);
X          players[owner].population-=
X            map[to.x][to.y].population;
X          if (map[to.x][to.y].terrain==5) {
X            players[owner].citadels--;
X            if (players[owner].citadels==0) {
X              sprintf(playertext,"Your last citadel has been eliminated!");
X              tellplayer(owner);
X              killplayer(owner);
X            }
X            players[currentplayer].citadels++;
X          }
X        map[to.x][to.y].owner=currentplayer;
X        map[to.x][to.y].troops=number;
X        players[currentplayer].hexes++;
X        players[owner].hexes--;
X        players[currentplayer].population+=map[to.x][to.y].population;
X        broadcast(_PLAYERSTATUS,(void*)&currentplayer);
X        broadcast(_PLAYERSTATUS,(void*)&owner);
X        broadcast(_HEXSTATUS,(void*)&from);
X        broadcast(_HEXSTATUS,(void*)&to);
X        }
X      }
X    }
X  }
X  else {
X    marchers=number+map[to.x][to.y].troops;
X    if (marchers>99) {
X      sprintf(playertext,"Up to 99 troops per territory.");
X      tellcurrentplayer();
X      map[to.x][to.y].troops=99;
X      players[currentplayer].action+=(marchers-99);
X      map[from.x][from.y].troops+=(marchers-99);
X      broadcast(_HEXSTATUS,(void*)&from);
X      broadcast(_HEXSTATUS,(void*)&to);
X      broadcast(_PLAYERSTATUS,(void*)&currentplayer);
X      return; }
X    else {
X      map[to.x][to.y].troops=marchers;
X      broadcast(_HEXSTATUS,(void*)&from);
X      broadcast(_HEXSTATUS,(void*)&to);
X      broadcast(_PLAYERSTATUS,(void*)&currentplayer);
X      return;
X    }
X  }
X}
X
Xint legal(x,y)
X  int x;
X  int y;
X{
X  if (x>=0 && x<_MAPX && y>=0 && y<_MAPY)
X    return 1;
X  else
X    return 0;
X}
X
Xvoid construct(at)
X  location at;
X{
X  if (map[at.x][at.y].owner!=currentplayer) {
X    sprintf(playertext,"You don't own that territory!");
X    tellcurrentplayer();
X    return;
X  }
X  if (map[at.x][at.y].terrain!=2) {
X    sprintf(playertext,"That territory is not a town.");
X    tellcurrentplayer();
X    return;
X  }
X  if (players[currentplayer].action<_CITYCOST) {
X    sprintf(playertext,"You don't have enough action points!");
X    tellcurrentplayer();
X    return;
X  }
X  map[at.x][at.y].terrain=5;
X  players[currentplayer].citadels++;
X  players[currentplayer].action-=_CITYCOST;
X  players[currentplayer].population-=terrainpopulation[2];
X  players[currentplayer].population+=terrainpopulation[5];
X  broadcast(_HEXSTATUS,(void*)&at);
X  broadcast(_PLAYERSTATUS,(void*)&currentplayer);
X  sprintf(playertext,"You have constructed a new city.");
X  tellcurrentplayer();
X}   
X
Xvoid destroy(at)
X  location at; {
X  if ((players[currentplayer].citadels==1) && (map[at.x][at.y].terrain==5)) {
X    sprintf(playertext,"You cannot destroy your last city.");
X    tellcurrentplayer();
X    return;
X  }
X  if (map[at.x][at.y].owner!=currentplayer) {
X   sprintf(playertext,"You don't control that territory!");
X   tellcurrentplayer();
X   return;
X  }
X  if ((map[at.x][at.y].terrain!=2) && (map[at.x][at.y].terrain!=5)) {
X   sprintf(playertext,"That territory is not a town or city!");
X   tellcurrentplayer();
X   return;
X  }
X  if (map[at.x][at.y].troops==0) {
X   sprintf(playertext,"You must have forces in the territory!");
X   tellcurrentplayer();
X   return;
X  }
X  if (players[currentplayer].action<_DESTROYCOST) {
X   sprintf(playertext,"You need at least %d action points.",_DESTROYCOST);
X   tellcurrentplayer();
X   return;
X  }
X  players[currentplayer].action-=_DESTROYCOST;
X  if (map[at.x][at.y].terrain==5)
X    players[currentplayer].citadels--;
X  players[currentplayer].population-=map[at.x][at.y].population;
X  map[at.x][at.y].terrain=1;
X  players[currentplayer].population+=terrainpopulation[1];
X  broadcast(_HEXSTATUS,(void*)&at);
X  broadcast(_PLAYERSTATUS,(void*)&currentplayer);
X  sprintf(playertext,"You have destroyed the territory.");
X  tellcurrentplayer();
X} 
Xvoid recruit(at)
X  location at;
X{
X  int chance;
X  int population;
X  int x,y;
X  int recruits;
X  if (map[at.x][at.y].owner!=currentplayer) {
X    sprintf(playertext,"You don't own that territory!");
X    tellcurrentplayer();
X    return;
X  }
X  if ((map[at.x][at.y].terrain!=2) && (map[at.x][at.y].terrain!=5)) {
X    sprintf(playertext,"That territory is not a town or citadel.");
X    tellcurrentplayer();
X    return;
X  }
X  chance=currenttime-map[at.x][at.y].lastuse;
X  map[at.x][at.y].lastuse=currenttime;
X  population=0;
X  if (chance>((rand() / 100) % 20)) {
X    for (y=(-1); (y<=1); y++) {
X      for (x=(-1); (x<=1); x++) {
X        if (legal(at.x+x,at.y+y)) {
X          if ((((map[at.x][at.y].terrain!=2) && (map[at.x][at.y].terrain!=5)) ||
X              ((x==0) && (y==0))) && ((map[at.x][at.y].owner==currentplayer) ||
X                                      (map[at.x][at.y].owner==0)))
X          population+=map[at.x+x][at.y+y].population;
X        }
X      }
X    }
X    recruits=population/(100*((rand()/100) % 5+2))+1;
X    sprintf(playertext,"You recruited %d troops.",recruits);
X    tellcurrentplayer();
X    players[currentplayer].troops+=recruits;
X    map[at.x][at.y].troops+=recruits;
X    if (map[at.x][at.y].troops>99) {
X      map[at.x][at.y].troops=99;
X      sprintf(playertext,"There is a maximum of 99 troops per territory.");
X      tellcurrentplayer();
X    }
X    broadcast(_PLAYERSTATUS,(void*)&currentplayer);
X    broadcast(_HEXSTATUS,(void*)&at);
X    return;
X  }
X  else {
X    sprintf(playertext,"Recruitment failed; the territory is under too much stress.");
X    tellcurrentplayer();
X    return;
X  }
X}
X
Xvoid setupmap() {
X/* Relatively safe, but if there isn't room for the number of players
X   the game may lock up. */
X  int altitude[_MAPX][_MAPY];
X  int newaltitude[_MAPX][_MAPY];
X  int x,y,subx,suby;
X  int seed;
X  int seedcomp;
X  int acceptable;
X  int full;
X  int totalaltitude;
X  location newhex;
X  int placement;
X  full=0;
X  sprintf(playertext,"Generating the map...");
X  broadcast(_TEXT,(void*)playertext);
X  for (y=0; (y<_MAPY); y++) {
X    for (x=0; (x<_MAPX); x++) {
X      altitude[x][y]=0;
X      map[x][y].terrain=3;
X      map[x][y].population=terrainpopulation[3];
X      map[x][y].lastuse=0;
X      map[x][y].troops=0;
X      map[x][y].owner=0;
X      newhex.x=x;
X      newhex.y=y;
X      broadcast(_HEXSTATUS,(void*)&newhex);
X    }
X  }
X  for (seed=1; (seed<=totalplayers); seed++) {
X    x=((rand()/32) % (_MAPX-4))+2;
X    y=((rand()/32) % (_MAPY-4))+2;
X    while(1) {
X      acceptable=1;
X      x++;
X      if (x>_MAPX-2) {
X        x=3;
X        y++;
X        if (y>_MAPY-2) {
X          y=1;
X        }
X      }
X      for (seedcomp=1; (seedcomp<seed); seedcomp++) {
X        if (abs(x-players[seedcomp].start.x)+
X            abs(y-players[seedcomp].start.y)<5) 
X            acceptable=0;
X      }
X      if (acceptable==1)
X        break;
X    }
X    map[x][y].terrain=5;
X    altitude[x][y]=100;
X    map[x][y].population=terrainpopulation[5];
X    map[x][y].owner=seed;
X    players[seed].start.x=x;
X    players[seed].start.y=y;
X    players[seed].population=terrainpopulation[5];
X    players[seed].live=1;
X    players[seed].hexes++;
X    players[seed].citadels=1;
X    broadcast(_HEXSTATUS,(void*)&players[seed].start);
X    broadcast(_PLAYERSTATUS,(void*)&seed);
X    full++;
X  }
X  /* Now sweep, dropping altitudes of new hexes based on adjacents.
X     Don't let altitude drop to zero; increment full as each hex
X     is filled. At 90% full or so, call it quits. */   
X  
X  acceptable=(_MAPY*_MAPX)*9/10;
X  while (full<acceptable) { 
X    for (y=0; (y<_MAPY); y++) {
X      for (x=0; (x<_MAPX); x++) {
X        newaltitude[x][y]=altitude[x][y];
X        if (altitude[x][y]==0) {
X          totalaltitude=0;
X          for (suby=(-1); (suby<=1); suby++) {
X            for (subx=(-1); (subx<=1); subx++) {   
X              if (legal(x+subx,y+suby)) 
X                totalaltitude+=altitude[x+subx][y+suby];
X            }
X          }
X          if (totalaltitude>0) {
X            newaltitude[x][y]=totalaltitude/2+((rand()/100) % 20);
X            map[x][y].lastuse=0;
X            map[x][y].troops=0;
X            map[x][y].owner=0;
X            placement=((rand()/100) % 100)*(newaltitude[x][y])/100;
X            if (placement>90) {
X              map[x][y].terrain=7; }
X            else if (placement>70) {
X              map[x][y].terrain=3; }
X            else if (placement>50) {
X              map[x][y].terrain=4; }
X            else if (placement>40) {
X              map[x][y].terrain=2; }
X            else if (placement>20) {
X              map[x][y].terrain=1; }
X            else {
X              map[x][y].terrain=6; }  
X            map[x][y].population=terrainpopulation[map[x][y].terrain]; 
X            newhex.x=x;
X            newhex.y=y;
X            broadcast(_HEXSTATUS,(void*)&newhex);
X            full++;
X          }
X        }
X      }
X    }
X    for (y=0; (y<_MAPY); y++) {
X      for (x=0; (x<_MAPX); x++) {
X        altitude[x][y]=newaltitude[x][y];
X      }
X    }
X  }
X}
X 
Xvoid tellcurrentplayer()
X{
X  tellplayer(currentplayer);
X} 
X
Xvoid killplayer(owner)
X  int owner;
X{
X  int x,y;
X  broadcast(_PLAYERDEAD,(void*)&owner);
X  for (y=0; (y<_MAPY); y++) {
X    for (x=0; (x<_MAPX); x++) {
X      if (map[x][y].owner==owner) {
X        map[x][y].owner=0;
X        map[x][y].troops=0;
X      }
X    }
X  }
X  players[owner].population=0;
X  players[owner].troops=0;
X  players[owner].live=2;
X  living--; 
X}
X 
Xchar* namelocation(at)
X  location at;
X{
X  static char namespace[20];
X  sprintf(namespace,"[%c%d]",65+at.x,at.y);
X  return &namespace[0];
X}
X
Xchar* nameplayer(thisid)
X  int thisid;
X{
X  static char namespace[20];
X  namespace[0]=' ';
X  namespace[1]=64+thisid;
X  namespace[2]=' ';
X  namespace[3]=NULL;
X  return &namespace[0];
X}
X
END_OF_FILE
if test 17782 -ne `wc -c <'btserver.c'`; then
    echo shar: \"'btserver.c'\" unpacked with wrong size!
fi
# end of 'btserver.c'
fi
if test -f 'btserver.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'btserver.h'\"
else
echo shar: Extracting \"'btserver.h'\" \(1140 characters\)
sed "s/^X//" >'btserver.h' <<'END_OF_FILE'
X/* bt.h- declarations and controlling constants for Broken Throne.
X   Copyright 1990 by Tom Boutell. */
X
X#define _MAPX 16
X#define _MAPY 16
X#define _SPEED 5
X#define _MAXPLAYERS 6
X#define _CITYCOST 90
X#define _DESTROYCOST 10
X
X#define _HEXSTATUS 65
X#define _PLAYERSTATUS 66
X#define _PLAYERDEAD 67
X#define _ACTION 68
X#define _TEXT 69
X#define _STARTUP 70
X#define _END 71
X#define _YOUARE 72
X
X#define _MOVE 65
X#define _RECRUIT 66
X#define _QUIT 67
X#define _PRIVATE 68
X#define _TELLALL 69
X#define _DISCONNECT 70
X#define _CONSTRUCT 71
X#define _DESTROY 72
X
Xhex map[_MAPX][_MAPY];
Xplayer players[20];
Xint currentplayer;
Xint totalplayers;
Xint currenttime;
Xint living;
X
X/* Beware terrain type zero... existing to keep C happy. */
X
Xvoid move(); /* location from,location to,int number */
Xvoid tellcurrentplayer(); /* char* fmt, variable args */
Xvoid setupmap();
Xvoid recruit(); /* location at */
Xint legal(); /* int x,int y */
Xvoid killplayer(); /* int player */
Xchar* namelocation(); /* location at */
Xchar* nameplayer(); /* int player */
Xint cost(); /* int x,y */
Xvoid destroy(); /* location at */
Xvoid construct(); /* location at */
Xvoid endprogram();
END_OF_FILE
if test 1140 -ne `wc -c <'btserver.h'`; then
    echo shar: \"'btserver.h'\" unpacked with wrong size!
fi
# end of 'btserver.h'
fi
if test -f 'changes' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'changes'\"
else
echo shar: Extracting \"'changes'\" \(67 characters\)
sed "s/^X//" >'changes' <<'END_OF_FILE'
XSee the readme file for a description of changes in version 1.10.
X
END_OF_FILE
if test 67 -ne `wc -c <'changes'`; then
    echo shar: \"'changes'\" unpacked with wrong size!
fi
# end of 'changes'
fi
if test -f 'interface.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'interface.h'\"
else
echo shar: Extracting \"'interface.h'\" \(601 characters\)
sed "s/^X//" >'interface.h' <<'END_OF_FILE'
Xvoid setupinterface(); /* int portnumber */
Xvoid shutdowninterface();
Xint getrequest();
X     /* int* player,char* requesttype,char* specific */
Xvoid broadcast(); /* char messagetype,void* details */
Xvoid tellplayer(); /* int player */
Xint init_socket(); /* int port */
Xint disconnect_all();
Xint shutdown();
Xchar *hostfrom(); /* int i */
Xchar *hostname();
Xint new_player(); /* int wait */
Xint disconnect(); /* int id */
Xchar *read_player(); /* int playerFd */
Xint w_p(); /* int id, char* str, int len */
Xchar* playertext; /* String to sprinted by tellplayer */
Xvoid broken(); /* Broken pipe handler */
END_OF_FILE
if test 601 -ne `wc -c <'interface.h'`; then
    echo shar: \"'interface.h'\" unpacked with wrong size!
fi
# end of 'interface.h'
fi
if test -f 'msleep.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'msleep.c'\"
else
echo shar: Extracting \"'msleep.c'\" \(203 characters\)
sed "s/^X//" >'msleep.c' <<'END_OF_FILE'
X
X#include <sys/types.h>
X#include <sys/time.h>
X
X#define NULL 0
X
Xvoid msleep(n)
X  long n;
X{
X  struct timeval sleept;
X  sleept.tv_sec=0;
X  sleept.tv_usec=n;
X  select(FD_SETSIZE,NULL,NULL,NULL,&sleept);
X}
X 
END_OF_FILE
if test 203 -ne `wc -c <'msleep.c'`; then
    echo shar: \"'msleep.c'\" unpacked with wrong size!
fi
# end of 'msleep.c'
fi
if test -f 'msleep.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'msleep.h'\"
else
echo shar: Extracting \"'msleep.h'\" \(224 characters\)
sed "s/^X//" >'msleep.h' <<'END_OF_FILE'
X/* msleep: A portable routine to sleep for n microseconds.
X   By Tom Boutell 2/2/91, from various suggestions from netfolk.
X   (One fellow sent me such a routine earlier, which I have lost.)
X*/
X
Xvoid msleep(/* int n */);   
END_OF_FILE
if test 224 -ne `wc -c <'msleep.h'`; then
    echo shar: \"'msleep.h'\" unpacked with wrong size!
fi
# end of 'msleep.h'
fi
if test -f 'pack.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'pack.c'\"
else
echo shar: Extracting \"'pack.c'\" \(757 characters\)
sed "s/^X//" >'pack.c' <<'END_OF_FILE'
X/* pack.c: Functions for packing of Broken Throne data for transmission
X   via sockets. Copyright 1990, Tom Boutell. */
X
X#include "bt.h"
X#include "pack.h"
X
Xvoid packint(offset,contents)
X  int offset;
X  int contents; 
X{
X  outputline[offset]=(contents % 32)+32;
X  contents/=32;
X  outputline[offset+1]=(contents % 32)+32;
X  contents/=32;
X  outputline[offset+2]=(contents % 32)+32;
X}
X
Xvoid striplocation(at,specific,offset)
X  location* at;
X  char* specific;
X  int* offset;
X{
X  at->x=specific[*offset]-64;
X  at->y=specific[*offset+1]-64;
X  *offset+=2;
X}
X
Xvoid stripint(number,specific,offset)
X  int* number;
X  char* specific;
X  int* offset;
X{
X  *number=specific[*offset]-32+(specific[*offset+1]-32)*32+
X          (specific[*offset+2]-32)*1024;
X  *offset+=3;
X}
X
X
END_OF_FILE
if test 757 -ne `wc -c <'pack.c'`; then
    echo shar: \"'pack.c'\" unpacked with wrong size!
fi
# end of 'pack.c'
fi
if test -f 'pack.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'pack.h'\"
else
echo shar: Extracting \"'pack.h'\" \(328 characters\)
sed "s/^X//" >'pack.h' <<'END_OF_FILE'
X/* pack.h: function declarations for packing data for socket
X   transmission under Broken Throne. (C) 1990 Tom Boutell. */
X
Xextern char outputline[];
X
Xvoid packint(); /* int offset,int contents */
Xvoid striplocation(); /* location* at,char* specific,int* offset */
Xvoid stripint(); /* int* number,char* specific,int* offset */
X
END_OF_FILE
if test 328 -ne `wc -c <'pack.h'`; then
    echo shar: \"'pack.h'\" unpacked with wrong size!
fi
# end of 'pack.h'
fi
echo shar: End of archive 1 \(of 2\).
cp /dev/null ark1isdone
MISSING=""
for I in 1 2 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked both archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0