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

billr@saab.CNA.TEK.COM (Bill Randle) (11/22/90)

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

[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 bt.c bt.doc bt.h client.c client.h
#   interface.h pack.c pack.h
# Wrapped by billr@saab on Wed Nov 21 12:13:25 1990
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'\" \(1118 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
XTHE BROKEN THRONE
X
XBy Tom Boutell, 11/20/90. Do whatever you want with it, provided changed
Xversions are not released without clearing them through me first. I intend
Xto 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.
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 very, very surprised. Please mail me
Xthe details (boutell@freezer.it.udel.edu).
X
XIf "make btclient" fails, I won't be so surprised. This is because the game
Xplays games with stdin to find out if there are characters waiting or not.
XThis shouldn't vary from one Unix to another but it probably will anyway.
XIf you can fix it for your machine, great; mail me what you did so I can
Xcreate a compile- time option for that machine. One possible replacement
Xapproach for System V folks is to use the nodelay() routine of Curses.
X(This routine is missing in SunOS curses. Ahrgh.) Of course, if the code
Xruns as- is, you're golden!
X
END_OF_FILE
if test 1118 -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'\" \(517 characters\)
sed "s/^X//" >'MANIFEST' <<'END_OF_FILE'
X   File Name		Archive #	Description
X-----------------------------------------------------------
X MANIFEST                   1	This shipping list
X Makefile                   2	
X README                     1	
X bt.c                       1	
X bt.doc                     1	
X bt.h                       1	
X client.c                   1	
X client.h                   1	
X interface.c                2	
X interface.h                1	
X pack.c                     1	
X pack.h                     1	
X types.h                    2	
END_OF_FILE
if test 517 -ne `wc -c <'MANIFEST'`; then
    echo shar: \"'MANIFEST'\" unpacked with wrong size!
fi
# end of 'MANIFEST'
fi
if test -f 'bt.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'bt.c'\"
else
echo shar: Extracting \"'bt.c'\" \(17223 characters\)
sed "s/^X//" >'bt.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 "types.h"
X#include "bt.h"
X#include "interface.h"
X#include "pack.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};
Xvoid endprogram();
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  long lasttime;
X  long newtime;
X  char specificspace[256];
X  char *specific;
X  int offset;
X  printf("Broken Throne server program launching...\n");
X  if (argc>1) 
X    totalplayers=atoi(argv[1]);
X  else {
X    printf("Number of players? ");
X    scanf("%d",&totalplayers); 
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();
X  signal(SIGTERM,endprogram);
X  broadcast(_STARTUP,(void*)NULL);
X  setupmap();
X  while (living>0) {
X    usleep(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 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        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      return;
X  }
X  owner=map[to.x][to.y].owner;
X  if (owner!=currentplayer) {
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      while ((number>0) && (enemystrength>0)) {
X      if (weight>((rand()/32) % 100)) { /* In search of good random #s! */
X        number--;
X        players[currentplayer].troops--;
X        }
X      else {
X        enemystrength--;
X        players[owner].troops--;
X      }
X    }
X      if (number==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) {
X    sprintf(playertext,"You cannot destroy your last city.");
X    tellcurrentplayer();
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/* Not yet safe; will keep trying by means of rand to position a
X   player indefinitely if it runs out of space or hits a numeric
X   ugly. Can be remedied by sweeping the map starting from one
X   random point. Map generator is not optimized well. */
X
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  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(player)
X  int player;
X{
X  static char namespace[20];
X  namespace[0]=' ';
X  namespace[1]=64+player;
X  namespace[2]=' ';
X  namespace[3]=NULL;
X  return &namespace[0];
X}
X
END_OF_FILE
if test 17223 -ne `wc -c <'bt.c'`; then
    echo shar: \"'bt.c'\" unpacked with wrong size!
fi
# end of 'bt.c'
fi
if test -f 'bt.doc' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'bt.doc'\"
else
echo shar: Extracting \"'bt.doc'\" \(11224 characters\)
sed "s/^X//" >'bt.doc' <<'END_OF_FILE'
XThe Broken Throne
X
XA multiplayer, realtime game of conquest for the Unix operating system
X
XBy Tom Boutell, 11/14/90
X
XPermission granted to freely copy and alter the code, provided that new
Xversions are cleared through me before release. I will be acting as a
Xsource of information, and would appreciate copies of all changes.
X
XFIRST
X
XSee the readme file for instructions on building the game! This file
Xcovers the rules of the game, not installation.
X
XTHE STORY
X
XEmperor Lettuce Prei of Squaria has died, leaving no heir. The nobles
Xof the land scramble to reunite the pieces of the Broken Throne; do it
Xfirst and become the next Emperor!
X
XABOUT THE GAME
X
XBroken Throne is a strategic wargame which runs in "real time." Every
Xfew seconds, "action points" are transmitted to the players, which allow
Xthem to do things- recruiting troops, moving troops, attacking, and
Xconstructing and destroying cities. The object is to seize control of
Xall of your opponents' cities; when a player controls no cities, that
Xplayer is out of the game. Gameplay is quite rapid, and the map is
Xcontinuously updated on the screen. All that is required to play is
Xan ordinary terminal with curses screen control; any terminal that can
Xrun a visual editor (like vi or emacs) will do fine. If the terminal
Xis especially large, the game will take advantage of this, displaying
Xmore space between locations and adding extra lines to the output window.
XThis is handy if you are running Xwindows.
X
XThe map of Broken Throne is 16x16, consisting of square "hexes." Each
Xhex may contain one of several types of terrain:
X
X.      Plains
XT      Town         
XM      Mountains (passable) 
XF      Forest
XC      City
XS      Swamp
X^      Crags (impassable mountains)
X
XOn the screen, a hex is displayed like this:
X
XaC05
X
XWhere the lowercase letter is the letter of the player who controls the
Xhex, the capital letter (or . or ^ ) is the terrain type, and the number
Xfollowing is the number of troops. Unowned territories have no lowercase
Xletter, and territories without troops have no following number.
X
XSTARTING OUT
X
XTo launch the game, someone must launch the server program. This is done
Xby typing btserver #ofplayers &, where #ofplayers is replaced by the
Xactual number of combatants and the & sign puts the server in the background.
XPutting the server in the background is important because it allows the
Xplayer who runs the server to play in the game as well.
X
XThen, the individual players each need to launch the client program. This
Xis done by typing btclient addressOfServer, where addressOfServer is the
XInternet address of the machine on which the server is being run.
X
XFor instance, in our local network, bt resides in a subdirectory of my
Xown directory, so a player types:
X
Xcd ~boutell/bt    <-- Of course this should be replaced by the path of your
X                      *own* Broken Throne directory. 
Xbtserver 2 &      ... To launch the game for two players, then each
X                  player moves to the bt directory and types:
Xbtclient toffee   <-- Where "toffee" is the name of the machine the game
X                  is running on. EVEN IF YOU ARE ALL ON THE SAME MACHINE
X                  IT IS STILL NECESSARY TO SPECIFY THE NAME OF THE MACHINE.
X                  ONLY ONE PLAYER LAUNCHES THE SERVER.
X
XThe player who is running the server should start up his client last, in
Xorder to keep his screen from being "munged" as the server announces
Xplayers.
X
XOnce the client is launched, a blank map will be displayed. Within a moment
Xor two it should be replaced by the full map; be patient if there are
Xseveral players.
X
XWhen the game first begins, the player is told which lowercase letter
Xbelongs to them. Initially the player controls one city and no troops.
XIn order to acquire troops, the r (recruit) command should be used
Ximmediately. A cursor will appear at the city's location. This cursor
Xcan be moved with the standard vi/ moria/ hack movement keys:
X
X          k
X          |
X        h-*-l
X          |
X          j
X
XThe space bar is used to actually select a location at which to
Xrecruit. Since the cursor starts out at the first city, the player should
Xjust tap the space bar after pressing r to recruit. Several troops should
Xappear in the city.
X
XIt is then possible to fan out and claim control of towns. The m (move)
Xcommand is used to do this. The player should press m, then move the cursor
Xto the source of the troops, initially the first city, and press the space
Xbar. Then the cursor should be moved to the destination of the troops,
Xwhich can be no more than one hex away; diagonal moves are acceptable.
XWhen the space bar is pressed again, the troops will be moved.
XOnce a new town is reached, the player can use the r command here to recruit
Xmore troops.
X
XRecruitment can only take place every twenty ticks in any given town or
Xcity. The exact length of time is found in the file bt.h, in terms of seconds.
XAlso found in bt.h is the constant _SPEED, which determines how often a "tick"
Xtakes place, awarding action points and bringing the next recruitment closer.
XThe player can attempt to recruit early, but runs the risk of failure, and
Xwill be forced to wait the full time again if recruitment does not succeed.
XThe number of troops recruited is a function of population.
X
XAction points are required to move troops! The player will soon find
Xthemselves running low on them while exploring the area. This is because
Xmovement in unfamiliar territory costs 4 action points per army moved.
XMovement in the player's own territory costs only 2 action points per
Xarmy moved. When each tick takes place, the player will receive 2 action
Xpoints, plus 1 action point for each city under the player's control.
X
XCOMBAT
X
XCombat in the Broken Throne is simple; the player simply moves troops into
Xenemy territory, and the game resolves the conflict. Each round of battle,
Xthere is a 50% chance the attacker will lose an army, and a 50% chance
Xthe defender will; this continues until one side has no more troops.
XUsually, however, the odds of combat are modified by the terrain of the
Xtwo hexes, as detailed below. If the attacker seizes an opponent's last
Xcity, that opponent is out of the game!
X
XTERRAIN
X
XThe different terrain types have different roles in the game. Plains
Xhexes (' . ') have no effect on combat, but provide substantial population.
XThe number of troops a town or city can recruit depends on the surrounding
Xpopulation either controlled by the player or not controlled.
X
XTown hexes (' T ') are the basic source of troops in the game, since
Xrecruitment can only take place here and in cities. They have a strong
Xdefensive advantage, 10%, but no offensive advantage.
X
XMountain hexes (' M ') are difficult to move through. When moving troops
Xinto them, some troops will usually be balked by conditions in the passes,
Xand the action points will still be spent. However, a player in the mountains
Xenjoys a 10% advantage, both defending against attack and when striking
Xdown out of the mountains.
X
XForest hexes (' F ') hide enemy troops from view. A player can only tell
Xwho the forest is controlled by; the exact numbers of troops are hidden.
XForces in a forest enjoy a 5% advantage when attacking out of the forest.
XForces in the forest have a 10% defensive advantage as well.
X
XCity hexes (' C ') are the strongest hexes in the game, with a 15% defensive
Xadvantage, though only a 5% offensive advantage. The player can recruit
Xtroops at cities, and cities also provide an extra action point per tick;
Xthe more cities the player controls, the more resources they have, and
Xthe more quickly they can move armies. Cities can be constructed out of
Xtowns, at a cost of typically 90 action points! This value can be set
Xin the file bt.h, and is the constant _CONSTRUCTCOST. If a player loses
Xcontrol of all cities, that player is out of the game, and their existing
Xtroops disband, leaving their territory free for the taking! Although
Xbuilding cities is expensive, the action points they provide can quickly
Xmake up for the cost. It is very inexpensive to destroy a city, however,
Xso they should be built in safe areas.
X
XSwamp hexes (' S ') are the armpit of Broken Throne! Troops are very
Xunhappy here; the swamp will often gobble one army of the advancing
Xforce. Swamps have no population, and unless in the player's way, aren't
Xworth slogging through. Troops in a swamp are at a 10% DISadvantage,
Xboth attacking and defending!
X
XCrag hexes (' ^ ') are impassable mountains. It is not possible to
Xenter these hexes, and so they do not affect combat.
X
XCOMMAND REFERENCE& SOME NEW COMMANDS
X
Xc (location): construct a city. This requires, usually, 90 action points,
X  unless the value has been changed in bt.h before compiling.
X  Can only be used in towns.
Xd (location): destroy a city or town. Usually costs 10 action points.
Xq: quit the game. This has no confirmation, so be careful!
Xt (player letter) (message): Tell a player something. Type their player
X  letter (case is not important) after striking t, then your message to
X  them (one line). The message will appear in their output window. 
Xt # (message): If you type a '#' sign instead of a letter, the message
X  will go to all players, including yourself.
Xm (location) (location) (# of troops): move troops. The game will prompt
X  for the locations from which and to which troops are to be moved, then
X  for the number of troops.
Xr (location): recruit troops. See notes above on the rules of recruitment.
X
XWINNING& LOSING
X
XYou win the game when no other players remain. You lose the game if you
Xcontrol no cities. If your last city is taken (towns don't count), it
Xdoesn't matter how many troops you still have, so be careful to defend
Xthem! When a player is eliminated, any remaining troops and territories
Xare dissolved and available to the first taker.
X
XIf you have lost the game, you can continue to watch the game for as long
Xas you like, before striking 'q' to actually leave the program.
X
XIf you win, the game will not automatically halt itself. You need to type
X'q' to quit the game as the last player. This allows the game to be launched
Xby one person on one terminal to test it out.
X
XThe player who launched the server should see the message "Ending" after
Xthe last player quits. If not, that player should do a ps -aux command
Xand look for a lingering copy of the server, then kill that copy of the
Xserver.
X
XIF YOU ABORT THE PROGRAM
X
XIf you control-c a client, the other players can continue, but you will be
Xout of the game. You will not be able to see what you type unless you quit
Xproperly with the 'q' command. Type 'tset' to repair this.
X
XRUNNING ON LARGE TERMINALS& UNDER X-WINDOWS
X
XUnder X- Windows and on other systems providing large terminals, the game
Xwill take advantage of the terminal's size to provide more spacing and
Xmore room for output messages. The wider and deeper, the better. The game
Xwill *not* take notice if you resize the window while it is running, however.
X
XSUNVIEW
X
XBroken Throne does not run properly under Sunview. This is because curses
Xsupport for Sunview seems to be broken. If someone is willing to investigate
Xthe cause I'll be glad to accommodate a fix.
X
X... Enjoy! Someday I'll make this into a proper man page.
X 
X
X
END_OF_FILE
if test 11224 -ne `wc -c <'bt.doc'`; then
    echo shar: \"'bt.doc'\" unpacked with wrong size!
fi
# end of 'bt.doc'
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'\" \(1126 characters\)
sed "s/^X//" >'bt.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 */
X    
END_OF_FILE
if test 1126 -ne `wc -c <'bt.h'`; then
    echo shar: \"'bt.h'\" unpacked with wrong size!
fi
# end of 'bt.h'
fi
if test -f 'client.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'client.c'\"
else
echo shar: Extracting \"'client.c'\" \(16990 characters\)
sed "s/^X//" >'client.c' <<'END_OF_FILE'
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 <stropts.h>
X#include <errno.h>
X
X#include "types.h"
X#include "pack.h"
X#define _MAPX 16
X#define _MAPY 16
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#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
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 1L
Xhex map[_MAPX][_MAPY];
Xplayer players[20];
Xint playernumber;
Xint totalplayers;
Xint currenttime;
Xint living;
Xint port;
Xint playerFd;
Xint promptoffset;
Xfd_set active;
Xint inputstate;
Xchar* host;
Xint connectstream();
Xint inputwaiting();
Xvoid setupmap();
Xvoid displayhex();
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
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 struct strpeek checkio;
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 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 /* ioctl(fileno(stdin),I_SETSIG,S_INPUT);
X signal(SIGPOLL,inputfork); */
X fflush(stdin);
X done=0;
X inputpos=0;
X specific=specificspace;
X cursorx=-1;
X cursory=-1;
X while (!getmessage(&messagetype,specific));
X playernumber=specific[0]-64;
X sprintf(inputline,"We are player %c.\n",playernumber+64);
X tellplayer(inputline);
X while (!getmessage(&messagetype,specific));
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 (getmessage(&messagetype,specific)) {
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       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     }
X   }
X  if (done!=0) {
X   if (ioctl(inputfd,I_PEEK,&checkio)) {
X     inputchar=getch();
X     if (inputchar=='q')
X       done=2;
X   }
X  }
X  else {
X   checkio.flags=0;
X   while (ioctl(inputfd,I_PEEK,&checkio)) {
X    inputchar=getch();
X    switch (inputstate) {
X    case _CURSORM1:
X    case _CURSORM2:
X    case _CURSORRECRUIT:
X    case _CURSORCONSTRUCT:
X    case _CURSORDESTROY:
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       break;
X      }
X      break;
X     }
X    break;
X    case _CURSORCOMMAND:
X     switch(inputchar) {
X      case 'h':
X      case '/':
X      case '?':
X      help=newwin(16,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. Careful, there is no confirmation.");
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,"Press any key now to continue play.");
X      wrefresh(help);
X      inputchar=getch();
X      delwin(help);
X      touchwin(mapw);
X      wrefresh(mapw);
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      tellplayer("Quit\n");
X      outputline[0]=_QUIT;
X      outputline[1]=NULL;
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    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         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         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  usleep(100000);
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 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  refresh();
X}
X
Xvoid endprogram() {
X  outputline[0]=_DISCONNECT;
X  outputline[1]=NULL;
X  write(playerFd,outputline,strlen(outputline)+1); 
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()) {
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 (!inputwaiting());
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,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 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,&active);
X  playerFd=s;
X  return(1);
X}
X
Xint inputwaiting() {
X  int errrec;
X  fd_set readfds;
X  struct timeval waitTime;
X  waitTime.tv_sec=SECONDSLIMIT;
X  waitTime.tv_usec=MICROSECONDSLIMIT;
X  bcopy((char *) &active, (char *) &readfds,sizeof(active));
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(playerFd,&readfds))
X    return 1;
X  else
X    return 0;
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 16990 -ne `wc -c <'client.c'`; then
    echo shar: \"'client.c'\" unpacked with wrong size!
fi
# end of 'client.c'
fi
if test -f 'client.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'client.h'\"
else
echo shar: Extracting \"'client.h'\" \(151 characters\)
sed "s/^X//" >'client.h' <<'END_OF_FILE'
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
X
END_OF_FILE
if test 151 -ne `wc -c <'client.h'`; then
    echo shar: \"'client.h'\" unpacked with wrong size!
fi
# end of 'client.h'
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'\" \(539 characters\)
sed "s/^X//" >'interface.h' <<'END_OF_FILE'
Xvoid setupinterface();
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 */
END_OF_FILE
if test 539 -ne `wc -c <'interface.h'`; then
    echo shar: \"'interface.h'\" unpacked with wrong size!
fi
# end of 'interface.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'\" \(760 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 "types.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 760 -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