/*****************************************************************************
 *                                                                           *
 *  Copyright (c) 1993, 1994, 1995, Elan Feingold (feingold@zko.dec.com)     *
 *                                                                           *
 *     PERMISSION TO USE, COPY, MODIFY, AND TO DISTRIBUTE THIS SOFTWARE      *
 *     AND ITS DOCUMENTATION FOR ANY PURPOSE IS HEREBY GRANTED WITHOUT       *
 *     FEE, PROVIDED THAT THE ABOVE COPYRIGHT NOTICE APPEAR IN ALL           *
 *     COPIES AND MODIFIED COPIES AND THAT BOTH THAT COPYRIGHT NOTICE AND    *
 *     THIS PERMISSION NOTICE APPEAR IN SUPPORTING DOCUMENTATION.  THERE     *
 *     IS NO REPRESENTATIONS ABOUT THE SUITABILITY OF THIS SOFTWARE FOR      *
 *     ANY PURPOSE.  THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS       *
 *     OR IMPLIED WARRANTY.                                                  *
 *                                                                           *
 *****************************************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

#include "callbacks.h"
#include "utils.h"
#include "dice.h"
#include "game.h"
#include "debug.h"
#include "riskgame.h"
#include "types.h"
#include "colormap.h"
#include "client.h"
#include "gui-vars.h"

/* Private routines */
Flag  GAME_CanAttack(Int32 iAttackSrc, Int32 iCountry);
Flag  GAME_IsEnemyAdjacent(Int32 iPlayer, Int32 iCountry);

/* Globals */
MsgNetMessage mess;


/************************************************************************ 
 *  FUNCTION: GAME_CanAttack
 *  HISTORY: 
 *     03.04.94  ESF  Created.
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
Flag GAME_CanAttack(Int32 iAttackSrc, Int32 iAttackDst)
{
  Int32 i;

  for (i=0; i!=6 && RISK_GetAdjCountryOfCountry(iAttackSrc, i)!=-1; i++)
    if (RISK_GetAdjCountryOfCountry(iAttackSrc, i) == iAttackDst)
      return TRUE;
  
  return FALSE;
}


/************************************************************************ 
 *  FUNCTION: GAME_MoveArmies
 *  HISTORY: 
 *     03.04.94  ESF  Created.
 *     03.18.94  ESF  Added server update.
 *     05.19.94  ESF  Added verbose message across network.
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
void GAME_MoveArmies(Int32 iSrcCountry, Int32 iDstCountry, Int32 iNumArmies)
{
  MsgMoveNotify msgMoveNotify;

  /* Notify the server */
  msgMoveNotify.iSrcCountry = iSrcCountry;
  msgMoveNotify.iDstCountry = iDstCountry;
  (void)RISK_SendMessage(CLNT_GetCommLinkOfClient(iThisClient), 
			 MSG_MOVENOTIFY, &msgMoveNotify);

  /* send a verbose message */
  sprintf(strScratch, "%s moved %d armies from %s to %s\n",
	  RISK_GetNameOfPlayer(iCurrentPlayer),
	  iNumArmies,
	  RISK_GetNameOfCountry(iSrcCountry),
	  RISK_GetNameOfCountry(iDstCountry));
  mess.strMessage = strScratch;
  (void)RISK_SendMessage(CLNT_GetCommLinkOfClient(iThisClient), 
			 MSG_NETMESSAGE, &mess);

  /* Update date structures */
  RISK_SetNumArmiesOfCountry(iDstCountry, 
		   RISK_GetNumArmiesOfCountry(iDstCountry) + iNumArmies);
  RISK_SetNumArmiesOfCountry(iSrcCountry, 
		   RISK_GetNumArmiesOfCountry(iSrcCountry) - iNumArmies);
}


