/* inlcomm.c
*/

/*
   INL server robot by Nick Trown & Kurt Siegl
*/

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <signal.h>
#include <setjmp.h>
#include <varargs.h>
#include <sys/file.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <errno.h>
#include <pwd.h>
#include <ctype.h>
#include <time.h>
#include "defs.h"
#include "struct.h"
#include "data.h"
#include "inldefs.h"

extern Inl_stats inl_stat;
extern Team	 inl_teams[];
extern int	 inl_start;
extern Sides	 sides[];
extern char	*inl_from;
extern Inl_countdown inl_countdown;

extern char	*addr_mess();
extern int	doResources();
extern void     inl_freeze();

int check_player(who, captain)
int who, captain;
{
    int c,  num=-1;

#ifdef INLDEBUG
    ERROR(2,("  Enter check_player\n"));
#endif

    if ((players[who].w_queue != QU_HOME) && (players[who].w_queue != QU_AWAY)) {
	pmessage(who, MINDIV, addr_mess(who),
		"You are not on a team. Send yourself 'HOME' or 'AWAY'");
	return NONE;
    }

    for (c=0; c < INLTEAM; c++) {
        if (players[who].p_team == inl_teams[c].side) {
                num = c;
                break;
        }
    }
    if (num == -1) {
        pmessage(who, MINDIV, addr_mess(who),"You need to pick a team first.");
        return NONE;
    }

    if (captain) {
        if (inl_teams[num].captain != who) {
            pmessage(who, MINDIV, addr_mess(who),"You are not the captain of your team.");
            return NONE;
        }
    }
    return num;
}



do_switchside(comm,mess)
char *comm;
struct message *mess;
{
    int who;
    int queue;
    int side;
    struct player *j;

#ifdef INLDEBUG
    ERROR(2,("  Enter do_switchside\n"));
#endif

   who = mess->m_from;

#ifdef nodef
   /* Should we allow changing the side during a game? [007] */
   if (!inl_stat.change) return 0;              /* can't change it anymore */
#endif

   if (inl_teams[check_player(who, 0)].captain == who) {
	pmessage(who, MINDIV, addr_mess(who), 
		"You can't switch while you are captain!");
	return 0;
   }

   if (strcmp(comm, "AWAY") == 0) {
	side = AWAY;
	queue = QU_AWAY;
   }
   else if (strcmp(comm, "HOME") == 0) {
	side = HOME;
	queue = QU_HOME;
   }
   else {
	ERROR(1,("Unknown side passed to do_switchside (%s)\n", comm));
	return 0;
   }

   if (players[who].w_queue == queue) {
        pmessage(who, MINDIV, addr_mess(who),"You are already on the %s team.",
		inl_teams[side].name);
	return 0;
   } else
   if (queues[queue].free_slots) {
	j = &players[who];
        queues[j->w_queue].free_slots++;
        j->w_queue = queue;
        queues[queue].free_slots--;
        j->p_team = inl_teams[side].side;
	sprintf(j->p_mapchars,"%c%c",
		teamlet[j->p_team], shipnos[j->p_no]);
        pmessage(who, MINDIV, addr_mess(who),"You are now on the %s team.",
		inl_teams[side].name);
	return 1;
   } else pmessage(who, MINDIV, addr_mess(who),"You can't switch teams now.");
}


/* Allows the captain to agree on starting the game */

do_start(comm,mess)
char *comm;
struct message *mess;
{
   int who;
   int c, num = -1;
   int begin = 0;

#ifdef INLDEBUG
    ERROR(2,("  Enter do_start\n"));
#endif

   who = mess->m_from;

   if (!inl_stat.change) { 			/* can't change it anymore */
	pmessage(who, MINDIV, addr_mess(who),
            "You can no longer start. The game has started.");
	return 0;
   }

   if ((num = check_player(who, 1)) == NONE) return 0;

   if ((inl_teams[num].side_index == NOT_CHOOSEN)
       || (inl_teams[num].side_index == RELINQUISH))
   {
     pmessage(who, MINDIV, addr_mess(who),
	      "You have to pick a side first");
     return 0;
   }

