[comp.sources.games] v12i052: bt3 - Broken Throne, multiplayer realtime conquest game

billr@saab.CNA.TEK.COM (Bill Randle) (04/24/91)

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

[From the author...]
[[Here's version 1.13 of the Broken Throne. New in this version:

- User- designed maps
- MAJOR bugfixes: battles are fair for the first time
- No more garbled messages betweenplayers 
- Shell script for folks with XWindows (NOT required)
- Various other enhancements and bugfixes ]]

#! /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 btclient.c btserver.c pack.h
# Wrapped by billr@saab on Tue Apr 23 13:33:08 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'\" \(10247 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
XTHE BROKEN THRONE
X
XBy Tom Boutell, 11/20/90- 4/22/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 (PARTIALLY OR COMPLETELY)
X
XThe Server:
X  If you get errors concering the file "bt.h,"
X  or the variable "details" in interface.c, then what you need to do
X  is go into bt.h and uncomment a line. There are further instructions
X  in bt.h explaining how to do this.
X  If anything else goes wrong, please mail me the details
X  (boutell@freezer.acs.udel.edu). It's worth noting that I wrote
X  a hopefully portable routine, msleep(), to sleep for n microseconds
X  by way of select, but my method may get "optimized out of existence"
X  by your compiler. This won't stop the game from running, though, just
X  make it use up more processor than it should. If you have the routine
X  usleep() you can use it instead. Plain sleep() only works in increments
X  of seconds, though (much too long).
X
XThe Client:
X  "make btclient" can be more problematic, since I have to use Curses, and
X  curses has portability problems here and there. But keyboard I/O is now
X  handled in a way that's just as portable as the socket stuff, so if the
X  server compiles, that shouldn't be a problem either.
X
X  "But what's wrong with the backspace key?" If you have problems with the
X  backspace or escape keys, look in btclient.h for the definitions (right
X  at the beginning) of these keys. I assumed the regular ASCII definitions,
X  and I'm accepting delete as interchangeable with backspace, so this
X  should cover most folks. If you need to alter these, go ahead; at your
X  own risk of course!
X
X  If the system complains about "cbreak" and "nocbreak," then insert
X  the line #define BSD_CURSES at the start of btclient.c and recompile.
X
X  If you get a compiler complaint about FD_SETSIZE being undefined,
X  edit the Makefile and uncomment the define for NO_FDSET in the
X  CFLAGS line and recompile.
X
XThe Robot:   
X  "make btrobot" should have no problems, especially if you have no problem
X  making the client, since it's a hacked client without the ugly user
X  interface stuff that's so difficult to port.
X
XAll Three:
X
X  One easy fix you may have to make: if your compiler complains that errno
X  is undefined, insert "int errno;" at main level in btclient.c. This really
X  shouldn't be necessary, but sometimes it is.
X
XIf You Change Things:
X
X  If you *do* make a fix, please mail it to me so others can have it.
X  Please set it up with an #ifdef specific to your machine, unless of course
X  it's a general bug. 
X
XCONTACT SERVICE
X
XContact me at boutell@freezer.acs.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
XTHINGS STILL COMING
X
XI've been sent patches for some *very* interesting variations, such as
Xplaying the game on a torus (wraparound), volcanoes that occasionally
Xmess up the map, and more intelligent robot play. (All of these
Xcame from Rudolf Fleischer.) I'll be working these into a future version.
X
XCHANGES IN VERSION 1.13
X
X- Add support for older (BSD 4.2 based) Unixes that don't have the
X  full complement of FD_SET macros available.
X
X- Include <ctype.h> for those systems that implement isdigit() and
X  friends as macros rather than library functions.
X
XCHANGES IN VERSION 1.12
X
X- You can now design your own maps on which to play! This should make games
X  between more than two players substantially more fair than they have
X  been. See the main documentation file.
X
X- Armies are now represented by three units each in battle rather than
X  five, since the primary problem with battle odds was somewhere else
X  entirely (see below!).
X
X- Another subtle bug: a received text message during the entry of a user's
X  command to move troops or tell a message used to ruin the user's input.
X  This has been fixed.
X
X- The redraw key should *finally* work, as long as curscr behaves as it
X  should on your screen. Redraw is capital M ("messy").
X
X- The addition of a recruitment warning: the client displays an "R" in the
X  lower right corner for a few seconds each time the necessary period
X  between safe recruitments goes by. The player need not pay attention to this,
X  but it is one way of keeping up.
X
X- The client is now guaranteed to correctly display up to 9999 action
X  points per player, without the previous occasional erasing.
X
X- A major bug nobody ever reported: The terrain modifiers for combat have
X  been *backwards* since day one! No kidding! I reversed this. All of a
X  sudden it's no longer absurdly easy to take over citadels.
X
X- Some users have reported that btclient doesn't accept their backspace key.
X  I've taken my best shot at repairing this, and if you still can't seem
X  to backspace, see the notes above regarding this in the section entitled
X  "The Client:".
X
X- A fail- safe escape key has been added; it gets you out of whatever you
X  were doing. I added this to make the default for the move command acceptable
X  to folks used to cancelling it by just pressing RETURN. (Entering 0 will
X  work too of course.)
X
X- The infamous void* details; problem has been resolved. In bt.h, the main
X  makefile, you can now uncomment a line to force the program to use
X  char* as a generic pointer type instead. This should satisfy the
X  longings of older compilers.
X
X- The move command now defaults to *all* armies in the sector you are
X  moving from. Of course it is possible that these armies may get sacked
X  as you are in the process of giving the order, in which case it won't
X  go through, but the client will try to move the most current number of
X  armies. (This was just as possible before when typing in the number of
X  armies explicitly and losing a few in the process.)
X
X- More compatibility fixes. Cleaned up use of "=-" and "=*"; added
X  #ifdefs for BSD curses (see "if it doesn't work"). Please send your patches!
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 10247 -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'\" \(734 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.doc                     2	
X bt.h                       2	
X btclient.c                 1	
X btclient.h                 2	
X btserver.c                 1	
X btserver.h                 2	
X changes                    2	
X interface.c                2	
X interface.h                2	
X map.txt                    2	
X msleep.c                   2	
X msleep.h                   2	
X pack.c                     2	
X pack.h                     1	
X robot.c                    2	
X ultrix_cpt.h               2	
X xbt                        2	
END_OF_FILE
if test 734 -ne `wc -c <'MANIFEST'`; then
    echo shar: \"'MANIFEST'\" unpacked with wrong size!