/************************************************************************ 
 *  FUNCTION: GAME_PlaceArmies
 *  HISTORY: 
 *     03.04.94  ESF  Stubbed.
 *     05.19.94  ESF  Coded and added verbose message across network.
 *     10.02.94  ESF  Added support for PLACE_ALL.
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
void GAME_PlaceArmies(Int32 iCountry, Int32 iPlaceType)
{
  MsgPlaceNotify  msgPlaceNotify;
  Int32           iArmies;

  /* Depending on the type of placement, popup a dialog or not */
  if (iPlaceType == PLACE_MULTIPLE)
    {
      iArmies = UTIL_GetArmyNumber(1, 
				   RISK_GetNumArmiesOfPlayer(iCurrentPlayer),
				   TRUE);
      if (!iArmies)
	return;
    }
  else if (iPlaceType == PLACE_ALL)
    iArmies = RISK_GetNumArmiesOfPlayer(iCurrentPlayer);
  else
    iArmies = 1;

  /* Send a message to the server about this */
  msgPlaceNotify.iCountry = iCountry;
  (void)RISK_SendMessage(CLNT_GetCommLinkOfClient(iThisClient), 
			 MSG_PLACENOTIFY, &msgPlaceNotify);

  /* Send a verbose message */
  sprintf(strScratch, "%s placing %d %s on %s\n",
	  RISK_GetNameOfPlayer(iCurrentPlayer),
	  iArmies, 
	  iArmies == 1 ? "army" : "armies",
	  RISK_GetNameOfCountry(iCountry));
  mess.strMessage = strScratch;
  (void)RISK_SendMessage(CLNT_GetCommLinkOfClient(iThisClient), 
			 MSG_NETMESSAGE, &mess);

  /* Once we've placed, can't exchange cards anymore */
  fCanExchange = FALSE;
  
  /* Update the data structures */
  RISK_SetNumArmiesOfPlayer(iCurrentPlayer, 
			    RISK_GetNumArmiesOfPlayer(iCurrentPlayer)
			    - iArmies);
  RISK_SetNumArmiesOfCountry(iCountry,
			     RISK_GetNumArmiesOfCountry(iCountry)+iArmies);
  
  /* If we're out of armies, jump into attack mode */
  if (!RISK_GetNumArmiesOfPlayer(iCurrentPlayer) && iState == STATE_PLACE)
    {
      iState = STATE_ATTACK;
      UTIL_ServerEnterState(iState);
      UTIL_DisplayActionCString(iState, iCurrentPlayer);
    }
  
  UTIL_DisplayActionCString(iState, iCurrentPlayer);
  
  /* If we're fortifying, we're done. */
  if (iState == STATE_FORTIFY)
    UTIL_ServerEndTurn();
}