   inl_teams[num].flags |= T_START;

   pmessage(0, MALL, inl_from, "%s (%s) requests the game to start.", inl_teams[num].t_name,
	players[who].p_mapchars);

   for (c=0; c < INLTEAM; c++) {
	if (inl_teams[c].flags & T_START) begin++;
   }	
#ifdef INLDEBUG
    ERROR(2,("    begin = %d\n",begin));
#endif

   if (begin != 2) return 1;
  
   if ((inl_teams[HOME].time != inl_teams[AWAY].time ) ||
       (inl_teams[HOME].overtime != inl_teams[AWAY].overtime)) {
	pmessage(0, MALL, inl_from, 
		"The Game time has not be approved by both sides!");
	for (c = 0; c < INLTEAM; c++) {
		pmessage(0, MALL, inl_from, 
		    "%s (%s) has %d mins regulation and %d overtime", 
		    inl_teams[c].t_name, inl_teams[c].name, 
		    inl_teams[c].time, inl_teams[c].overtime);
	}
	return 0;
   }

   for (c=0; c < INLTEAM; c++) 
        inl_teams[c].flags &= ~T_START;

   start_countdown();
}

do_gametime(comm,mess)
char *comm;
struct message *mess;
{
   int who;
   int time, overtime;
   int num;

#ifdef INLDEBUG
    ERROR(2,("  Enter do_settime\n"));
#endif

   who = mess->m_from;

   if (!(inl_stat.flags & S_PREGAME))
   {
     time = inl_stat.time;
     if (inl_stat.flags & S_OVERTIME)
       time += inl_stat.overtime;
     pmessage(who, MINDIV, addr_mess(who),
	      "Minutes remaining: %i    Planet tally: %i - %i - %i",
	      ((time - inl_stat.game_ticks) / PERMIN ),
	      inl_teams[HOME].planets, inl_teams[AWAY].planets,
	      (20 - inl_teams[HOME].planets - inl_teams[AWAY].planets));
     return 1;
   }

   if ((num = check_player(who, 1)) == NONE) return 0;

   if (!(comm = strchr(comm, ' '))) {
	if ((inl_teams[HOME].time != inl_teams[AWAY].time ) ||
            (inl_teams[HOME].overtime != inl_teams[AWAY].overtime)) 
	    pmessage(who, MINDIV, addr_mess(who),
		"The Game time has not be approved by both sides!");

	pmessage(who, MINDIV, addr_mess(who), 
		"%s (%s) has %d mins regulation and %d overtime",
		inl_teams[num].t_name, inl_teams[num].name,
		inl_teams[num].time, inl_teams[num].overtime);
	return 1;
   }

   if (!inl_stat.change) {			/* can't change it anymore */
	pmessage(who, MINDIV, addr_mess(who),
            "You can no longer change the gametime. The game has started.");
        return 0;
   }

   if ((sscanf(comm,"%d %d",&time,&overtime))!=2) {
        pmessage(who, MINDIV, addr_mess(who), "Two times are needed (regulation & overtime)");
        return 0;
   }

   inl_teams[num].time = time;
   inl_teams[num].overtime = overtime;

   pmessage(who, MALL, inl_from,
                "%s (%s) has requested %d mins regulation and %d overtime",
                inl_teams[num].t_name, inl_teams[num].name,
                inl_teams[num].time, inl_teams[num].overtime);
}


/* Allows the captains of both teams to agree on resetting the galaxy */

do_resetgalaxy(comm,mess)
char *comm;
struct message *mess;
{
   int who;
   int c, num = -1;
   int rebuild = 0;

#ifdef INLDEBUG
    ERROR(2,("  Enter do_start\n"));
#endif

   who = mess->m_from;

   if (!inl_stat.change) {			/* can't change it anymore */
	pmessage(who, MINDIV, addr_mess(who),
            "You can no longer reset the galaxy. The game has started.");
        return 0;
   }

   if ((num = check_player(who, 1)) == NONE) return 0;

   inl_teams[num].flags |= T_RESETGALAXY;

