Vanilla Netrek Server Development Archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[VANILLA-L:466] INL Robot server mods - part 1



*** ntserv/commands.c.orig	Tue Dec 26 05:15:04 1995
--- ntserv/commands.c	Thu Feb 22 14:35:51 1996
***************
*** 122,127 ****
--- 122,129 ----
  extern int do_pause();
  extern int do_free();
  extern int do_gametime();
+ extern int do_timeout();
+ extern int do_confine();
  
  #else					/* OTHER stuff */
  
***************
*** 174,180 ****
  */
  
  struct command_handler commands[] = {
!     { NULL, NULL, NULL},         /* record 0 */
      { "HELP",
  		0,
  		"Show this help information",
--- 176,182 ----
  */
  
  struct command_handler commands[] = {
!     { NULL, 0, NULL, NULL},         /* record 0 */
      { "HELP",
  		0,
  		"Show this help information",
***************
*** 305,310 ****
--- 307,316 ----
  		C_CAPTAIN,
  		"Change your side to Orion.",
  		do_pickside },			/* ORI */
+     { "PASS",
+ 		C_CAPTAIN,
+ 		"Pass the first choice of sides to the home team.",
+ 		do_pickside },			/* PASS */
      { "PAUSE",
  		C_CAPTAIN,
  		"Requests a pause.",
***************
*** 317,322 ****
--- 323,332 ----
  		C_CAPTAIN,
  		"Requests that the game continues.",
  		do_pause },			/* CONTINUE */
+     { "TOUT",
+ 		C_CAPTAIN,
+ 		"Call a timeout.",
+ 		do_timeout },			/* TIMEOUT */
      { "FREE",
  		C_CAPTAIN | C_PLAYER,
  		"Frees a slot. Ex: 'FREE 0'",
***************
*** 325,330 ****
--- 335,344 ----
  		C_CAPTAIN,
  		"Shows the game time or sets it. Ex: 'GAMETIME 30 10'",
  		do_gametime },			/* GAMETIME */
+     { "CONFINE",
+ 		C_CAPTAIN,
+ 		"Confine teams from other races cores.",
+ 		do_confine },			/* CONFINE */
  #else
      { "SBSTATS",
                  C_PLAYER,
***************
*** 1126,1132 ****
  
          pcount++;
          if (i==who) {
! 	    if (j->voting[what] != 0) {
  		if (j->voting[what] < time(NULL) + votes[num].frequency) {
        		    bounce(who,"Sorry, you can only use %s every %1.1f minutes",
  				votes[num].type, votes[num].frequency / 60.0);
--- 1140,1146 ----
  
          pcount++;
          if (i==who) {
! 	    if ((j->voting[what] != 0) && (votes[num].frequency != 0)) {
  		if (j->voting[what] < time(NULL) + votes[num].frequency) {
        		    bounce(who,"Sorry, you can only use %s every %1.1f minutes",
  				votes[num].type, votes[num].frequency / 60.0);
*** ntserv/death.c.orig	Mon Jul 18 14:45:06 1994
--- ntserv/death.c	Thu Feb 22 14:35:51 1996
***************
*** 41,46 ****
--- 41,47 ----
      case KTORP:
      case KTORP2:
      case KPLASMA:
+     case KPLASMA2:
      case KPHASER:
      case KPLANET:
      case KSHIP:
*** ntserv/gencmds.c.orig	Thu Feb 22 23:24:27 1996
--- ntserv/gencmds.c	Thu Feb 22 23:41:32 1996
***************
*** 0 ****
--- 1,373 ----
+ /*
+  *  gencmds.c		
+  */
+ 
+ /*
+    Command interface routines general to ntserv/daemon/robot code.  This
+    is a replacement for the spagetti compile method used previously in
+    commands.c .  Instead of compiling this file several times with modified
+    defines, the generalized routines found here can be linked into other
+    executables.
+ */
+ 
+ #include <stdio.h>
+ #include <ctype.h>
+ #include <string.h>
+ #include <time.h>
+ #include <signal.h>
+ #include "defs.h"
+ #include "struct.h"
+ #include "data.h"
+ #include "gencmds.h"
+ 
+ char *
+ addr_mess(who, type)
+      int who, type;
+ {
+     static char addrbuf[10];
+     char* team_names[MAXTEAM+1] = { "", "FED", "ROM", "", "KLI",
+                                  "", "", "", "ORI" };
+ 
+     if (type == MALL) 
+       sprintf(addrbuf,"%s->ALL", myname);
+     else if (type == MTEAM)
+       sprintf(addrbuf,"%s->%3s", myname, team_names[who]);
+     else /* Assume MINDIV */
+       sprintf(addrbuf, "%s->%2s", myname, players[who].p_mapchars);
+ 
+     return (addrbuf);
+ }
+ 
+ /* Check a command list to see if a message is a command */
+ int
+ check_2_command(mess, cmds, prereqs)
+      struct message *mess;
+      struct command_handler_2 *cmds;
+      int prereqs;
+ {
+   int i, j, len;
+   char *comm;
+   FILE *glog;
+ 
+   if (!(mess->m_flags & MINDIV)) return 0;	/* double-check */
+ 
+   len = strlen(mess->m_data);
+ 
+   /* Check if a null command ( First 8 characters are XXX->XXX ) */
+   if (len <= 8)
+     return 0;
+ 
+   for (i = 8; i < len && isspace(mess->m_data[i]); i++) ; /* skip header/space */
+ 
+   comm = strdup(&mess->m_data[i]);
+ 
+   len = strlen(comm);
+ 
+   /* Convert first word in msg to uppercase */
+   for (i=0; (i < len && !isspace(comm[i])); i++)
+   {
+     comm[i] = toupper(comm[i]);
+   }
+ 
+   for (i=0; cmds[i].command != NULL; i++)
+   {
+     /* Dont check dummy descriptions.. */
+     if (cmds[i].tag & C_DESC)
+       continue;
+     if ((cmds[i].tag & C_PR_MASK) & ~(prereqs))
+       /* Dont meet the prereqs */
+       continue;
+     if (strncmp(cmds[i].command, comm, strlen(cmds[i].command)) == 0)
+     {
+ 
+       /* Record vote in GodLog if C_GLOG is set */
+       if (cmds[i].tag & C_GLOG )
+       {
+ 	if (!(glog = fopen(GodLog,"a")) )       /* Log votes for God to see */
+ 	  perror(GodLog);
+ 	else
+ 	{
+ 	  time_t    t = time(NULL);
+ 	  char      tbuf[40];
+ 	  struct tm *tmv = localtime(&t);
+ 
+ #ifdef STRFTIME
+ 	  sprintf(tbuf, "%02d/%02d %d/%d", tmv->tm_hour,tmv->tm_min,
+ 		  tmv->tm_mday,tmv->tm_mon);
+ #else
+ 	  strftime(tbuf, 39, "%R %D", tmv);
+ #endif
+ 	  fprintf(glog, "VOTE: %s %s@%s \"%s\"\n", tbuf,
+ 		  players[mess->m_from].p_login, 
+ 		  players[mess->m_from].p_monitor, comm);
+ 	  fclose(glog);
+ 	}
+       }
+ 
+ /* assume C_NAME */
+ #ifdef nodef
+       if (!(cmds[i].tag & C_NAME))
+       {
+ 	for (j=i; j < strlen(upper); j++)
+ 	  upper[j] = toupper(upper[j]);
+       }
+ #endif
+ 
+ #ifdef VOTING
+       if (cmds[i].tag & (C_VC_ALL | C_VC_TEAM))
+       {
+ 	if (do_vote(comm, mess, cmds, i))
+ 	{
+ 	  if ( cmds[i].tag & C_GLOG )
+ 	    if (!(glog = fopen(GodLog,"a")) )       /* Log pass for God to see */
+ 	      perror(GodLog);
+ 	    else
+ 	    {
+ 	      fprintf(glog, "VOTE: The motion %s passes\n", comm);
+ 	      fclose(glog);
+ 	    }
+ 	}
+       }
+       else
+ #endif
+       {
+ 	if (cmds[i].tag & C_PLAYER)
+ 	{
+ 	  int who;
+ 
+ 	  /* This is really not that usefull - If the calling
+ 	     function needs a player field, it should check it
+ 	     on it's own.  I include it here for compatibility */
+ 	  who = getplayer(mess->m_from, comm);
+ 	  if (who >= 0)
+ 	    (*cmds[i].handler)(comm, mess, who, cmds, i, prereqs);
+ 	  else
+ 	  {
+ 	    free(comm);
+ 	    return 0;
+ 	  }
+ 	}
+ 	else
+ 	  (*cmds[i].handler)(comm, mess, cmds, i, prereqs);
+       }
+       free(comm);
+       return 1;
+     }
+   }
+   free(comm);
+   return 0;
+ }
+ 
+ int
+ getplayer(from, line)
+      int from;
+      char *line;
+ {
+   char id;
+   char temp[MSG_LEN];
+   int what;
+   int numargs;
+ 
+   numargs = sscanf(line,"%s %c", temp, &id);
+   id = toupper(id);
+ 
+   if (numargs==1)
+     what = from;
+   else if ((id >= '0') && (id <='9'))
+     what = id - '0';
+   else if ((id >= 'A') && (id <= ('A' + MAXPLAYER - 10)))
+     what = id - 'A' + 10;
+   else
+   {
+     pmessage(from, MINDIV, addr_mess(from, MINDIV),
+ 	     " Invalid player number.");
+     return -1;
+   }
+   if (players[what].p_status == PFREE)
+   {
+     pmessage(from, MINDIV, addr_mess(from, MINDIV),
+ 	     " Player not in the game.");
+     return -1;
+   }
+   return what;
+ }
+ 
+ #ifdef VOTING
+ int
+ do_vote(comm, mess, votes, num)
+      char *comm;
+      struct message *mess;
+      struct command_handler_2 *votes;
+      int num;
+ {
+   int what = 0;
+   int player = -1;
+   register int i;
+   register struct player *j;
+   int pcount=0;  /* Total players in game */
+   int vcount=0;  /* Number who've voted recently */
+   int mflag = 0;
+   int sendto = 0;
+   int who;
+ 
+   who = mess->m_from;
+ 
+ #ifdef OBSERVERS
+   if (players[who].p_status == POBSERV)
+   {
+     bounce(who, "Sorry, Observers can't vote");
+     return 0;
+   }
+ #endif
+ 
+   if (votes[num].tag & C_PLAYER)
+   {
+     if ((what = getplayer(who, comm)) < 0)
+       return 0;
+     player = what;
+   }
+ 
+   what += votes[num].start;
+ 
+   if (what >= PV_TOTAL) return 0;
+ 
+   j = &players[who];
+   if ((j->voting[what] != 0) && (votes[num].frequency != 0))
+   {
+     if (j->voting[what] < time(NULL) + votes[num].frequency)
+     {
+       bounce(who,"Sorry, you can only use %s every %1.1f minutes",
+ 	     votes[num].command, votes[num].frequency / 60.0);
+       return 0;
+     }
+   }
+   j->voting[what] = time(NULL);  /* Enter newest vote */
+ 
+   if (votes[num].tag & C_VC_TEAM)
+   {
+     sendto = players[who].p_team;
+     mflag = MTEAM;
+   }
+   else if (votes[num].tag & C_VC_ALL)
+   {
+     sendto = 0;
+     mflag = MALL;
+   }
+   else
+   {
+     ERROR(1,("Unrecognized message flag in do_vote() for %s\n",
+ 	     comm));
+     return 0;
+   }
+ 
+   for (i=0, j = &players[i]; i < MAXPLAYER; i++, j++)
+   {
+     /*Skip free slots and robots */
+     if ( (j->p_status == PFREE) || (j->p_flags & PFROBOT))
+       continue;
+ 
+     /*Also skip players that are not on the same team if team flag is set*/
+     if ((votes[num].tag & C_VC_TEAM) && (j->p_team != players[who].p_team))
+       continue; 
+ 
+ #ifdef OBSERVERS
+     /* Also skip observers */
+     if (j->p_status == POBSERV)
+       continue;
+ #endif
+ 
+     pcount++;
+ 
+     /* Did a player vote expire? */
+     if ((j->voting[what] > 0)
+ 	&& ((votes[num].expiretime == 0)
+ 	    || (j->voting[what] + votes[num].expiretime > time(NULL))))
+       vcount++;
+   }
+   pmessage(sendto, mflag, addr_mess(sendto,mflag),
+ 	   "%s has voted to %s. %d player%s (of %d) %s voted.",
+ 	   players[who].p_mapchars, comm, vcount, vcount==1 ? "":"s",
+ 	   pcount, vcount==1 ? "has":"have");
+ 
+   /* The Votes Passes */
+   if ( (vcount >= votes[num].minpass)
+       && ( ((pcount < (2*vcount)) && (votes[num].tag & C_VC_ALL))
+ 	  || ((votes[num].tag & C_VC_TEAM) && (pcount-1 <= vcount)) ))
+   {
+     pmessage(sendto, mflag, addr_mess(sendto,mflag), 
+ 	     "The motion %s passes", comm);
+ 
+     /* Reset the Votes for this item since it passed */
+     for (i = 0, j = &players[i]; i < MAXPLAYER; i++, j++) 
+       j->voting[what] = -1;
+ 
+     if (votes[num].tag & C_PLAYER)
+       (*votes[num].handler)(who, player, mflag, sendto);
+     else
+       (*votes[num].handler)(who, mflag, sendto);
+ 
+     return 1;
+   }
+   else
+   {
+     if (votes[num].tag & C_VC_TEAM) 
+       i = pcount - vcount - 1;
+     else
+       i = pcount/2 + 1 - vcount;
+ 
+     if (i < (votes[num].minpass - vcount))
+       i = votes[num].minpass - vcount;
+ 
+     pmessage(sendto, mflag, addr_mess(sendto, mflag),
+ 	     "Still need %d more vote%s %s", i, (i==1 ? " ":"s"), comm);
+     return 0;
+   }
+ }
+ #endif /* VOTING */
+ 
+ /* ARGSUSED */
+ int
+ do_help(comm, mess, cmds, cmdnum, prereqs)
+      char *comm;
+      struct message *mess;
+      struct command_handler_2 *cmds;
+      int cmdnum;
+ {
+   int who;
+   int i;
+   char *addr;
+ 
+   who = mess->m_from;
+   addr = addr_mess(who,MINDIV); 
+ 
+   if (cmds[0].command != NULL)
+   { 
+     for (i=0; cmds[i].command != NULL; i++)
+     {
+       if ((cmds[i].tag & C_PR_MASK) & ~(prereqs))
+ 	/* Dont meet the prereqs */
+ 	continue;
+       if (cmds[i].tag & C_DESC)
+ 	/* Command is just a dummy description */
+ 	pmessage(who,MINDIV,addr, cmds[i].command);
+ #ifdef VOTING
+       else if (cmds[i].tag & (C_VC_TEAM | C_VC_ALL))
+       {
+ 	char ch;
+ 
+ 	if (cmds[i].tag & C_VC_TEAM)
+ 	  ch = 'T';
+ 	else if (cmds[i].tag & C_VC_ALL)
+ 	  ch = 'M';
+ 	else
+ 	  ch = '?';
+ 	pmessage(who,MINDIV,addr, "|%10s - %c: %s",
+ 		 cmds[i].command, ch, cmds[i].desc);
+       }
+ #endif /* VOTING */
+       else
+ 	pmessage(who,MINDIV,addr, "|%10s - %s",
+ 		 cmds[i].command, cmds[i].desc);
+     }
+   }
+ }
*** ntserv/ntscmds.c.orig	Thu Feb 22 23:24:39 1996
--- ntserv/ntscmds.c	Thu Feb 22 23:26:53 1996
***************
*** 0 ****
--- 1,935 ----
+ /*
+  *  ntscmds.c		
+  */
+ 
+ /*
+    Command interface routines general to ntserv processes.  This
+    is a replacement for the spagetti compile method used previously in
+    commands.c .  This file contains the defines, and code for performing
+    general commands issued to the server.
+    It doesn't contain the PUCK/DOG/INL and general command checking
+    routines that used to be grouped together in commands.c
+ */
+ 
+ #include <stdio.h>
+ #include <ctype.h>
+ #include <string.h>
+ #include <time.h>
+ #include <signal.h>
+ #include "defs.h"
+ #include "struct.h"
+ #include "data.h"
+ #include "gencmds.h"
+ 
+ #define ALLOW_EJECT
+ 
+ #if defined (ALLOW_EJECT)
+ int do_player_eject();
+ #endif
+ 
+ #if defined (AUTO_INL)
+ int do_start_inl();
+ #endif
+ 
+ #if defined (AUTO_PRACTICE)
+ int do_start_basep();
+ #endif
+ 
+ #if defined(AUTO_HOCKEY)
+ int do_start_puck();
+ #endif
+ 
+ #if defined(AUTO_DOGFIGHT)
+ int do_start_mars();
+ #endif
+ 
+ #if defined (TRIPLE_PLANET_MAYHEM)
+ char *teamNames[9] = {" ", "Federation", "Romulans", " ", "Klingons",
+                       " ", " ", " ", "Orions"};
+ int do_balance();
+ int do_triple_planet_mayhem();
+ #endif
+ 
+ const char myname[] = {"GOD"};
+ 
+ int do_sbstats_query();
+ int do_time_msg();
+ int do_queue_msg();
+ 
+ #ifdef GENO_COUNT
+ int do_genos_query();
+ #endif
+ 
+ int do_client_query();
+ int do_ping_query();
+ int do_stats_query();
+ #ifdef RSA
+ int bounceRSAClientType();
+ #endif
+ int bouncePingStats();
+ int bounceSessionStats();
+ int bounceSBStats();
+ 
+ /********* COMMANDS LIST ********
+ 	Note: - The commands *must* be in upper case. All commands are converted
+ 		to upper case before be checked. 
+ 	      - Votes are entered in the Voting list.
+ 	The first field is the command, the second is a description string 
+ 		(up to 50 chars.), lastly is the function to call.
+ */
+ 
+ static struct command_handler_2 nts_commands[] =
+ {
+     { "Possible commands are:", C_DESC },
+     { "HELP",
+ 	        0,
+ 		"Show this help information",
+ 		do_help },     			/* HELP */
+     { "CLIENT",
+ 		C_PLAYER,
+ 		"Show a user's client. Ex: 'CLIENT 0'",
+ 		do_client_query },		/* CLIENT */
+     { "PING",
+ 		C_PLAYER,
+ 		"Show client's ping (lag). Ex: 'PING 0'",
+ 		do_ping_query },		/* PING */
+     { "STATS",
+ 		C_PLAYER,
+ 		"Show player's stats during t-mode. Ex: 'STATS 0'",
+ 		do_stats_query },		/* STATS */
+ #ifdef ALLOW_PAUSE
+     { "PAUSE",
+                 0,
+                 "Pause the game",
+                 game_pause },
+     { "UNPAUSE",
+                 0,
+                 "Resume the game",
+                 game_resume },
+ #endif
+     { "SBSTATS",
+                 C_PLAYER,
+                 "Show player's starbase stats. Ex: 'SBSTATS 0'",
+                 do_sbstats_query },             /* SBSTATS */
+ 						/***** Vanilla commands */
+     { "TIME",
+ 		0,
+ 		"Show time left on Genocide timer.",
+ 		do_time_msg },			/* TIME */
+     { "QUEUE",
+ 		0,
+ 		"Show how many people are on the queue.",
+ 		do_queue_msg },			/* QUEUE */
+ #ifdef GENO_COUNT
+     { "GENOS",
+ 		C_PLAYER,
+ 		"Show player's winning genocides.  Ex: 'GENOS 0'",
+ 		do_genos_query },                       /* GENOS */
+ #endif
+ #ifdef VOTING
+ 
+     { "The following votes can be used:  (M=Majority, T=Team vote)", C_DESC },
+     { "Ejection Votes are recorded for the god to review.", C_DESC },
+ #if defined(ALLOW_EJECT)
+     { "EJECT",
+ 	C_VC_TEAM | C_GLOG | C_PLAYER,
+ 	"To eject a player. Ex: 'EJECT 0'", 
+ 	do_player_eject,				/* EJECT */
+ 	2, 0, 120, 30},
+ #endif
+ #if defined(TRIPLE_PLANET_MAYHEM)
+     { "TRIPLE",
+         C_VC_ALL | C_GLOG,
+         "Start triple planet mayhem by vote",
+         do_triple_planet_mayhem,
+ 	2, 22, 0},
+     { "BALANCE",
+         C_VC_ALL | C_GLOG,
+         "Request team randomise & balance",
+         do_balance,
+         4, 23, 0 },
+ #endif
+ #if defined(AUTO_INL)
+   { "INL",
+ 	C_VC_ALL | C_GLOG,
+ 	"Start game under INL rules.",
+ 	do_start_inl,
+ 	1, 20, 0 },
+ #endif
+ #if defined(AUTO_PRACTICE)
+   { "PRACTICE",
+ 	C_VC_ALL,
+ 	"Start basepractice by majority vote.",
+ 	do_start_basep,
+ 	1, 20, 0 },
+ #endif
+ #if defined(AUTO_HOCKEY)
+   { "HOCKEY",
+ 	C_VC_ALL | C_GLOG,
+ 	"Start hockey by majority vote.",
+ 	do_start_puck,
+ 	1, 20, 0 },
+ #endif
+ #if defined(AUTO_DOGFIGHT)
+   { "DOGFIGHT",
+ 	C_VC_ALL | C_GLOG,
+ 	"Start dogfight tournament by majority vote.",
+ 	do_start_mars,
+ 	1, 20, 0 },
+ #endif
+ #endif /* VOTING */
+ 
+     { NULL }
+     };
+ 
+ int
+ check_command(mess)
+      struct message *mess;
+ {
+   return check_2_command(mess, nts_commands, 0);
+ }
+ 
+ #if defined (ALLOW_EJECT)
+ do_player_eject(who,player,mflags, sendto)
+ int who, player, mflags, sendto;
+ {
+     register struct player *j;
+ 
+     j = &players[player];
+ 
+     if (j->p_status == PFREE){
+ 	pmessage(sendto, mflags, addr_mess(who,MTEAM), 
+ 		"That player is not in the game");
+ 	return;
+      }
+     if (j->p_flags & PFROBOT) {
+ 	pmessage(sendto, mflags, addr_mess(players[who].p_team,MTEAM), 
+ 		"You cannot eject a robot, twinks!");
+ 	return;
+     }
+     if (j->p_team != players[who].p_team){
+       pmessage(players[who].p_team, MTEAM, 
+ 	addr_mess(players[who].p_team,MTEAM), 
+ 	"You can only eject your own teammates, twinks!");
+       return;
+     }
+ 
+     pmessage(0, MALL, addr_mess(who,MALL), 
+ 	" %2s has been ejected by the players", j->p_mapchars);
+ 
+ #if defined (DOG)
+     reset_player(j->p_no);
+     dont_score(j->p_no);
+ #endif
+ 
+     eject_player(j->p_no);
+ }
+ 
+ eject_player(who)
+ int who;
+ {
+   struct player* j;
+ 
+   j = &players[who];
+   j->p_ship.s_type = STARBASE;
+   j->p_whydead=KQUIT;
+   j->p_explode=10;
+   j->p_status=PEXPLODE;
+   j->p_whodead=me->p_no;
+ }
+ #endif /* ALLOW_EJECT */
+ 
+ #if defined(AUTO_PRACTICE)
+ int do_start_basep(who, mflags, sendto)
+ int who, mflags,sendto;
+ {
+   if (vfork() == 0) {
+       (void) SIGNAL(SIGALRM,SIG_DFL);
+       execl(Basep, "basep", 0);
+       perror(Basep);
+       }
+ }
+ #endif
+ 
+ #if defined(AUTO_INL)
+ int do_start_inl(who, mflags, sendto)
+ int who, mflags, sendto;
+ {
+   if (vfork() == 0) {
+       (void) SIGNAL(SIGALRM,SIG_DFL);
+       execl(Inl, "inl", 0);
+       perror(Inl);
+       }
+ }
+ #endif
+ 
+ #if defined(AUTO_HOCKEY)
+ int do_start_puck(who, mflags, sendto)
+ int who, mflags, sendto;
+ {
+   if (vfork() == 0) {
+       (void) SIGNAL(SIGALRM,SIG_DFL);
+       execl(Puck, "puck", 0);
+       perror(Puck);
+       }
+ }
+ #endif
+ 
+ #if defined(AUTO_DOGFIGHT)
+ int do_start_mars(who, mflags, sendto)
+ int who, mflags, sendto;
+ {
+   if (vfork() == 0) {
+       (void) SIGNAL(SIGALRM,SIG_DFL);
+       execl(Mars, "mars", 0);
+       perror(Mars);
+       }
+ }
+ #endif
+ 
+ /*** QUERIES ***/
+ send_query(which,who,from)
+ char which;
+ int  who, from;
+ {
+ 	pmessage2(who, MINDIV, " ", from, "%c", which);
+ }
+ 
+ /* ARGSUSED */
+ do_client_query(comm,mess,who)
+ char *comm;
+ struct message *mess;
+ int who;
+ {
+ #ifdef RSA
+ 	send_query('#', who, mess->m_from); 
+ #endif
+ }
+ 
+ /* ARGSUSED */
+ do_ping_query(comm,mess,who)
+ char *comm;
+ struct message *mess;
+ int who;
+ {
+       send_query('!', who, mess->m_from);
+ }
+ 
+ /* ARGSUSED */
+ do_stats_query(comm,mess,who)
+ char *comm;
+ struct message *mess;
+ int who;
+ {
+       send_query('?', who, mess->m_from); 
+ }
+ 
+ #ifdef RSA
+ int bounceRSAClientType(from)
+ int from;
+ {
+         bounce(from,"Client: %s", RSA_client_type);
+ 
+         return 1;
+ }
+ #endif
+ 
+ int
+ bounceSessionStats(from)
+    int from;
+ {
+     float                          sessionBombing, sessionPlanets,
+                                    sessionOffense, sessionDefense;
+     int                            deltaArmies, deltaPlanets,
+                                    deltaKills, deltaLosses,
+                                    deltaTicks;
+     deltaPlanets = me->p_stats.st_tplanets - startTplanets;
+     deltaArmies = me->p_stats.st_tarmsbomb - startTarms;
+     deltaKills = me->p_stats.st_tkills - startTkills;
+     deltaLosses = me->p_stats.st_tlosses - startTlosses;
+     deltaTicks = me->p_stats.st_tticks - startTticks;
+ 
+ 
+     if (deltaTicks == 0) {
+         bounce(from,
+                 "Session stats are only available during t-mode.");
+         return 1; /* non t-mode */
+     }
+ 
+     sessionPlanets = (double ) deltaPlanets * status->timeprod /
+         ((double) deltaTicks * status->planets);
+ 
+     sessionBombing = (double) deltaArmies * status->timeprod /
+         ((double) deltaTicks * status->armsbomb);
+ 
+     sessionOffense = (double) deltaKills * status->timeprod /
+         ((double) deltaTicks * status->kills);
+ 
+     sessionDefense = (double) deltaTicks * status->losses /
+         (deltaLosses!=0 ? (deltaLosses * status->timeprod) : (status->timeprod));
+ 
+     bounce(from,
+         "%2s stats: %d planets and %d armies. %d wins/%d losses. %5.2f hours.",
+         me->p_mapchars,
+         deltaPlanets,
+         deltaArmies,
+         deltaKills,
+         deltaLosses,
+         (float) deltaTicks/36000.0);
+     bounce(from,
+         "Ratings: Pla: %5.2f  Bom: %5.2f  Off: %5.2f  Def: %5.2f  Ratio: %4.2f",
+         sessionPlanets,
+         sessionBombing,
+         sessionOffense,
+         sessionDefense,
+         (float) deltaKills /
+         (float) ((deltaLosses == 0) ? 1 : deltaLosses));
+     return 1;
+ }
+ 
+ int
+ bounceSBStats(from)
+     int from;
+ {
+     float                       sessionRatio, sessionKPH, sessionDPH,
+                                 overallKPH, overallDPH, overallRatio;
+     int                         deltaKills, deltaLosses,
+                                 deltaTicks;
+ 
+     deltaKills = me->p_stats.st_sbkills - startSBkills;
+     deltaLosses = me->p_stats.st_sblosses - startSBlosses;
+     deltaTicks = me->p_stats.st_sbticks - startSBticks;
+     overallRatio = (float) (me->p_stats.st_sblosses==0) ?
+       (float) me->p_stats.st_sbkills :
+       (float) me->p_stats.st_sbkills / (float) me->p_stats.st_sblosses;
+ 
+ /*  if (deltaTicks == 0) {
+         bounce(from,
+         "No SB time yet this session.");
+         return 0;
+     } */
+ 
+     if (deltaTicks != 0) {
+         sessionKPH = (float) deltaKills * 36000.0 / (float) deltaTicks;
+         sessionDPH = (float) deltaLosses * 36000.0 / (float) deltaTicks;
+     } else {
+         sessionKPH = sessionDPH = 0.0;
+     }
+ 
+     sessionRatio = (float) (deltaLosses==0) ? (float) deltaKills :
+         (float) deltaKills / (float) deltaLosses;
+ 
+     if (me->p_stats.st_sbticks != 0) {
+         overallKPH = (float) me->p_stats.st_sbkills * 36000.0 /
+                 (float) me->p_stats.st_sbticks;
+         overallDPH = (float) me->p_stats.st_sblosses * 36000.0 /
+                 (float) me->p_stats.st_sbticks;
+     } else {
+         overallKPH = overallDPH = 0.0;
+     }
+ 
+     bounce(from,
+       "%2s overall SB stats: %d wins/%d losses. %5.2f hours. Ratio: %5.2f",
+       me->p_mapchars,
+       me->p_stats.st_sbkills,
+       me->p_stats.st_sblosses,
+       (float) me->p_stats.st_sbticks/36000.0,
+       overallRatio);
+ 
+     if (deltaTicks)
+         bounce(from,
+           "%2s session SB stats: %d wins/%d losses. %5.2f hours. Ratio: %5.2f",
+           me->p_mapchars,
+           deltaKills,
+           deltaLosses,
+           (float) deltaTicks/36000.0,
+           sessionRatio);
+         bounce(from,
+           "Kills/Hour: %5.2f (%5.2f total), Deaths/Hour: %4.2f (%4.2f total)",
+           sessionKPH,
+           overallKPH,
+           sessionDPH,
+           overallDPH);
+     return 1;
+ }
+ 
+ 
+ #ifdef PING
+ int bouncePingStats(from)
+ int from;
+ {
+     if(me->p_avrt == -1){
+         /* client doesn't support it or server not pinging */
+         bounce(from,"No PING stats available for %c%c",
+            me->p_mapchars[0], me->p_mapchars[1]);
+     }
+     else{
+         bounce(from,
+             "%c%c PING stats: Average: %d ms, Stdv: %d ms, Loss: %d%%/%d%% s->c/c->s",
+             me->p_mapchars[0], me->p_mapchars[1],
+             me->p_avrt,
+             me->p_stdv,
+             me->p_pkls_s_c,
+             me->p_pkls_c_s);
+     }
+     return 1;
+ }
+ #endif
+ 
+ 
+ /* ARGSUSED */
+ do_sbstats_query(comm,mess,who)
+ char *comm;
+ struct message *mess;
+ int who;
+ {
+       send_query('^', who, mess->m_from);	
+ }
+ 
+ #ifdef GENO_COUNT
+ do_genos_query(comm,mess,who)
+ char *comm;
+ struct message *mess;
+ int who;
+ {
+   char *addr;
+ 
+   addr = addr_mess(mess->m_from,MINDIV);
+   pmessage(mess->m_from, MINDIV, addr, "%s has won the game %d times.",
+ 	players[who].p_name,players[who].p_stats.st_genos);
+ }
+ #endif
+ 
+ 
+ /* ARGSUSED */
+ do_time_msg(comm,mess)
+ char *comm;
+ struct message *mess;
+ {
+   char *teamNames[9] = {" ", "Federation", "Romulans", " ", "Klingons", 
+ 			  " ", " ", " ", "Orions"};
+   int who;
+   int t;
+   char *addr;
+ 
+   who = mess->m_from;
+   addr = addr_mess(who,MINDIV);
+ 
+   for (t=0;((t<=MAXTEAM)&&(teams[t].s_surrender==0));t++);
+ 
+   if (t>MAXTEAM) {
+     pmessage(who, MINDIV, addr, "No one is considering surrender now.  Go take some planets.");
+   } else {
+     pmessage(who, MINDIV, addr, "The %s have %d minutes left before they surrender.", teamNames[t],teams[t].s_surrender);
+   }
+ }
+ 
+ /*
+  * Give information about the waitqueue status
+  */
+ 
+ /* ARGSUSED */
+ do_queue_msg(comm,mess)
+ char *comm;
+ struct message *mess;
+ {
+     int who;
+     char *addr;
+     int i;
+ 
+     who = mess->m_from;
+     addr = addr_mess(who,MINDIV);
+ 
+     for (i=0; i < MAXQUEUE; i++) {
+         /* Only report open queues that have the report flag set. */
+         if (!(queues[i].q_flags & QU_OPEN))   continue;
+ 	if (!(queues[i].q_flags & QU_REPORT)) continue;
+       
+ 	if (queues[i].count>0 ) 
+   	    pmessage(who, MINDIV, addr, "Approximate %s queue size: %d", 
+ 		     queues[i].q_name, queues[i].count);
+ 	else
+ 	    pmessage(who, MINDIV, addr, "There is no one on the %s queue.",
+ 		     queues[i].q_name);
+     }
+ }
+ 
+ #ifdef TRIPLE_PLANET_MAYHEM
+ /*
+ **  16-Jul-1994 James Cameron
+ **
+ **  Balances teams according to player statistics.
+ **  Intended for use with Triple Planet Mayhem
+ */
+ 
+ /*
+ **  Tell all that player will be moved
+ */
+ static void
+ moveallmsg ( int p_no, int ours, int theirs )
+ {
+     struct player *k = &players[p_no];
+     pmessage(0, MALL, addr_mess(myname, MALL),
+        "Balance: %16s (%c%c) is to join the %s",
+        k->p_name, teamlet[k->p_team], shipnos[p_no], teamNames[ours] );
+ }
+ 
+ /*
+ **  Move a player to the specified team, if they are not yet there.
+ **  Make them peaceful with the new team, and hostile/at war with the
+ **  other team.
+ */
+ static void 
+ move(int p_no, int ours, int theirs)
+ {
+   struct player *k = &players[p_no];
+     
+   if ( k->p_team != ours ) {
+     pmessage(k->p_no, MINDIV, addr_mess(k->p_no,MINDIV),
+ 	     "%s: please SWAP SIDES to the %s", k->p_name, teamNames[ours] );
+   }
+   else {
+     pmessage(k->p_no, MINDIV, addr_mess(k->p_no,MINDIV),
+ 	     "%s: please remain with the %s", k->p_name, teamNames[ours] );
+   }
+   
+   printf("Balance: %16s (%s) is to join the %s\n", 
+ 	 k->p_name, k->p_mapchars, teamNames[ours]);
+   
+   k->p_hostile |= theirs;
+   k->p_swar    |= theirs;
+   k->p_hostile &= ~ours;
+   k->p_swar    &= ~ours;
+   k->p_war      = (k->p_hostile | k->p_swar);
+   k->p_team     =  ours;
+   sprintf(k->p_mapchars, "%c%c", teamlet[k->p_team], shipnos[p_no]);
+   sprintf(k->p_longname, "%s (%s)", k->p_name, k->p_mapchars);
+   
+   k->p_status = PEXPLODE;
+   k->p_whydead = KPROVIDENCE; /* should be KTOURNSTART? */
+   if (k->p_ship.s_type = STARBASE)
+     k->p_explode = 2 * SBEXPVIEWS;
+   else
+     k->p_explode = 10;
+   k->p_ntorp = 0;
+   k->p_nplasmatorp = 0;
+   k->p_hostile = (FED | ROM | ORI | KLI);
+   k->p_war     = (k->p_hostile | k->p_swar);
+ }
+ 
+ /*
+ **  Return two team masks corresponding to the teams of the first two
+ **  teams found in the player list.
+ */
+ static void sides ( int *one, int *two )
+ {
+     struct player *k;
+     int i;
+     int unseen;
+ 
+     unseen = (FED | ROM | ORI | KLI);
+     *one = 0;
+     *two = 0;
+     k = &players[0];
+     for(i=0;i<MAXPLAYER;i++)
+     {
+         if ( (k->p_status != PFREE) && (!(k->p_flags & PFROBOT)))
+         {
+             if ( ( unseen & k->p_team ) != 0 )
+             {
+                 if ( *one == 0 )
+                 {
+                     *one = k->p_team;
+                     unseen &= ~k->p_team;
+                     k++;
+                     continue;
+                 }
+                 *two = k->p_team;
+                 return;
+             }
+         }
+         k++;
+     }
+ }
+ 
+ /*
+ **  Calculate a player value
+ */
+ static int value ( struct player *k )
+ {
+     return
+     (int)
+     (
+         (float)
+         (
+             bombingRating(k) +
+             planetRating(k) +
+             offenseRating(k)
+         ) * 100.0
+     );
+ }
+ 
+ /*
+ **  Balance the teams
+ **
+ **  Uses an exhaustive algorithm (I'm exhausted!) to find the best combination
+ **  of the current players that balances the teams in terms of statistics.
+ **  The algorithm will support only the number of players that fits into the
+ **  number of bits in an int.
+ **
+ **  If there are multiple "best" combinations, then the combination
+ **  involving the least number of team swaps will be chosen.
+ */
+ int do_balance(void)
+ {
+     int i, j;                   /* miscellaneous counters    */
+     int records;                /* number of players in game */
+     int one;                    /* team number one mask      */
+     int two;                    /* team number two mask      */
+ 
+     struct player *k;           /* pointer to current player */
+ 
+     struct item
+     {
+         int p_no;               /* player number             */
+         int p_value;            /* calculated player value   */
+         int p_team;             /* team player on previously */
+     } list[MAXPLAYER];          /* working array             */
+ 
+     struct
+     {
+         int combination;        /* combination number        */
+         int value;              /* team balance difference   */
+         int one;                /* team one total value      */
+         int two;                /* team two total value      */
+         int swaps;              /* number of swaps involved  */
+     } best;                     /* best team combination     */
+ 
+     /* which teams are playing?  give up if only one found */
+     sides ( &one, &two );
+     if ( two == 0 )
+     {
+         pmessage ( 0, MALL, addr_mess(myname,MALL),
+           "Can't balance only one team!" );
+         pmessage ( 0, MALL, addr_mess(myname,MALL),
+           "Please could somebody move to another team, then all vote again?" );
+         return;
+     }
+ 
+     /* initialise best to worst case */
+     best.combination = -1;
+     best.value = 1<<30;
+     best.one = 0;
+     best.two = 0;
+     best.swaps = 1<<30;
+ 
+     /* reset working array */
+     for(i=0;i<MAXPLAYER;i++)
+     {
+         list[i].p_no    = 0;
+         list[i].p_value = 0;
+     }
+ 
+     /* insert players in working array */
+     records = 0;
+     k = &players[0];
+     for(i=0;i<MAXPLAYER;i++)
+     {
+         if ( (k->p_status != PFREE) && (!(k->p_flags & PFROBOT)))
+         {
+             list[records].p_no    = k->p_no;
+             list[records].p_value = value ( k );
+             list[records].p_team  = k->p_team;
+             records++;
+         }
+         k++;
+     }
+ 
+     /* randomise the working array; may cause different team mixes */
+     for(i=0;i<records;i++)
+     {
+         int a, b;
+         struct item swapper;
+ 
+         a = random() % records;
+         b = random() % records;
+ 
+         swapper = list[a];
+         list[a] = list[b];
+         list[b] = swapper;
+     }
+ 
+     /* loop for every _possible_ combination to find the best */
+     for(i=0;i<(1<<records);i++)
+     {
+         int difference;         /* difference in team total       */
+         int value_a, value_b;   /* total stats per team           */
+         int count_a, count_b;   /* total count of players on team */
+         int swaps;              /* number of swaps involved       */
+ 
+         /* if this a shadow combination already considered, ignore it */
+         /* if ( ( i ^ ( ( 1<<records ) - 1 ) ) < i ) continue; */
+         /* disabled - it will interfere with swap minimisation goal */
+ 
+         /* is this combination an equal number of players each side? */
+         count_a = 0;
+         count_b = 0;
+ 
+         for(j=0;j<records;j++)
+             if((1<<j)&i)
+                 count_a++;
+             else
+                 count_b++;
+ 
+         /* skip this combination if teams are significantly unequal */
+         if ( abs ( count_a - count_b ) > 1 ) continue;
+ 
+         /* reset team total for attempt */
+         value_a = 0;
+         value_b = 0;
+ 
+         /* calculate team total stats */
+         for(j=0;j<records;j++)
+             if((1<<j)&i)
+                 value_a += list[j].p_value;
+             else
+                 value_b += list[j].p_value;
+ 
+         /* calculate number of swaps this combination produces */
+         swaps = 0;
+         for(j=0;j<records;j++)
+             if((1<<j)&i)
+             {
+                 if ( list[j].p_team != one ) swaps++;
+ 	    }
+             else
+ 	    {
+                 if ( list[j].p_team != two ) swaps++;
+ 	    }
+ 
+         /* calculate difference in team total stats */
+         difference = abs ( value_a - value_b );
+ 
+         /* if this combo is better than the previous one we had,
+            or the combo is the same and the number of swaps is lower...  */
+         if ( ( difference < best.value )
+             || ( ( difference == best.value )
+                 && ( swaps < best.swaps ) ) )
+         {
+             /* remember it */
+             best.value = difference;
+             best.combination = i;
+             best.one = value_a;
+             best.two = value_b;
+             best.swaps = swaps;
+         }
+     }
+ 
+     /* announce movements intended */
+     for(j=0;j<records;j++)
+         if ( (1<<j)&best.combination )
+             moveallmsg ( list[j].p_no, one, two );
+ 
+     for(j=0;j<records;j++)
+         if ( !((1<<j)&best.combination) )
+             moveallmsg ( list[j].p_no, two, one );
+ 
+     /* move players to their teams */
+     for(j=0;j<records;j++)
+         if ( (1<<j)&best.combination )
+             move ( list[j].p_no, one, two );
+         else
+             move ( list[j].p_no, two, one );
+ 
+     /* advise all of resultant team mix difference */
+     pmessage ( 0, MALL, addr_mess(myname,MALL),
+         "The %s total rating will be %.2f",
+         teamNames[one],
+         (float) ( best.one / 100.0 ) );
+ 
+     pmessage ( 0, MALL, addr_mess(myname,MALL),
+         "The %s total rating will be %.2f",
+         teamNames[two],
+         (float) ( best.two / 100.0 ) );
+ 
+ }
+ 
+ int do_triple_planet_mayhem(void)
+ {
+     int i;
+ 
+     struct player* j;
+ 
+     /* balance the teams */
+     do_balance();
+ 
+     /* move all planets off the galaxy */
+     for (i=0; i<MAXPLANETS; i++)
+     {
+         planets[i].pl_flags = 0;
+         planets[i].pl_owner = 0;
+         planets[i].pl_x = -10000;
+         planets[i].pl_y = -10000;
+         planets[i].pl_info = 0;
+         planets[i].pl_armies = 0;
+         strcpy ( planets[i].pl_name, "" );
+     }
+ 
+     /* disable Klingon and Orion teams; stop people from joining them */
+     planets[20].pl_couptime = 999999; /* no Klingons */
+     planets[30].pl_couptime = 999999; /* no Orions */
+ 
+     /* initialise earth */
+     i = 0;
+     planets[i].pl_flags |= FED | PLHOME | PLCORE | PLAGRI | PLFUEL | PLREPAIR;
+     planets[i].pl_x = 40000;
+     planets[i].pl_y = 65000;
+     planets[i].pl_armies = 40;
+     planets[i].pl_info = FED;
+     planets[i].pl_owner = FED;
+     strcpy ( planets[i].pl_name, "Earth" );
+ 
+     /* initialise romulus */
+     i = 10;
+     planets[i].pl_flags |= ROM | PLHOME | PLCORE | PLAGRI | PLFUEL | PLREPAIR;
+     planets[i].pl_x = 40000;
+     planets[i].pl_y = 35000;
+     planets[i].pl_armies = 40;
+     planets[i].pl_info = ROM;
+     planets[i].pl_owner = ROM;
+     strcpy ( planets[i].pl_name, "Romulus" );
+ 
+     /* initialise indi */
+     i = 18;
+     planets[i].pl_flags |= PLFUEL | PLREPAIR;
+     planets[i].pl_flags &= ~PLAGRI;
+     planets[i].pl_x = 15980;
+     planets[i].pl_y = 50000;
+     planets[i].pl_armies = 4;
+     planets[i].pl_info &= ~ALLTEAM;
+     strcpy ( planets[i].pl_name, "Indi" );
+ 
+     /* fix all planet name lengths */
+     for (i=0; i<MAXPLANETS; i++)
+     {
+         planets[i].pl_namelen = strlen(planets[i].pl_name);
+     }
+ 
+     /* advise players */
+     {
+         char *list[] =
+         {
+ 	    "Galaxy reset for triple planet mayhem!",
+ 	    "Rule 1: they take Indi, they win,",
+ 	    "Rule 2: they take your home planet, they win,",
+ 	    "Rule 3: you can't bomb Indi,",
+ 	    "Rule 4: you may bomb their home planet, and;",
+ 	    "Rule 5: all planets are FUEL & REPAIR, home planets are AGRI.",
+ 	    ""
+ 	};
+ 
+ 	for ( i=0; strlen(list[i])!=0; i++ )
+             pmessage ( 0, MALL, addr_mess(myname,MALL), list[i] );
+     }
+ }
+ #endif /* TRIPLE_PLANET_MAYHEM */

-- 
 ------------------------------------------------------------------------
 | Kevin O'Connor               "BTW, IMHO we need a FAQ for 'RTFM',    | **
 | koconnor@acsu.buffalo.edu    'IMHO', 'IMO', 'FAQ', 'BTW', etc. !"    | **
 ------------------------------------------------------------------------ **
   *************************************************************************