/************************************************************************ 
 *  FUNCTION: GAME_Attack
 *  HISTORY: 
 *     03.04.94  ESF  Created.
 *     03.06.94  ESF  Added missing pieces.
 *     03.07.94  ESF  Fixed owner update bug & army subtraction bug.
 *     03.18.94  ESF  Added server notification.
 *     03.28.94  ESF  Added message when country is taken.
 *     03.29.94  ESF  Added more informative moving message.
 *     04.02.94  ESF  Fixed bug, darken country if wrong number of die.
 *     04.02.94  ESF  Only notify server if fCacheNotify == FALSE.
 *     05.04.94  ESF  Fixed bug, changed location of call to GAME_PlayerDied().
 *     05.04.94  ESF  Fixed bug, when end of game occurs.
 *     05.19.94  ESF  Added verbose notification when player takes country.
 *     05.19.94  ESF  Moved calculation of dice correctness to ValidDice.
 *     11.06.94  ESF  Cached results, so that Do-or-die runs quicker.
 *     11.06.94  ESF  Added attack notification.
 *     01.17.95  ESF  Changed fCacheNotify to fNotify.
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
void GAME_Attack(Int32 iSrcCountry, Int32 iDstCountry, Flag fNotify)
{
  MsgAttackNotify  msgAttackNotify;
  Int32            iAttackDie, iDefendDie, iArmiesWon, iArmiesMove;
  Int32            iSrc, iDst, iPlayerAttackDie;

  /* Cached values */
  static Int32 iCachedSrcCountry=-1;
  static Int32 iCachedDstCountry=-1;

  iPlayerAttackDie = RISK_GetDiceModeOfPlayer(iCurrentPlayer);
  
  /* If the dice have been selected automatically, calculate them */
  if (iPlayerAttackDie != ATTACK_AUTO)
    iAttackDie = iPlayerAttackDie+1;
  else
    iAttackDie = MIN(RISK_GetNumArmiesOfCountry(iSrcCountry)-1, 3);
  
  iDefendDie = MIN(RISK_GetNumArmiesOfCountry(iDstCountry), 2);
  
  /* Set the color of the dice if the attack has changed */
  if (iCachedSrcCountry != iSrcCountry || iCachedDstCountry != iDstCountry)
    {
      COLOR_CopyColor(COLOR_PlayerToColor(RISK_GetOwnerOfCountry(iSrcCountry)),
		      COLOR_DieToColor(0));
      COLOR_CopyColor(COLOR_PlayerToColor(RISK_GetOwnerOfCountry(iDstCountry)),
		      COLOR_DieToColor(1));
    }
  
  /* Get the number of armies that the attacker won */
  iArmiesWon = DICE_Attack(iAttackDie, iDefendDie, 
			   RISK_GetOwnerOfCountry(iSrcCountry), 
			   RISK_GetOwnerOfCountry(iDstCountry));
  
  RISK_SetNumArmiesOfCountry(iDstCountry, 
	      RISK_GetNumArmiesOfCountry(iDstCountry) - iArmiesWon);
  RISK_SetNumArmiesOfCountry(iSrcCountry,
	      RISK_GetNumArmiesOfCountry(iSrcCountry) - 
	      (MIN(iDefendDie, iAttackDie) - iArmiesWon));

  /* Country was taken */
  if (!RISK_GetNumArmiesOfCountry(iDstCountry))
    {
      /* The attacking player gets a card */
      fGetsCard = TRUE;

      /* Send a verbose message */
      sprintf(strScratch, "%s captured %s from %s\n",
	      RISK_GetNameOfPlayer(iCurrentPlayer),
	      RISK_GetNameOfCountry(iDstCountry),
	      RISK_GetNameOfCountry(iSrcCountry));
      mess.strMessage = strScratch;
      (void)RISK_SendMessage(CLNT_GetCommLinkOfClient(iThisClient), 
			     MSG_NETMESSAGE, &mess);
      
      /* Display informative message telling of victory */
      sprintf(strScratch, "%s moving armies from %s to %s.\n", 
	      RISK_GetNameOfPlayer(iCurrentPlayer),
	      RISK_GetNameOfCountry(iSrcCountry),
	      RISK_GetNameOfCountry(iDstCountry));
      UTIL_DisplayComment(strScratch);

      /* Update data structures */
      iSrc = RISK_GetOwnerOfCountry(iSrcCountry);
      RISK_SetNumCountriesOfPlayer(iSrc, RISK_GetNumCountriesOfPlayer(iSrc)+1);
      iDst = RISK_GetOwnerOfCountry(iDstCountry);
      RISK_SetNumCountriesOfPlayer(iDst, RISK_GetNumCountriesOfPlayer(iDst)-1);

      /* New owner */
      RISK_SetOwnerOfCountry(iDstCountry, RISK_GetOwnerOfCountry(iSrcCountry));

      /* Move the desired number of armies if it isn't end of game */
      if (RISK_GetNumCountriesOfPlayer(iDst) != 0 ||
	  RISK_GetNumLivePlayers() != 2)
	{
	  iArmiesMove = UTIL_GetArmyNumber(iAttackDie, 
				     RISK_GetNumArmiesOfCountry(iSrcCountry)-1,
				     FALSE);
	  
	  RISK_SetNumArmiesOfCountry(iSrcCountry, 
				     RISK_GetNumArmiesOfCountry(iSrcCountry) - 
				     iArmiesMove);
	  RISK_SetNumArmiesOfCountry(iDstCountry, 
				     RISK_GetNumArmiesOfCountry(iDstCountry) + 
				     iArmiesMove);
	}

      /* See if this victory signalled a _DEADPLAYER or _ENDOFGAME condition */
      if (RISK_GetNumCountriesOfPlayer(iDst) == 0)
	GAME_PlayerDied(iDst);
    }
  else
    {
      /* If a new attack and not do-or-die, notify the server */
      if (!(iCachedSrcCountry == iSrcCountry && 
	    iCachedDstCountry == iDstCountry))
	{
	  /* Send a verbose message */
	  sprintf(strScratch, "%s is attacking from %s to %s\n",
		  RISK_GetNameOfPlayer(iCurrentPlayer),
		  RISK_GetNameOfCountry(iSrcCountry),
		  RISK_GetNameOfCountry(iDstCountry));
	  mess.strMessage = strScratch;
	  (void)RISK_SendMessage(CLNT_GetCommLinkOfClient(iThisClient), 
				 MSG_NETMESSAGE, &mess);
	  UTIL_DisplayComment(strScratch);
	}
    }
     
  /* If we are told to notify, then notify */
  if (fNotify == TRUE)
    {
      msgAttackNotify.iSrcCountry = iSrcCountry;
      msgAttackNotify.iDstCountry = iDstCountry;
      (void)RISK_SendMessage(CLNT_GetCommLinkOfClient(iThisClient), 
			     MSG_ATTACKNOTIFY, &msgAttackNotify);
    }

  /* Update cached values */
  iCachedSrcCountry = iSrcCountry;
  iCachedDstCountry = iDstCountry;
}