   pmessage(0, MALL, inl_from, "%s (%s) requests that the galaxy be rebuilt.", 
	inl_teams[num].t_name,
        players[who].p_mapchars);

   for (c=0; c < INLTEAM; c++) {
        if (inl_teams[c].flags & T_RESETGALAXY) rebuild++;
   }

   if (rebuild != 2) return 1;

   for (c=0; c < INLTEAM; c++) {
        inl_teams[c].flags = 0;		/* reset all flags */
   }

   doResources(1);

   pmessage (0, MALL, inl_from, 
	"Game restarting with new galaxy.  Teams should be reselected");
}


/* Makes the player who sent the message captain if it hasn't been taken
   yet. */ 

do_captain(comm,mess)
char *comm;
struct message *mess;
{
   int who;
   int c, num=-1;

#ifdef INLDEBUG
    ERROR(2,("  Enter do_captain\n"));
#endif

   who = mess->m_from;

   if ((num = check_player(who, 0)) == NONE) return 0;

   if ((inl_teams[num].captain != NONE) &&
       (players[inl_teams[num].captain].p_status != PFREE)){
	pmessage(who, MINDIV, addr_mess(who),"%s (%s) is captain of your team already.",
		players[inl_teams[num].captain].p_name,
		players[inl_teams[num].captain].p_mapchars);
        return 0;
   }

   inl_teams[num].captain = who;

   pmessage(0, MALL, inl_from, "%s (%s) is captain of %s team.",
                        players[who].p_name, 
			players[inl_teams[num].captain].p_mapchars,
			inl_teams[num].name);
   return 1;
}


/* Allows the captain to release captaining duties */	

do_uncaptain(comm,mess)
char *comm;
struct message *mess;
{
   int who;
   int c, num=-1;

#ifdef INLDEBUG
    ERROR(2,("  Enter do_uncaptain\n"));
#endif

   who = mess->m_from;

   if ((num = check_player(who, 1)) == NONE) return 0;

   inl_teams[num].captain = NONE;
   pmessage(0, MALL, inl_from, "%s (%s) relinquishes captain control of %s team.",
	players[who].p_name, players[who].p_mapchars, inl_teams[num].name);

   return 1;
}


/* This allows the captain to pick the different races */

do_pickside(comm,mess)
char *comm;
struct message *mess;
{
    int who;

    int c, other_side, num=-1;
    int race,diagonal;

#ifdef INLDEBUG
    ERROR(2,("  Enter pickside\n"));
#endif

    who = mess->m_from;

    if (!inl_stat.change) {			/* can't change it anymore */
	pmessage(who, MINDIV, addr_mess(who),
            "You can no longer pick a side. The game has started.");
        return 0;
   }


    if ((num = check_player(who, 1)) == NONE) return 0;

    for (c=0; c < INLTEAM; c++) {
      if (inl_teams[num].team != inl_teams[c].team)
	other_side = inl_teams[c].team;
    }

    if (inl_teams[num].flags & T_SIDELOCKED)
    {
      pmessage(who, MINDIV, addr_mess(who),
	       "Choice of sides is no longer available.");
      return 0;
    }

    if ((num == HOME) && (inl_teams[other_side].side_index == NOT_CHOOSEN))
    {
      pmessage(who, MINDIV, addr_mess(who),
	       "The away team needs to pick a side first");
      return 0;
    }