fi
# end of 'MANIFEST'
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'\" \(18584 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#ifdef BSD_CURSES
X#define cbreak crmode
X#define nocbreak nocrmode
X#endif
X
X#ifdef mips
Xextern int errno;
X#endif
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 char recvdline[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 int rollticks;
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 rollticks=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         sprintf(recvdline,"%s\n",specific);
X         tellplayer(recvdline);
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         rollticks++;
X         if (rollticks==_RTICKS) {
X           rollticks=0;
X           displaysignal("R");
X         } else if (rollticks==1) {
X           displaysignal(" "); 
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 mescape:
X      if (inputstate!=_CURSORCOMMAND) {
X        tellplayer("Cancelled.\n");
X        promptplayer("Command:");
X        inputstate=_CURSORCOMMAND;
X      }
X      break;
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 mbackspace:
X      case mdelete:
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 mescape:
X        tellplayer("");
X        tellplayer("Cancelled.\n");
X        promptplayer("Command:");
X        inputstate=_CURSORCOMMAND;
X        break;
X      case '\n':
X        inputline[inputpos]=NULL;
X        move(19,0);
X        refresh();
X        tellplayer("\n");
X        switch(inputstate) {
X         case _CURSORTELL:
X         inputpos=0;
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         if (inputpos==0)
X           packint(5,map[from.x][from.y].troops);
X         else 
X           packint(5,atoi(inputline));
X         inputpos=0; 
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  wrefresh(curscr);
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*12-11);
X  if (players[theplayer].live==1) 
X    addstr(sprintf(outputline,"%c:%4d,%3d",theplayer+64,
X                   players[theplayer].hexes,players[theplayer].action));
X  else addstr(sprintf(outputline,"%c: Dead   ",theplayer+64));
X}
X
Xvoid displaysignal(smessage)
X  char* smessage;
X{
X  move(18,_MAXPLAYERS*12);
X  addstr(smessage);
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 18584 -ne `wc -c <'btclient.c'`; then
    echo shar: \"'btclient.c'\" unpacked with wrong size!
fi
# end of 'btclient.c'
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'\" \(20681 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#include <ctype.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  char mapfile[80];
X  int ismap;
X  int offset;
X  printf("Broken Throne server program launching...\n");
X  ismap=0;
X  portnumber=2727;
X  if (argc>1) {
X    totalplayers=atoi(argv[1]);
X    if (argc>2) {
X      if (isalpha(*argv[2])) {
X        strcpy(mapfile,argv[2]);
X        ismap=1;
X      } else {
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      if (argc>3) {
X        strcpy(mapfile,argv[3]);
X        ismap=1;
X      }
X    }
X  }
X  else {
X    printf("Usage: btserver <#ofplayers> [port# (optional)] [map filename (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,(pointer)NULL);
X  setupmap(ismap,mapfile);
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,(pointer) playertext);
X      }
X    }
X    time(&newtime);
X    if (newtime-lasttime>=_SPEED) {
X      currenttime++;
X      broadcast(_ACTION,(pointer)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,(pointer)NULL);
X  shutdowninterface();
X  exit(0);
X}
X
Xvoid stopgame(message)
X  char* message;
X{
X  broadcast(_TEXT,(pointer) message);
X  endprogram();
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,(pointer)&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,(pointer)&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,(pointer)&owner);
X      } 
X      broadcast(_PLAYERSTATUS,(pointer)&currentplayer);
X      broadcast(_HEXSTATUS,(pointer)&to);
X      broadcast(_HEXSTATUS,(pointer)&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*3;
X      unitsb=enemystrength*3;
X      while ((unitsa>0) && (unitsb>0)) {
X      if (weight>((rand()/32) % 100)) { /* In search of good random #s! */
X        unitsb--;
X        }
X      else {
X        unitsa--;
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,(pointer)&from);
X        broadcast(_HEXSTATUS,(pointer)&to);
X        broadcast(_PLAYERSTATUS,(pointer)&currentplayer);
X        broadcast(_PLAYERSTATUS,(pointer)&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,(pointer)&currentplayer);
X        broadcast(_PLAYERSTATUS,(pointer)&owner);
X        broadcast(_HEXSTATUS,(pointer)&from);
X        broadcast(_HEXSTATUS,(pointer)&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,(pointer)&from);
X      broadcast(_HEXSTATUS,(pointer)&to);
X      broadcast(_PLAYERSTATUS,(pointer)&currentplayer);
X      return; }
X    else {
X      map[to.x][to.y].troops=marchers;
X      broadcast(_HEXSTATUS,(pointer)&from);
X      broadcast(_HEXSTATUS,(pointer)&to);
X      broadcast(_PLAYERSTATUS,(pointer)&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,(pointer)&at);
X  broadcast(_PLAYERSTATUS,(pointer)&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,(pointer)&at);
X  broadcast(_PLAYERSTATUS,(pointer)&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) % _RTICKS)) {
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+x][at.y+y].terrain!=2) && (map[at.x+x][at.y+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,(pointer)&currentplayer);
X    broadcast(_HEXSTATUS,(pointer)&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(ismap,mapfile)
X  int ismap;
X  char* mapfile;
X  {
X/* Relatively safe, but if there isn't room for the number of players
X   the game may lock up. */
X  FILE* mf;
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  int typ;
X  char buf[2];
X  full=0;
X  if (ismap) {
X    broadcast(_TEXT,"Loading the map...");
X  } else {
X    broadcast(_TEXT,"Generating the map...");
X  }
X  for (y=0; (y<_MAPY); y++) {
X    for (x=0; (x<_MAPX); x++) {
X      map[x][y].terrain=0;
X      altitude[x][y]=0;
X    }
X  }
X  if (ismap) {
X    mf=fopen(mapfile,"r");
X    if (!mf) {
X      stopgame("Btserver: map file specified not found.\n");
X    }
X    for (y=0; (y<_MAPY); y++) {
X      x=0;
X      while (x<_MAPX) {
X        placement=getc(mf);
X        if (isdigit(placement)) {
X            buf[0]=placement;
X            buf[1]=0;
X            seed=atoi(buf);
X            if (seed<=totalplayers) {
X              map[x][y].terrain=5;
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=1;
X              players[seed].citadels=1;
X              map[x][y].lastuse=0;
X              broadcast(_HEXSTATUS,(pointer)&players[seed].start);
X              broadcast(_PLAYERSTATUS,(pointer)&seed);
X            } else {
X              map[x][y].terrain=1;
X              map[x][y].population=terrainpopulation[1];
X              map[x][y].owner=0;
X              map[x][y].troops=0;
X              newhex.x=x;
X              newhex.y=y;
X              broadcast(_HEXSTATUS,(pointer)&newhex);
X            }
X            x++;
X        } else {
X          switch (placement) {
X            case '.':
X              typ=1;
X              break;
X            case 'T':
X              typ=2;
X              break;
X            case 'M':
X              typ=3;
X              break;
X            case 'F':
X              typ=4;
X              break;
X            case 'C':
X              typ=5;
X              break;
X            case 'S':
X              typ=6;
X              break;
X            default:
X              typ=0;
X              break;
X          }
X          if (typ) {
X            map[x][y].terrain=typ;
X            map[x][y].population=terrainpopulation[typ];
X            map[x][y].owner=0;
X            map[x][y].troops=0;
X            map[x][y].lastuse=0;
X            newhex.x=x;
X            newhex.y=y;
X            broadcast(_HEXSTATUS,(pointer)&newhex);
X            x++;
X          }
X        }
X      }
X    }
X    return;
X  }
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    map[x][y].lastuse=0;
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,(pointer)&players[seed].start);
X    broadcast(_PLAYERSTATUS,(pointer)&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,(pointer)&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  for (y=0; (y<_MAPY); y++) {
X    for (x=0; (x<_MAPX); x++) {
X      if (!map[x][y].terrain) {
X        map[x][y].terrain=7;
X        map[x][y].population=terrainpopulation[7];
X        map[x][y].owner=0;
X        map[x][y].lastuse=0;
X        map[x][y].troops=0;
X        newhex.x=x;
X        newhex.y=y;
X        broadcast(_HEXSTATUS,(pointer)&newhex);
X      }
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,(pointer)&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 20681 -ne `wc -c <'btserver.c'`; then
    echo shar: \"'btserver.c'\" unpacked with wrong size!
fi
# end of 'btserver.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