/************************************************************************ 
 *  FUNCTION: GAME_IsEnemyAdjacent
 *  HISTORY: 
 *     03.05.94  ESF  Created.
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
Flag GAME_IsEnemyAdjacent(Int32 iPlayer, Int32 iCountry)
{
  Int32 i;

  for (i=0; i!=6 && RISK_GetAdjCountryOfCountry(iCountry, i)!=-1; i++)
    if (RISK_GetOwnerOfCountry(RISK_GetAdjCountryOfCountry(iCountry, i)) != 
	iPlayer)
      return TRUE;
  
  return FALSE;
}


/************************************************************************ 
 *  FUNCTION: GAME_AttackClick
 *  HISTORY: 
 *     03.06.94  ESF  Created.
 *     04.02.94  ESF  Added notification of server after Do-or-Die.
 *     05.19.94  ESF  Added verbose message across network.
 *     05.19.94  ESF  Fixed for fixed attack dice do-or-die.
 *     07.14.94  ESF  Fixed bug, bot checking attack dice on dest country.
 *     10.15.94  ESF  Added printing of "Attacking from foo to bar" locally.
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
void GAME_AttackClick(Int32 iCountry)
{
  if (iAttackSrc < 0)
    {
      if (GAME_ValidAttackSrc(iCountry, TRUE) &&
	  GAME_ValidAttackDice(RISK_GetDiceModeOfPlayer(iCurrentPlayer),
			       iCountry))
	{
	  iAttackSrc = iCountry;

	  sprintf(strScratch, "%s is attacking from %s to" 
		  " <click on territory>",
		  RISK_GetNameOfPlayer(iCurrentPlayer),
		  RISK_GetNameOfCountry(iAttackSrc));
	  UTIL_DisplayComment(strScratch);
	  UTIL_LightCountry(iAttackSrc);
	}
    }
  else if (GAME_ValidAttackDst(iAttackSrc, iCountry, TRUE) &&
	   GAME_ValidAttackDice(RISK_GetDiceModeOfPlayer(iCurrentPlayer),
				iAttackSrc))
    {
      iAttackDst = iCountry;
      
      if (iActionState == ACTION_ATTACK)
	{
	  /* Keep the attack to be able to repeat */
	  iLastAttackSrc = iAttackSrc;
	  iLastAttackDst = iAttackDst;
	  
	  GAME_Attack(iAttackSrc, iAttackDst, TRUE);
	}
      else if (iActionState == ACTION_DOORDIE)
	{
	  Int32 iDice = RISK_GetDiceModeOfPlayer(iCurrentPlayer);
	  Flag  fNotify = TRUE;

	  while(GAME_ValidAttackSrc(iAttackSrc, FALSE) && 
		GAME_ValidAttackDst(iAttackSrc, iAttackDst, FALSE) &&
		GAME_ValidAttackDice(iDice, iAttackSrc))
	    {
	      GAME_Attack(iAttackSrc, iAttackDst, fNotify);
	      
	      /* Only True the first time around */
 	      fNotify = FALSE;
	    }
	}
      
      iAttackSrc = iAttackDst = -1;
      UTIL_DisplayActionCString(iState, iCurrentPlayer);
    }
}


/************************************************************************ 
 *  FUNCTION: GAME_ValidAttackDice
 *  HISTORY: 
 *     05.19.94  ESF  Created.
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
Flag GAME_ValidAttackDice(Int32 iAttackDice, Int32 iCountry)
{
  /* If the dice have been selected manually, check them */
  if (iAttackDice != ATTACK_AUTO)
    {
      if (RISK_GetNumArmiesOfCountry(iCountry) <= iAttackDice+1)
	{
	  sprintf(strScratch, "You can't attack with %d dice, dummy.",
		  iAttackDice+1);
	  UTIL_DarkenCountry(iCountry);
	  UTIL_DisplayError(strScratch);
	  return FALSE;
	}
    }
  return TRUE;
}