    if (strcmp(comm, "PASS") == 0)
      if (num == HOME)
      {
	pmessage(who, MINDIV, addr_mess(who),
		 " Hey!! Only the Away team captain uses this!");
	return 0;
      }
      else
      {
	inl_teams[num].side_index = RELINQUISH;
	pmessage(0, MALL, inl_from, 
		 "Away team (%s) passes first race choice to home team!",
		 inl_teams[num].name);
      }
    else
      for (c = 0; c < NUMTEAM; c++) {
    	if (strcmp(comm, sides[c].name) == 0)
	{
	  if (inl_teams[other_side].side_index != NOT_CHOOSEN
	      && inl_teams[other_side].side_index != RELINQUISH)
	  {
	    if (inl_teams[other_side].side_index == c) 
	    {
              pmessage(who, MINDIV, addr_mess(who),
		       "The %s team already choose %s",
		       inl_teams[other_side].name,
		       sides[inl_teams[other_side].side_index].name);
              return 0;
	    }
	    if (sides[inl_teams[other_side].side_index].diagonal == c)
	    {
              pmessage(who, MINDIV, addr_mess(who),
		       "Cannot choose diagonal race to %s",
		       sides[inl_teams[other_side].side_index].name);
              return 0;
	    }
	    inl_teams[other_side].flags |= T_SIDELOCKED;
	  }

	  inl_teams[num].side_index = c;
	  inl_teams[num].name = strdup(sides[c].name);
	  pmessage(0, MALL, inl_from, "The %s team picks %s", 
		   inl_teams[num].t_name,
		   sides[inl_teams[num].side_index].name);
	}
      }
    return 1;
}	

do_tname(comm,mess)
char *comm;
struct message *mess;
{
    int who;

    int c, other_side, num=-1;

#ifdef INLDEBUG
    ERROR(2,("  Enter do_tname\n"));
#endif
    who = mess->m_from;

    if (!inl_stat.change) {		/* can't change it anymore */
	pmessage(who, MINDIV, addr_mess(who),
	    "You can no longer change the team name. The game has started.");
	return 0;
    }


    if ((num = check_player(who, 1)) == NONE) return 0;

    if (!(comm = strchr(comm, ' '))) {
	pmessage(who, MINDIV, addr_mess(who), "No team name was given.");
	return 0;
    }
 
    if (strlen(comm)+1 < 2) {
	pmessage(who, MINDIV, addr_mess(who), "No team name was given.");
        return 0;
    }

    comm = ++comm;

/*    free (inl_teams[num].t_name); */
    inl_teams[num].t_name = strdup (comm);
    pmessage(who, MINDIV, addr_mess(who), "Team name set to %s", inl_teams[num].t_name);
	
    return 1;
}

int end_pause()
{
  inl_stat.flags &= ~(S_FREEZE | S_COUNTDOWN);
  status->gameup &= ~(GU_PRACTICE | GU_PAUSED);
  pmessage(0,MALL, inl_from, "---- Game continues ----");
}
 
do_pause(comm,mess)
char *comm;
struct message *mess;
{
    int who;
    int queue;
    int side;
    struct player *j;
    int c, begin=0, num=-1;

#ifdef INLDEBUG
    ERROR(2,("  Enter do_pause\n"));
#endif

   who = mess->m_from;

   if ((num = check_player(who, 1)) == NONE) return 0;

   if (inl_stat.flags & S_PREGAME)
   {
     pmessage(who, MINDIV, addr_mess(who), "You aren't even PLAYING!");
     return 0;
   }

   if (strcmp(comm, "PAUSE") == 0) {
      inl_teams[num].flags |= T_PAUSE;
      pmessage(0, MALL, inl_from, "%s (%s) requests a pause.", 
	    inl_teams[num].t_name,
            players[who].p_mapchars);
   }
   else if (strcmp(comm, "PAUSENOW") == 0) {
      for (c=0; c < INLTEAM; c++)
          inl_teams[c].flags |= T_PAUSE;
      pmessage(0, MALL, inl_from, "%s (%s) pauses the game.", 
	    inl_teams[num].t_name,
            players[who].p_mapchars);
   }
   else if (strcmp(comm, "CONTINUE") == 0) {
      if (!(inl_teams[num].flags & T_PAUSE)) {
	pmessage(who, MINDIV, addr_mess(who), "Game is not paused.");
	return 0;
      }	
      inl_teams[num].flags &= ~T_PAUSE;
      pmessage(0, MALL, inl_from, 
	   "%s (%s) requests that the game continues.",
	    inl_teams[num].t_name,
            players[who].p_mapchars);
   }
   else {
      ERROR(1,("Unknown request passed to do_pause (%s)\n", comm));
      return 0;
   }

   for (c=0; c < INLTEAM; c++) {
      if (inl_teams[c].flags & T_PAUSE) begin++;
   }