/************************************************************************ 
 *  FUNCTION: GAME_ValidAttackSrc
 *  HISTORY: 
 *     03.06.94  ESF  Created.
 *     03.07.94  ESF  Added verbose option
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
Flag GAME_ValidAttackSrc(Int32 iAttackSrc, Flag fVerbose)
{
  if (iAttackSrc >= NUM_COUNTRIES)
    {
      if (fVerbose)
	UTIL_DisplayError("You can't attack from the ocean!");
    }
  else if (RISK_GetOwnerOfCountry(iAttackSrc) != iCurrentPlayer)
    {
      if (fVerbose)
	{
	  sprintf(strScratch, "You can't attack from %s -- you don't own it.",
		  RISK_GetNameOfCountry(iAttackSrc));
	  UTIL_DisplayError(strScratch);
	}
    }
  else if (!GAME_IsEnemyAdjacent(iCurrentPlayer, iAttackSrc))
    {
      if (fVerbose)
	{
	  sprintf(strScratch, "There are no enemy territories you can" 
		  " attack from %s.", RISK_GetNameOfCountry(iAttackSrc));
	  UTIL_DisplayError(strScratch);
	}
    }
  else if (RISK_GetNumArmiesOfCountry(iAttackSrc) == 1)
    {
      if (fVerbose)
	UTIL_DisplayError("You can't attack with 1 army.");
    }
  else
    return TRUE;

  return FALSE;
}


/************************************************************************ 
 *  FUNCTION: GAME_ValidAttackDst
 *  HISTORY: 
 *     03.06.94  ESF  Created.
 *     03.07.94  ESF  Added verbose option
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
Flag GAME_ValidAttackDst(Int32 iAttackSrc, Int32 iAttackDst, Flag fVerbose)
{
  if (iAttackDst >= NUM_COUNTRIES)
    {
      if (fVerbose)
	UTIL_DisplayError("You can't attack the ocean!");
    }
  else 
    {
      if (!GAME_CanAttack(iAttackSrc, iAttackDst))
	{
	  if (fVerbose)
	    {
	      sprintf(strScratch, "You can't attack %s from %s!\n",
		      RISK_GetNameOfCountry(iAttackDst),
		      RISK_GetNameOfCountry(iAttackSrc));
	      UTIL_DisplayError(strScratch);
	    }
	}
      else if (RISK_GetOwnerOfCountry(iAttackDst) == iCurrentPlayer)
	{
	  if (fVerbose)
	    {
	      sprintf(strScratch, "You can't attack %s -- you own it.",
		      RISK_GetNameOfCountry(iAttackDst));
	      UTIL_DisplayError(strScratch);
	    }
	}
      else
	return TRUE;
    }
  
  return FALSE;
}


/************************************************************************ 
 *  FUNCTION: GAME_PlaceClick
 *  HISTORY: 
 *     03.07.94  ESF  Created.
 *     03.16.94  ESF  Added support for placing multiple armies.
 *     05.19.94  ESF  Factored out code to GAME_PlaceArmies.
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
void GAME_PlaceClick(Int32 iCountry, Int32 iPlaceType)
{
  /* Is the country picked the player's? */
  if (GAME_ValidPlaceDst(iCountry))
    GAME_PlaceArmies(iCountry, iPlaceType);
}


/************************************************************************ 
 *  FUNCTION: GAME_ValidPlaceDst
 *  HISTORY: 
 *     03.07.94  ESF  Created.
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
Flag GAME_ValidPlaceDst(Int32 iPlaceDst)
{
  if (iPlaceDst >= NUM_COUNTRIES)
    UTIL_DisplayError("Ahhh...That's the ocean, buddy...");
  else if (RISK_GetOwnerOfCountry(iPlaceDst) != iCurrentPlayer)
    UTIL_DisplayError("You can't place army on a teritory that"
		      " isn't yours!");
  else if (RISK_GetOwnerOfCountry(iPlaceDst) != iCurrentPlayer)
    UTIL_DisplayError("You cannot place armies on opponent's territories.");
  else
    return TRUE;

  return FALSE;
}


/************************************************************************ 
 *  FUNCTION: GAME_MoveClick
 *  HISTORY: 
 *     03.07.94  ESF  Created.
 *     03.29.94  ESF  Added a message when iMoveDst is verified.
 *     03.29.94  ESF  Fixed lack of UTIL_DisplayComment() bug.
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
void GAME_MoveClick(Int32 iCountry)
{
  Int32 iNumArmies;
  
  if (iMoveSrc < 0)
    {
      if (GAME_ValidMoveSrc(iCountry))
	{
	  iMoveSrc = iCountry;

	  sprintf(strScratch, "%s moving armies from %s to" 
		  " <click on territory>", 
		  RISK_GetNameOfPlayer(iCurrentPlayer),
		  RISK_GetNameOfCountry(iMoveSrc));
	  UTIL_DisplayComment(strScratch);
	  UTIL_LightCountry(iMoveSrc);
	}
    }
  else if (GAME_ValidMoveDst(iMoveSrc, iCountry))
    {
      iMoveDst = iCountry;

      sprintf(strScratch, "%s moving armies from %s to %s.\n", 
	      RISK_GetNameOfPlayer(iCurrentPlayer),
	      RISK_GetNameOfCountry(iMoveSrc),
	      RISK_GetNameOfCountry(iMoveDst));
      UTIL_DisplayComment(strScratch);

      iNumArmies = UTIL_GetArmyNumber(1,
				      RISK_GetNumArmiesOfCountry(iMoveSrc)-1,
				      TRUE);
      if (iNumArmies>0)
	{
	  GAME_MoveArmies(iMoveSrc, iMoveDst, iNumArmies);
	  
	  /* Turn over, man! */
	  UTIL_ServerEndTurn();
	}
      else
	{
	  UTIL_DarkenCountry(iMoveSrc);
	  iMoveSrc = iMoveDst = -1;
	}
    }
}


/************************************************************************ 
 *  FUNCTION: GAME_ValidMoveSrc
 *  HISTORY: 
 *     03.07.94  ESF  Created.
 *     03.29.94  ESF  fixed a small bug, inserted "else".
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
Flag GAME_ValidMoveSrc(Int32 iMoveSrc)
{
  if (iMoveSrc >= NUM_COUNTRIES)
    UTIL_DisplayError("You can't move armies from the ocean.");
  else if (RISK_GetOwnerOfCountry(iMoveSrc) != iCurrentPlayer)
    UTIL_DisplayError("You cannot move armies from an opponent's"
			" territories.");
  else if (RISK_GetNumArmiesOfCountry(iMoveSrc) == 1)
    UTIL_DisplayError("You cannot move from territories that"
		      " have one army to begin with.");
  else
    return TRUE;

  return FALSE;
}


/************************************************************************ 
 *  FUNCTION: GAME_ValidMoveDst
 *  HISTORY: 
 *     03.07.94  ESF  Created.
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
Flag GAME_ValidMoveDst(Int32 iMoveSrc, Int32 iMoveDst)
{
  if (iMoveDst >= NUM_COUNTRIES)
    UTIL_DisplayError("You can't move armies into the ocean -- they'd drown.");
  else if (RISK_GetOwnerOfCountry(iMoveDst) != iCurrentPlayer)
    UTIL_DisplayError("You cannot move armies to an opponent's"
		      " territories.");
  else if (!GAME_CanAttack(iMoveSrc, iMoveDst))
    {
      sprintf(strScratch, "Armies can't get to %s from %s", 
	      RISK_GetNameOfCountry(iMoveDst),
	      RISK_GetNameOfCountry(iMoveSrc));
      UTIL_DisplayError(strScratch);
    }
  else
    return TRUE;

  return FALSE;
}


/************************************************************************ 
 *  FUNCTION: GAME_GetContinentBonus
 *  HISTORY: 
 *     03.16.94  ESF  Created.
 *     01.09.95  ESF  Changed an if..else to an assertion.
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
Int32 GAME_GetContinentBonus(Int32 iPlayer)
{
  Int32  piCount[NUM_CONTINENTS];
  Int32  i, iArmies=0;

  /* Init. */
  for (i=0; i!=NUM_CONTINENTS; i++)
    piCount[i] = 0;

  /* Count up how many countries the player has in each of the continents */
  for (i=0; i!=NUM_COUNTRIES; i++)
    if (RISK_GetOwnerOfCountry(i) == iPlayer)
      {
	/* Sanity check */
	D_Assert(RISK_GetContinentOfCountry(i) >= 0 &&
		 RISK_GetContinentOfCountry(i) < NUM_CONTINENTS,
		 "Country has messed up number of armies.");
	
	piCount[RISK_GetContinentOfCountry(i)]++;
      }

  /* If the player has the total number of countries, give him or her bonus */
  for (i=0; i!=NUM_CONTINENTS; i++)
    if (RISK_GetNumCountriesOfContinent(i) == piCount[i])
      iArmies += RISK_GetValueOfContinent(i);

  return(iArmies);
}