   if (begin == 2)
   {
     inl_freeze();
   }
   else if (begin == 0)
   {
      inl_stat.flags |= S_COUNTDOWN;
      inl_countdown.idx=2;		/* 10 Second mark */
      inl_countdown.end=inl_stat.ticks+10*PERSEC;
      inl_countdown.action=end_pause;
      inl_countdown.message="Game continues in %i seconds";
  }

}

do_timeout(char *comm, struct message *mess)
{
  int who;
  int other_side;
  int c, num=-1;

#ifdef INLDEBUG
  ERROR(2,("  Enter do_pause\n"));
#endif

  who = mess->m_from;

  if ((num = check_player(who, 1)) == NONE) return 0; /* captain? */

  if (inl_stat.flags & S_PREGAME)
  {
    pmessage(who, MINDIV, addr_mess(who), "You aren't even PLAYING!");
    return 0;
  }

  for (c=0; c < INLTEAM; c++)
  {
    if (inl_teams[num].team != inl_teams[c].team)
      other_side = inl_teams[c].team;
  }

  if (inl_teams[num].flags & T_TIMEOUT)
  {
    inl_teams[num].flags &= ~(T_TIMEOUT);
    inl_teams[num].tmout++;

    if (!(inl_teams[other_side].flags & T_TIMEOUT))
    {
      inl_stat.flags &= ~(S_TMOUT);
      inl_stat.tmout_ticks = 0;
    }

    pmessage(0, MALL, inl_from,
	     "%s team (%s) has cancelled their timeout request!",
	     inl_teams[num].t_name,
	     players[who].p_mapchars);
  }
  else if (inl_teams[num].tmout <= 0)
  {
    pmessage(who, MINDIV, addr_mess(who),
	     "You have exhausted your timeout allowance for this game.");
  }
  else
  {
    inl_teams[num].flags |= T_TIMEOUT;
    inl_teams[num].tmout--;
    inl_stat.flags |= S_TMOUT;

    pmessage(0, MALL, inl_from, 
	     "**********************************************************");
    pmessage(0, MALL, inl_from, "%s team (%s) has called a timeout!",
	     inl_teams[num].t_name,
	     players[who].p_mapchars);
    pmessage(0, MALL, inl_from, 
	     "**********************************************************");
  }
}

do_confine(char *comm, struct message *mess)
{
  int who;
  int c, other_side, num=-1;

#ifdef INLDEBUG
  ERROR(2,("  Enter do_confine\n"));
#endif
  who = mess->m_from;

  if ((num = check_player(who, 1)) == NONE) return 0;

  if (!(inl_stat.flags & S_PREGAME))
  {
    pmessage(who, MINDIV, addr_mess(who),
	     "Weenie. You can't do a confine in tmode!!");
    return 0;
  }

  for (c=0; c < INLTEAM; c++)
  {
    if (inl_teams[num].team != inl_teams[c].team)
      other_side = inl_teams[c].team;
  }

  if (inl_teams[num].flags & T_CONFINE)
  {
    inl_teams[num].flags &= ~(T_CONFINE);

    if (!(inl_teams[other_side].flags & T_CONFINE))
      inl_stat.flags &= ~(S_CONFINE);

    pmessage(0, MALL, inl_from,
	     "%s team (%s) has toggled confine OFF (confine is an OR function)",
	     inl_teams[num].t_name,
	     players[who].p_mapchars);
  }
  else
  {
    inl_teams[num].flags |= T_CONFINE;
    inl_stat.flags |= S_CONFINE;

    pmessage(0, MALL, inl_from,
	     "%s team (%s) has toggled confine ON (confine is an OR function)",
	     inl_teams[num].t_name,
	     players[who].p_mapchars);
  }
}

do_free(comm,mess,who)
char *comm;
struct message *mess;
int who;
{
    int num=-1;
    struct player *j;

    if ((num = check_player(who, 1)) == NONE) return 0; /* Captain ? */

    j = &players[who];
    if ((j->p_status!=PALIVE) && (j->p_status!=PFREE)) {
      freeslot(j);
      pmessage(0, MALL, inl_from, "Freeing slot %i .", who);
    }
}