/************************************************************************ 
 *  FUNCTION: GAME_PlayerDied
 *  HISTORY: 
 *     03.28.94  ESF  Created.
 *     03.29.94  ESF  Fixed off-by-one bug in end of game detection.
 *     04.11.94  ESF  Added iKillerPlayer parameter.
 *     05.04.94  ESF  Fixed bugs and exhanced.
 *     05.12.94  ESF  Fixed bug, reset dead player's cards to 0.
 *     09.31.94  ESF  Changed because of new ENDOFGAME contents.
 *     10.02.94  ESF  Changed to set number of live players == 0.
 *     10.30.94  ESF  Fixed bug, shouldn't be setting iNumLivePlayers to 0.
 *     01.09.95  ESF  Changed to be more verbose.
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
void GAME_PlayerDied(Int32 iDeadPlayer)
{
  MsgMessagePacket msg;

  /* One less live one */
  RISK_SetStateOfPlayer(iDeadPlayer, FALSE);
  RISK_SetNumLivePlayers(RISK_GetNumLivePlayers()-1);

  /* Notify everybody */
  sprintf(strScratch, "%s was destroyed by %s (who gained %d cards).",
	  RISK_GetNameOfPlayer(iDeadPlayer),
	  RISK_GetNameOfPlayer(iCurrentPlayer),
	  RISK_GetNumCardsOfPlayer(iDeadPlayer));
  msg.strMessage = strScratch;
  msg.strFrom = "Server";
  msg.iTo = DST_ALLPLAYERS;
  (void)RISK_SendMessage(CLNT_GetCommLinkOfClient(iThisClient),
			 MSG_MESSAGEPACKET, &msg);

  /* Is it an end-of-game of just a dead player? */
  if (RISK_GetNumLivePlayers() == 1)
    {
      MsgEndOfGame  msgEndOfGame;
      
      msgEndOfGame.iWinner = iCurrentPlayer; 
      (void)RISK_SendMessage(CLNT_GetCommLinkOfClient(iThisClient), 
			     MSG_ENDOFGAME, &msgEndOfGame);
    }
  else
    {
      Int32 i, n, m;
      
      /* Give the killer player all of the cards */
      n = RISK_GetNumCardsOfPlayer(iCurrentPlayer); 
      m = RISK_GetNumCardsOfPlayer(iDeadPlayer); 
      
      /* Let the player know how many cards he or she got. */
      if (m>0)
	{
	  sprintf(strScratch, "%s got %d cards from %s.", 
		  RISK_GetNameOfPlayer(iCurrentPlayer),
		  m, RISK_GetNameOfPlayer(iDeadPlayer));
	  mess.strMessage = strScratch;
	  (void)RISK_SendMessage(CLNT_GetCommLinkOfClient(iThisClient),
				 MSG_NETMESSAGE, &mess);
	}

      for (i=0; i!=m; i++)
	RISK_SetCardOfPlayer(iCurrentPlayer, n+i, 
			     RISK_GetCardOfPlayer(iDeadPlayer, i));
      RISK_SetNumCardsOfPlayer(iCurrentPlayer, n+m);
      RISK_SetNumCardsOfPlayer(iDeadPlayer, 0);
      
      /* If the player has more than 5 cards, then force an exchange. */
      if (RISK_GetNumCardsOfPlayer(iCurrentPlayer) > 5)
	{
	  fForceExchange = fCanExchange = TRUE;
	  iState = STATE_PLACE;
	  UTIL_ServerEnterState(iState);
	  CBK_ShowCards((Widget)NULL, (XtPointer)NULL, (XtPointer)NULL);
	}
    }
}


/************************************************************************ 
 *  FUNCTION: GAME_GameOverMan
 *  HISTORY: 
 *     03.28.94  ESF  Created.
 *     05.10.94  ESF  Completed.
 *     05.12.94  ESF  Added more functionality.
 *     01.05.95  DFE  Fixed "Free Card" bug -- gave 1st person free card.
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
void GAME_GameOverMan(void)
{
  Int32 i, iChoice;

  /* Does the client want to be involved in another game? */
  if (UTIL_PopupDialog("Game Over", "Play another game?", 2, "Yes", "No", 
		       NULL) == QUERY_NO)
    {
      MsgMessagePacket mess;

      /* Send a notification message */
      mess.strMessage = "I'm disconnecting.";
      mess.strFrom = strClientName;
      mess.iTo = DST_ALLBUTME;
      (void)RISK_SendMessage(CLNT_GetCommLinkOfClient(iThisClient), 
			     MSG_MESSAGEPACKET, &mess);

      UTIL_ExitProgram(0);
    }

  /* There will be another game */
  fGameStarted = FALSE;
  
  /* Since EndTurn isn't called, need to reset this so that first player
   * doesn't get a free card next game.
   */
  fGetsCard    = FALSE;

  /* The state will be one of registering */
  iState = STATE_REGISTER;
  UTIL_ServerEnterState(iState);
  UTIL_DisplayError("");
  UTIL_DisplayComment("");

  /* What kind of restart? */
  iChoice = UTIL_PopupDialog("New Game for Client", 
			     "Pick the desired option:", 3,
			     "New players", "Add players", "Same players");

  switch (iChoice)
    {
    /*************/
    case QUERY_YES:
    /*************/
      {
	/* Tell the server that we are going to play again with brand
	 * new players.
	 */
	
	(void)RISK_SendMessage(CLNT_GetCommLinkOfClient(iThisClient), 
			       MSG_GAMENEWPLAYERS, NULL);
	
	/* Free all of the client's players */
	for (i=0; i!=MAX_PLAYERS; i++)
	  if (RISK_GetClientOfPlayer(i) == iThisClient)
	    CLNT_FreePlayer(i);

	XtPopup(wRegisterShell, XtGrabExclusive);
      }

      break;

    /************/
    case QUERY_NO:
    /************/
      /* Pop up the registration dialog, without resetting RiskGame */
      (void)RISK_SendMessage(CLNT_GetCommLinkOfClient(iThisClient), 
			     MSG_GAMEMOREPLAYERS, NULL);

      XtPopup(wRegisterShell, XtGrabExclusive);
      break;

    /****************/
    case QUERY_CANCEL:
    /****************/
      /* Just initialize the RiskGame, without disturbing the players. */
      (void)RISK_SendMessage(CLNT_GetCommLinkOfClient(iThisClient), 
			     MSG_SAMEPLAYERS, NULL);
      break;

    default:
      D_Assert(FALSE, "Shouldn't be here!");
    }
  
  /* Set the colors to be of the initial setup */
  COLOR_SetWorldColors();

  /* Erase the dice roll */
  DICE_Hide();
}


/************************************************************************ 
 *  FUNCTION: GAME_ExchangeCards
 *  HISTORY: 
 *     03.29.94  ESF  Created.
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
void GAME_ExchangeCards(Int32 *piCards)
{
  Int32             i, j, piOldCards[3];
  MsgExchangeCards  msgCards;
  MsgNetMessage     msgNetMessage;

  /* Save a copy and transform array indices into card indices */
  for (i=0; i!=3; i++)
    {
      piOldCards[i] = piCards[i];
      piCards[i] = RISK_GetCardOfPlayer(iCurrentPlayer, piCards[i]);
    }
  
  /* Send the cards to the server -- this will generate an _REPLYPACKET */
  for (i=0; i!=3; i++)
    msgCards.piCards[i] = piCards[i];
  (void)RISK_SendSyncMessage(CLNT_GetCommLinkOfClient(iThisClient), 
			     MSG_EXCHANGECARDS, &msgCards,
			     MSG_REPLYPACKET, CBK_IncomingMessage);

  /* Update the date structures (bonus + countries on cards) */
  RISK_SetNumArmiesOfPlayer(iCurrentPlayer, 
			    RISK_GetNumArmiesOfPlayer(iCurrentPlayer)+iReply);

  /* See if any of the cards match countries that the player has */
  for (i=0; i!=3; i++)
    if (RISK_GetOwnerOfCountry(piCards[i]) == iCurrentPlayer)
      RISK_SetNumArmiesOfCountry(piCards[i], 
				 RISK_GetNumArmiesOfCountry(piCards[i])+2);

  /* Take the cards away from the player */
  for (i=0; i!=3; i++)
    for (j=piOldCards[i]; j!=MAX_CARDS-1; j++)
      RISK_SetCardOfPlayer(iCurrentPlayer, j,
			   RISK_GetCardOfPlayer(iCurrentPlayer, j+1));
  RISK_SetNumCardsOfPlayer(iCurrentPlayer, 
			   RISK_GetNumCardsOfPlayer(iCurrentPlayer)-3);

  /* Display a message */
  sprintf(strScratch, "%s got %d armies from exchanging cards.\n",
	  RISK_GetNameOfPlayer(iCurrentPlayer),
	  iReply);
  UTIL_DisplayError(strScratch);
  msgNetMessage.strMessage = strScratch;
  (void)RISK_SendMessage(CLNT_GetCommLinkOfClient(iThisClient), MSG_NETMESSAGE,
			 &msgNetMessage);
}

