#include <allegro.h>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include "carcass.h"
#include "playgame.h"
#include "dialog.h"
#include "global.h"
#include "sound.h"
#include "gfx.h"
#include "game.h"
#include "gamegfx.h"
#include "gmemusic.h"
#include "linked.h"
#include "genrand.h"
#include "palette.h"
#include "network.h"
#include "rules.h"
#include "str.h"
#include "ctsparse.h"
#include "tileset.h"

/* Current game state */
#define GAME_STATE_NORMAL        0
#define GAME_STATE_NEW_LEVEL     1
#define GAME_STATE_GAME_OVER     2
#define GAME_STATE_WIN           4
#define GAME_STATE_PASSWORD      8
#define GAME_STATE_PAUSEGAME     16
#define GAME_STATE_EXITMENU      32
#define GAME_STATE_ITEMMENU      64
#define GAME_STATE_GET_HIGHSCORE 128
#define GAME_STATE_SHOW_SCORES   256

/* User messages */
#define MSG_LENGTH   128
#define MAX_MSGS     8

/* Compare package types */
#define cmp_package_type(msg, header) (strstr((msg), (header))==(msg))

/* Number of pixels to scroll the tilemap */
#define SCROLL_SIZE  8

#define PACKET_SIZE     2048
/* Package headers */
#define PACKAGE_TEXT     "/msg"     /* Text message, client (user)   */
#define PACKAGE_STEXT    "/MSG"     /* Text message, server (system) */
#define PACKAGE_CQUIT    "/bye"     /* Client hangup                 */
#define PACKAGE_SQUIT    "/BYE"     /* Server hangup                 */
#define PACKAGE_CJOIN    "/join"    /* Client joins server           */
#define PACKAGE_SJOIN    "/JOIN"    /* Server tells other clients    */
#define PACKAGE_LEAVE    "/LEAV"    /* A client leaves the game      */
#define PACKAGE_PLNAME   "/pnm"     /* Player name                   */
#define PACKAGE_PTILE    "/ptile"   /* Tile placement                */
#define PACKAGE_ROTL     "/rotl"    /* Tile rotation, anti-clockwise */
#define PACKAGE_ROTR     "/rotr"    /* Tile rotation, clockwise      */
#define PACKAGE_SVER     "/VER"     /* Program version, server       */
#define PACKAGE_INIT     "/INIT"    /* Game initialization           */
#define PACKAGE_SRAN     "/sran"    /* New random number seed        */
#define PACKAGE_INITD    "/IDONE"   /* Game initialization complete  */
#define PACKAGE_LATE     "/LATE "   /* Game was already started      */
#define PACKAGE_START    "/START"   /* Game initialization complete  */
#define PACKAGE_ETURN    "/eturn"   /* End of turn                   */
#define PACKAGE_PMAN     "/pman"    /* Place man                     */
#define PACKAGE_STILE    "/lsts"    /* Load start tiles              */
#define PACKAGE_TILESET  "/ltst"    /* Load named tilesets           */
#define PACKAGE_CSCORE   "/cscr"    /* Change game scoring           */
#define PACKAGE_CHMEN    "/cmen"    /* Change initial men count      */

#define MAX_PLAYERS     MAX_CLIENTS
typedef struct {
   char name[MSG_LENGTH];
   const char *team_name;
   int team;
   int score;
   int active;
   int connected;
} PLAYER;

typedef struct {
   char text[MSG_LENGTH*2];
   int colour;
} GAME_MESSAGE;

/* Game music playlist */
static int game_playlist[] = { 4,  /* Number of items in the play-list */
   1, 2, 3, 4
};

/* Titlescreen playlist */
static int title_playlist[] = { 1,  /* Number of items in the play-list */
   0
};

static int startmen[MENTYPES] = {7, 1, 1, 1};

static char localname[MSG_LENGTH];
static char *player_names[MAX_PLAYERS];
static PLAYER player[MAX_PLAYERS];
static int num_players;
static int current_player;
static int local_player;

static char *tileset_flags = NULL;

static const char *theme_file = NULL;

static int game_state = GAME_STATE_NORMAL;
static int game_state_counter = 0;
static int game_state_options = 0;

static int game_type = 0;

static int new_game = TRUE;   /* True if the level was started from the game menu */
static int is_netgame = FALSE;/* TRUE if the game is a network game */
static int is_server = FALSE; /* TRUE if the local machine also runs the server */
static int client_init_done = FALSE;   /* TRUE if the client has received all data from the server */
static int game_started = FALSE;
static int game_done = FALSE;

static int display_score = FALSE;

/* Communications buffer */
static char comm_buffer_in[PACKET_SIZE];
static char comm_buffer_out[PACKET_SIZE];
static int sender;

/* Messaging */
static int enter_msg = TRUE;
static char user_msg[MSG_LENGTH];
static int new_msg;

static GAME_MESSAGE game_msg[MAX_MSGS];
static int msg_offset=0;

static BITMAP *statusbar = NULL;
static BITMAP *tilemap = NULL;
static int redraw_tilemap;

static char server_ip_address[256] = "127.0.0.1";
static int network_driver_nr;

/* Top left corner for map display */
static int map_x, map_y;
static int map_cursor_x, map_cursor_y;

/* Interface layout */
static int chat_y;
static int panel_x;
static int mw_width;
static const FONT *gamemsg_font;
static int turn_button_x;
static int turn_button_y;
static int turn_button_w;
static int turn_button_h;
static int rol_button_x;
static int rol_button_y;
static int ror_button_x;
static int ror_button_y;
static int man_button_x;
static int man_button_y;

/* Mouse navigation */
static int map_mouse_x, map_mouse_y;
static int old_mouse_x, old_mouse_y, old_mouse_b;
static int map_mouse_down_x, map_mouse_down_y;
static int map_mouse_down;
static int mouse_down_x, mouse_down_y;
static int mouse_down;

/* Game state variables */
static int try_place;         /* Try placing something on the map */
static int put_man;           /* True if a tile has been placed */
static int put_man_send;      /* True if the pman message has been send, but not processed yet */

static int man_type;

static int place_msg_sent;    /* True if the place message has been send, but not processed yet */

static int bonus_turn;        /* >0 if this turn is a bonus turn */
static int max_bonux_turns = 1;  /* Maximum number of bonus turns */
static int had_bonus_turn;    /* True if the current bonus has passed */

static int current_tile[MAX_PLAYERS];
static int last_tile_x, last_tile_y;
static int last_tile;

/* Game flags */
static int auto_end_turn;
static int map_cursor_mouse;

static int randseed;

/* Palettes */
static PALETTE gold_palette;
static PALETTE grey_palette;

/**********************************/
/* Client/Server message handling */
/**********************************/

/* Handle a message locally */
inline static void handle_message(const char *msg)
{
   const char *text;
   int tx, ty;
   int x, y;
   int c, n;
   
   if (cmp_package_type(msg, PACKAGE_STEXT)) {
      /* Server message */

      text = msg+strlen(PACKAGE_TEXT);
      snprintf(game_msg[msg_offset].text, MSG_LENGTH, "%s", text);
      game_msg[msg_offset].colour = yellow;
      msg_offset = (msg_offset+1)%MAX_MSGS;
      new_msg = TRUE;
      set_screen_changed();
   } else if (cmp_package_type(msg, PACKAGE_TEXT)) {
      /* Text message */

      text = msg+strlen(PACKAGE_TEXT);
      sscanf(text, "#%d:", &c);
      text = strstr(text, ":");
      snprintf(game_msg[msg_offset].text, MSG_LENGTH, "%s%s", player[c+1].name, text);
      if (game_started)
         game_msg[msg_offset].colour = get_team_colour(player[c+1].team);
      else
         game_msg[msg_offset].colour = white;
      msg_offset = (msg_offset+1)%MAX_MSGS;
      new_msg = TRUE;
      set_screen_changed();
   } else if (cmp_package_type(msg, PACKAGE_LATE)) {
      client_init_done = FALSE;
      game_started = TRUE;
      set_game_done();
      //printf("%d Late...\n", is_server);
   } else if (cmp_package_type(msg, PACKAGE_SQUIT)) {
      /* Server quits */
      
      /* Exit along with server, unless we're looking at the score screen */
      if (!game_done) {
         game_started = TRUE;
         set_game_done();
      }
   } else if (cmp_package_type(msg, PACKAGE_STILE)) {
      /* Load start tiles */
      text = msg+strlen(PACKAGE_STILE);
      load_starttile(text);
   } else if (cmp_package_type(msg, PACKAGE_TILESET)) {
      char *filelist;
      char *p;
      char *s;
      
      /* Load tilesets */
      text = msg+strlen(PACKAGE_TILESET);
      filelist = strdup(text);
      
      /* Parse the list of file names: each of them is enclosed within "" */      
      if ((s = strstr(filelist, "\"")))
         s++;
      p = s;
      if (s && (s = strstr(p, "\""))) {
         s[0] = '\0'; 
         s++;
      }
      
      while (p) {
         /* Load this file */
         load_tileset(p);
         //printf("[%s]\n", p);
         
         if ((s = strstr(s, "\"")))
            s++;
         p = s;
         if (s && (s = strstr(p, "\""))) {
            s[0] = '\0'; 
            s++;
         }
      }
      
      free(filelist);
   } else if (cmp_package_type(msg, PACKAGE_START)) {
      /* Start game */
      game_started = TRUE;
   } else if (cmp_package_type(msg, PACKAGE_INITD)) {
      /* Server acknowledges that it is ok to start the game */

      client_init_done = TRUE;
   } else if (cmp_package_type(msg, PACKAGE_SVER)) {
      /* Compare version numbers */

      text = msg+strlen(PACKAGE_SVER);
      if (!streq(text, GAME_NET_COMPAT_VER)) {
         /* Incompattible versions, don't connect */
         set_game_done();
      }
   } else if (cmp_package_type(msg, PACKAGE_INIT)) {
      /* Initialize */
      text = msg+strlen(PACKAGE_INIT);
      sscanf(text, "%d,%d", &randseed, &num_players);

      local_player = num_players-1;
   } else if (cmp_package_type(msg, PACKAGE_SRAN)) {
      /* Initialize */
      text = msg+strlen(PACKAGE_SRAN);
      sscanf(text, "%d", &randseed);
   } else if (cmp_package_type(msg, PACKAGE_SJOIN)) {
      /* Player has joined */
      text = msg+strlen(PACKAGE_SJOIN);
      sscanf(text, "%d", &c);
      player[c].active = TRUE;
      player[c].connected = TRUE;
   } else if (cmp_package_type(msg, PACKAGE_LEAVE)) {
      /* Player has left */
      text = msg+strlen(PACKAGE_LEAVE);
      sscanf(text, "%d", &c);
      
      if (c==current_player) {
         do {
            current_player++;
            if (current_player>=MAX_PLAYERS)
               current_player = 0;
         } while(!player[current_player].active);
         if (!is_netgame) {
            local_player = current_player;
         }
         put_man = FALSE;
         put_man_send = FALSE;
         bonus_turn = 0;
      }
   } else if (cmp_package_type(msg, PACKAGE_PLNAME)) {
      /* Set player name */
      text = msg+strlen(PACKAGE_PLNAME);
      sscanf(text, "%d,", &c);
      text = strstr(text, ",");
      if (text) {
         text++;
         if (text[0]) {
            snprintf(player[c].name, MSG_LENGTH, "%s", text);
         }
      }
      player[c].active = TRUE;
   } else if (player[local_player].active && cmp_package_type(msg, PACKAGE_CSCORE)) {
      text = msg+strlen(PACKAGE_CSCORE);
      sscanf(text, "%d,", &c);
      text = strstr(text, ",")+1;
      switch (c) {
         case 0:
            sscanf(text, "%d", &base_farm_multi);
            break;
         case 1:
            sscanf(text, "%d", &bonus_farm_multi);
            break;
         case 2:
            sscanf(text, "%d", &game_cathedral_multi);
            break;
         case 3:
            sscanf(text, "%d", &final_cathedral_multi);
            break;
         case 4:
            sscanf(text, "%d", &game_inn_multi);
            break;
         case 5:
            sscanf(text, "%d", &final_inn_multi);
            break;
         case 6:
            sscanf(text, "%d", &tradegood_bonus);
            break;
      }
   } else if (player[local_player].active && cmp_package_type(msg, PACKAGE_CHMEN)) {
      text = msg+strlen(PACKAGE_CHMEN);
      sscanf(text, "%d,", &c);
      text = strstr(text, ",")+1;
      sscanf(text, "%d", startmen + c);
   } else if (player[local_player].active && cmp_package_type(msg, PACKAGE_PTILE)) {
      /* Place a tile */
      text = msg+strlen(PACKAGE_PTILE);
      sscanf(text, "%d,%d,%d", &current_tile[current_player], &x, &y);

      place_tile(current_tile[current_player], x, y);
      connect_tile(x, y);
      last_tile = current_tile[current_player];
      last_tile_x = x;
      last_tile_y = y;

      put_man = TRUE;
      put_man_send = FALSE;
      current_tile[current_player] = NOTILE;
      place_msg_sent = FALSE;
      
      /* Check if this tile activated the builder for the current player */
      if (get_tile_feature_men(last_tile, player[current_player].team, MASTERBUILDER)) {
         bonus_turn++;
         had_bonus_turn = FALSE;
      }

      redraw_tilemap = TRUE;
      set_screen_changed();
   } else if (player[local_player].active && cmp_package_type(msg, PACKAGE_ROTL)) {
      /* Rotate tile */
      text = msg+strlen(PACKAGE_ROTL);
      sscanf(text, "%d", &current_tile[current_player]);
      rotate_big_tile_90(current_tile[current_player]);

      redraw_tilemap = TRUE;
      set_screen_changed();
   } else if (player[local_player].active && cmp_package_type(msg, PACKAGE_ROTR)) {
      /* Rotate tile */
      text = msg+strlen(PACKAGE_ROTR);
      sscanf(text, "%d", &current_tile[current_player]);
      rotate_big_tile_270(current_tile[current_player]);

      redraw_tilemap = TRUE;
      set_screen_changed();
   } else if (player[local_player].active && cmp_package_type(msg, PACKAGE_PMAN)) {
      /* Place a man */
      text = msg+strlen(PACKAGE_PMAN);
      sscanf(text, "%d,%d,%d,%d,%d,%d", &tx, &ty, &x, &y, &c, &n);
      place_man(tx, ty, x, y, c, n);

      redraw_tilemap = TRUE;
      set_screen_changed();
   } else if (player[local_player].active && cmp_package_type(msg, PACKAGE_ETURN)) {
      /* End turn */

      /* Update scores */
      /* we can do this by looking at features on the last tile placed and adjacent monasteries */
      //printf("ETURN: (%d, %d)\n", last_tile_x, last_tile_y);
      score_tile(last_tile_x, last_tile_y, current_player);

      /* Next player - unless current player gets a bonus */
      if (!bonus_turn || (bonus_turn>max_bonux_turns) || (had_bonus_turn)) {
         do {
            current_player++;
            if (current_player>=MAX_PLAYERS)
               current_player = 0;
         } while(!player[current_player].active);
         if (!is_netgame) {
            local_player = current_player;
         }
         bonus_turn = 0;
      }
      had_bonus_turn = TRUE;
      put_man = FALSE;
      put_man_send = FALSE;
      man_type = 0;
      redraw_tilemap = TRUE;

   }
}

/* Send a packet to all clients */
inline static void broadcast_message(const char *msg)
{
   if (!is_netgame) {
      /* Deal with message locally */
      handle_message(msg);
      return;
   }

   if (is_server) {
      //printf("%s\n", msg);
      /* Deal with message locally */
      handle_message(msg);

      /* Broadcast to other players */
      if (msg!=comm_buffer_out)
         snprintf(comm_buffer_out, PACKET_SIZE, "%s", msg);

      server_send_all(comm_buffer_out, strlen(comm_buffer_out)+1);
   } else {
      /* Send to server */
      if (msg!=comm_buffer_out)
         snprintf(comm_buffer_out, PACKET_SIZE, "%s", msg);

      client_send(comm_buffer_out, strlen(comm_buffer_out)+1);
   }
}

/* Send a text string to all clients */
inline static void broadcast_text(char *msg)
{
   if (!is_netgame || is_server) {
      /* Deal with text locally */
      if (msg!=comm_buffer_out)
         snprintf(comm_buffer_out, PACKET_SIZE, "%s#%d: %s", PACKAGE_TEXT, -1, msg);
   } else {
      /* Send to server */
      if (msg!=comm_buffer_out)
         snprintf(comm_buffer_out, PACKET_SIZE, "%s%s", PACKAGE_TEXT, msg);
   }

   broadcast_message(comm_buffer_out);
}

static inline void server_update(void)
{
   int new_conn;
   int n;

   /* Make the server listen for arrival of new connections */
   new_conn = server_listen();
   if (new_conn>0) {
      /* Send our version number to the client */
      snprintf(comm_buffer_out, PACKET_SIZE, "%s%s", PACKAGE_SVER, GAME_NET_COMPAT_VER);
      server_send(comm_buffer_out, strlen(comm_buffer_out)+1, new_conn-1);

      /* Send random number seed */
      snprintf(comm_buffer_out, PACKET_SIZE, "%s%d,%d", PACKAGE_INIT, randseed, num_players+1);
      server_send(comm_buffer_out, strlen(comm_buffer_out)+1, new_conn-1);

      /* Send names of other players */
      for (n=0; n<MAX_PLAYERS; n++) {
         if (player[n].active) {
            snprintf(comm_buffer_out, PACKET_SIZE, "%s%d,%s", PACKAGE_PLNAME, n, player[n].name);
            server_send(comm_buffer_out, strlen(comm_buffer_out)+1, new_conn-1);
         }
      }

      /* Send welcome message to new client */
      snprintf(comm_buffer_out, PACKET_SIZE, "%sWelcome!", PACKAGE_STEXT);
      server_send(comm_buffer_out, strlen(comm_buffer_out)+1, new_conn-1);

      if (game_started) {
         /* Inform the client that the game has already started */
         snprintf(comm_buffer_out, PACKET_SIZE, "%s", PACKAGE_LATE);
         server_send(comm_buffer_out, strlen(comm_buffer_out)+1, new_conn-1);

         server_disconnect_client(new_conn-1);
      } else {
         /* Inform the client that it has finished initializing and is ok to connect */
         snprintf(comm_buffer_out, PACKET_SIZE, "%s", PACKAGE_INITD);
         server_send(comm_buffer_out, strlen(comm_buffer_out)+1, new_conn-1);

         num_players++;
      }
   }

   /* Get messages */
   while (server_receive(comm_buffer_in, &sender)) {
      /* Handle client messages */

      /* Interpret message */
      if (cmp_package_type(comm_buffer_in, PACKAGE_CQUIT)) {
         /* Client has left the game */
         server_disconnect_client(sender);

         snprintf(comm_buffer_out, PACKET_SIZE, "%s%d", PACKAGE_LEAVE, sender+1);
         broadcast_message(comm_buffer_out);
         handle_message(comm_buffer_out);
         
         snprintf(comm_buffer_out, PACKET_SIZE, "%s%s has quit the game!", PACKAGE_STEXT, player[sender+1].name);
         broadcast_message(comm_buffer_out);
         
         set_screen_changed();
         player[sender+1].active = FALSE;
         player[sender+1].connected = FALSE;
         num_players--;
      } else if (cmp_package_type(comm_buffer_in, PACKAGE_LEAVE)) {
      
         /* Handle locally */
         handle_message(comm_buffer_in);
         
         /* Send server message to all clients */
         snprintf(comm_buffer_out, PACKET_SIZE, "%s%s has left the game!", PACKAGE_STEXT, player[sender+1].name);
         broadcast_message(comm_buffer_out);
         
         set_screen_changed();
         player[sender+1].active = FALSE;

         if (current_player == sender+1) {
            do {
               current_player++;
               if (current_player>=MAX_PLAYERS)
                  current_player = 0;
            } while(!player[current_player].active);
            put_man = FALSE;
            put_man_send = FALSE;
            bonus_turn = 0;
         }
      } else if (cmp_package_type(comm_buffer_in, PACKAGE_TEXT)) {
         char *text;

         text = comm_buffer_in+strlen(PACKAGE_TEXT);

         /* Pass message on to clients */
         snprintf(comm_buffer_out, PACKET_SIZE, "%s#%d: %s", PACKAGE_TEXT, sender, text);
         server_send_all(comm_buffer_out, strlen(comm_buffer_out)+1);

         /* Handle locally */
         handle_message(comm_buffer_out);

      } else if (cmp_package_type(comm_buffer_in, PACKAGE_CJOIN)) {
         /* Tell all clients to activate this player */
         snprintf(comm_buffer_out, PACKET_SIZE, "%s%d", PACKAGE_SJOIN, sender+1);
         broadcast_message(comm_buffer_out);

         /* Send server message to all clients */
         snprintf(comm_buffer_out, PACKET_SIZE, "%s%s has joined the game!", PACKAGE_STEXT, player[sender+1].name);
         broadcast_message(comm_buffer_out);
      } else {//if (cmp_package_type(comm_buffer_in, PACKAGE_PTILE) || cmp_package_type(comm_buffer_in, PACKAGE_ROTL) || cmp_package_type(comm_buffer_in, PACKAGE_ROTR) || cmp_package_type(comm_buffer_in, PACKAGE_PMAN) || cmp_package_type(comm_buffer_in, PACKAGE_ETURN) || cmp_package_type(comm_buffer_in, PACKAGE_PLNAME) || cmp_package_type(comm_buffer_in, PACKAGE_SJOIN)) {
         /* Place a tile, rotate a tile, place a man, end turn */

         /* Tell all clients (this includes the server) */
         broadcast_message(comm_buffer_in);
      }
   }
}

static inline void client_update(void)
{
   /* Make sure we are still connected */
   if (client_is_connected()<=0)
      printf("Server is not responding!");

   /* Get messages */
   while (client_receive(comm_buffer_in)) {
      /* Handle locally */
      handle_message(comm_buffer_in);
   }
}

/******************/
/* Main game menu */
/******************/

static char score_string[7][6];
static char mencount_string[MENTYPES][6];
static int slot_used[8];
   
/* Initialize game */
/* Returns non-zero on succes */
static int newgame_init(void)
{
   int n = 0;
   
   /* Clear message list */
   for(n=0; n<MAX_MSGS; n++)
      game_msg[n].text[0] = '\0';

   /* Initialize player datastructure */
   for (n=0; n<8/*get_num_team_names()*/; n++) {
      player_names[n] = player[n].name;
      player[n].active = FALSE;
      player[n].connected = FALSE;

      snprintf(player_names[n], MSG_LENGTH, "Player %d", n+1);

      slot_used[n] = 0;
   }
   slot_used[0] = 1;

   /* Local setting: auto end turn */
   auto_end_turn = get_config_int("game", "autoendturn", 1);
   
   startmen[PEASANTS] = get_config_int("pawns", "peasants", startmen[PEASANTS]);
   startmen[BIGMEN] = get_config_int("pawns", "bigmen", startmen[BIGMEN]);
   startmen[MASTERBUILDERS] = get_config_int("pawns", "builders", startmen[MASTERBUILDERS]);
   startmen[PIGS] = get_config_int("pawns", "pigs", startmen[PIGS]);

   base_farm_multi = get_config_int("score", "farm", base_farm_multi);
   bonus_farm_multi = get_config_int("score", "farm_bonus", bonus_farm_multi);
   game_cathedral_multi = get_config_int("score", "cathedral", game_cathedral_multi);
   final_cathedral_multi = get_config_int("score", "cathedral_final", final_cathedral_multi);
   game_inn_multi = get_config_int("score", "inn", game_inn_multi);
   final_inn_multi = get_config_int("score", "inn_final", final_inn_multi);
   tradegood_bonus = get_config_int("score", "tradegoods", tradegood_bonus);

   /* Initialization */
   /* Initialization based on the game type */
   switch (game_type) {
      case 1:     /* Local game */
         is_netgame = FALSE;
         num_players = 1;
         local_player = 0;
         player[local_player].active = TRUE;
         break;

      case 2:     /* Network game - we are server */
         /* Initialize network */
         network_init(NULL);
         is_server = TRUE;
         is_netgame = TRUE;
         num_players = 1;
         local_player = 0;
         player[local_player].active = TRUE;
         break;

      case 3:     /* Network game - we are client */
         /* Initialize network */
         network_init(NULL);
         is_server = FALSE;
         is_netgame = TRUE;
         break;
   }

   /* Network options */

   /* Initialization based on the game type */
   switch (game_type) {
      case 1:     /* Local game */
         break;

      case 2:     /* Network game - we are server */
         /* Initialize network */
         popup_network_options(server_ip_address, localname, &network_driver_nr);

         if (!start_server(network_driver_nr)) {
            game_message("Error - cannot start server!\n");
            return 0;
         }

         //if (localname[0]) {
         //   snprintf(player[local_player].name, MSG_LENGTH, "%s", localname);
         //}
         snprintf(comm_buffer_out, PACKET_SIZE, "%s%d,%s", PACKAGE_PLNAME, num_players-1, localname);
         broadcast_message(comm_buffer_out);
         break;

      case 3:     /* Network game - we are client */
         /* Initialize network */
         client_init_done = FALSE;
         popup_network_options(server_ip_address, localname, &network_driver_nr);

         if (!start_client(server_ip_address, network_driver_nr)) {
            game_message("Error - cannot start client!\n");
            return 0;
         }

         while (client_is_connected() <= 0 && !key[KEY_ESC]);
         if (key[KEY_ESC] || client_is_connected() < 0)
            return 0;

         /* Process startup messages from server */
         do {
            client_update();
            if (!client_init_done && game_started) {
               game_message("Unable to join.\nThe game has already started.");
               return 0;
            }
            if (key[KEY_ESC]) {
               return 0;
            }
         } while (!client_init_done);

         snprintf(comm_buffer_out, PACKET_SIZE, "%s%d,%s", PACKAGE_PLNAME, num_players-1, localname);
         broadcast_message(comm_buffer_out);

         snprintf(comm_buffer_out, PACKET_SIZE, "%s", PACKAGE_CJOIN);
         broadcast_message(comm_buffer_out);
         break;
   }
   
   return -1;
}

/* Perform cleanup operations for this game type */
static void game_shutdown(void)
{
   switch (game_type) {
      case 1:     /* Local game */
         break;
      case 2:     /* Network game - we are server */
         /* Send server message to all clients */
         snprintf(comm_buffer_out, PACKET_SIZE, "%sServer is shutting down!", PACKAGE_STEXT);
         broadcast_message(comm_buffer_out);
         broadcast_message(PACKAGE_SQUIT);
         shutdown_server();
         break;
      case 3:     /* Network game - we are client */
         broadcast_message(PACKAGE_CQUIT);
         shutdown_client();
         break;
   }
}

/* Returns 0 if the game was canceled, 1 if the game can start */
static int newgame_menu(void)
{
   int n, c, ret = 0;
   
   game_started = FALSE;

   /* Reinitialize random number seed */
   randseed = time(NULL);
   
   /* Game parameters menu and player selection */
   new_msg = TRUE;
   user_msg[0] = '\0';
   //init_game_params_dialog(player_names, MSG_LENGTH, slot_used, is_netgame, is_server);

   /* Initilize game options dialog structure */
   
   /* Client can't start the game */
   game_params_dialog[2].flags&=~D_DISABLED;
   if (is_netgame && !is_server) {
      game_params_dialog[2].flags |= D_DISABLED;
   }
   for(c=0; c<8; c++) {
      game_params_dialog[GP_PLAYER_NAME+c].dp = player_names[c];
      game_params_dialog[GP_PLAYER_NAME+c].d1 = MSG_LENGTH;
      
      if (slot_used[c])
         game_params_dialog[GP_PLAYER_CHECK+c].flags|=D_SELECTED;
      else
         game_params_dialog[GP_PLAYER_CHECK+c].flags&=~D_SELECTED;
      
      game_params_dialog[GP_PLAYER_NAME+c].flags&=~D_DISABLED;
      if (is_netgame) {
         game_params_dialog[GP_PLAYER_CHECK+c].flags|=D_DISABLED;
         game_params_dialog[GP_PLAYER_NAME+c].flags|=D_DISABLED;
      }
   }
   for (c=0; c<7; c++) {
      score_string[c][0] = '\0';
      snprintf(score_string[c], 5, "%d", 0);
      game_params_dialog[GP_SCORES+c].dp = score_string[c];
      game_params_dialog[GP_SCORES+c].fg = white;
      game_params_dialog[GP_SCORES+c].bg = darkgrey;
      game_params_dialog[GP_SCORES+c].d1 = 4;
      game_params_dialog[GP_SCORES+c].d2 = strlen(game_params_dialog[GP_SCORES+c].dp);

      game_params_dialog[GP_SCORES+c].flags&=~D_DISABLED;
      game_params_dialog[GP_CAPTION+c].flags&=~D_DISABLED;
      
      if (is_netgame && !is_server) {
         game_params_dialog[GP_SCORES+c].flags|=D_DISABLED;
         game_params_dialog[GP_CAPTION+c].flags|=D_DISABLED;
      }
   }
   snprintf(score_string[0], 5, "%d", base_farm_multi);
   snprintf(score_string[1], 5, "%d", bonus_farm_multi);
   snprintf(score_string[2], 5, "%d", game_cathedral_multi);
   snprintf(score_string[3], 5, "%d", final_cathedral_multi);
   snprintf(score_string[4], 5, "%d", game_inn_multi);
   snprintf(score_string[5], 5, "%d", final_inn_multi);
   snprintf(score_string[6], 5, "%d", tradegood_bonus);
   
   for (c=0; c<MENTYPES; c++) {
      snprintf(mencount_string[c], 2, "%d", startmen[c]);

      game_params_dialog[GP_MEN+c].dp = mencount_string[c];
      game_params_dialog[GP_MEN+c].fg = white;
      game_params_dialog[GP_MEN+c].bg = darkgrey;
      game_params_dialog[GP_MEN+c].d1 = 4;
      game_params_dialog[GP_MEN+c].d2 = strlen(game_params_dialog[GP_MEN+c].dp);

      game_params_dialog[GP_MEN+c].flags&=~D_DISABLED;
      game_params_dialog[GP_MEN_CAPTION+c].flags&=~D_DISABLED;
      
      if (is_netgame && !is_server) {
         game_params_dialog[GP_MEN+c].flags|=D_DISABLED;
         game_params_dialog[GP_MEN_CAPTION+c].flags|=D_DISABLED;
      }
   }
   
   if (auto_end_turn) {
      game_params_dialog[GP_AUTO_ENDT].flags|=D_SELECTED;
   } else {
      game_params_dialog[GP_AUTO_ENDT].flags&=~D_SELECTED;
   }
   
   tileset_flags = realloc(tileset_flags, get_num_cts_files()*sizeof *tileset_flags);
   for(n=0; n<get_num_cts_files(); n++) {
      CTS_FILE_INFO *cts = get_cts_info(n);
      char *s;
      s = strdup(cts->name);
      for(c=0; s[c]; c++)
         if (s[c] == ' ')
            s[c] = '_';
      if (get_config_int("tileset", s, 0)) {
         tileset_flags[n] = 1;
         game_params_dialog[GP_TILESET].d1 = n;
      } else {
         tileset_flags[n] = 0;
      }
      free(s);
   }
   game_params_dialog[GP_TILESET].dp2 = tileset_flags;
   
   if (is_netgame && !is_server) {
      game_params_dialog[GP_TILESET].flags |= D_DISABLED;
      game_params_dialog[GP_STARTTILE].flags |= D_DISABLED;
   }
   
   show_mouse(screen);
   game_popup_dialog(game_params_dialog, -1, FALSE);

   while (dialog_open() && !close_button_pressed) {
      ret = game_update_dialog();
      /* Synchronize data */
      for(c=0; c<8; c++) {
         if ((global_cycles_passed&31) == 0)
            game_params_dialog[GP_PLAYER_NAME+c].flags|=D_DIRTY;
         
         if (!is_netgame) {
            if (game_params_dialog[GP_PLAYER_CHECK+c].flags&D_SELECTED) {
               if (slot_used[c] != 1)
                  game_params_dialog[GP_PLAYER_CHECK+c].flags|=D_DIRTY;
               slot_used[c] = 1;
            } else {
               if (slot_used[c] != 0)
                  game_params_dialog[GP_PLAYER_CHECK+c].flags|=D_DIRTY;
               slot_used[c] = 0;
            }
         }
      }
      
      /* Synchronize men counts */
      if (is_netgame) {
         for (n=0; n<MENTYPES; n++) {
            sscanf(mencount_string[n], "%d", &c);
            if (startmen[n]!=c) {
               if (is_server) {  /* Input field is correct */
                  snprintf(comm_buffer_out, PACKET_SIZE, "%s%d,%d", PACKAGE_CHMEN, n, c);
                  broadcast_message(comm_buffer_out);
               } else {          /* Numerical value is correct */
                  snprintf(mencount_string[n], 5, "%d", startmen[n]);
                  game_params_dialog[GP_MEN+n].flags|=D_DIRTY;
               }
            }
         }
      }
      
      /* Synchronize scores */
      if (is_netgame) {
         sscanf(score_string[0], "%d", &c);
         n = base_farm_multi;
         if (n!=c) {
            if (is_server) {  /* Input field is correct */
               snprintf(comm_buffer_out, PACKET_SIZE, "%s%d,%d", PACKAGE_CSCORE, 0, c);
               broadcast_message(comm_buffer_out);
            } else {          /* Numerical value is correct */
               snprintf(score_string[0], 5, "%d", n);
               game_params_dialog[GP_SCORES+0].flags|=D_DIRTY;
            }
         }

         sscanf(score_string[1], "%d", &c);
         n = bonus_farm_multi;
         if (n!=c) {
            if (is_server) {  /* Input field is correct */
               snprintf(comm_buffer_out, PACKET_SIZE, "%s%d,%d", PACKAGE_CSCORE, 1, c);
               broadcast_message(comm_buffer_out);
            } else {          /* Numerical value is correct */
               snprintf(score_string[1], 5, "%d", n);
               game_params_dialog[GP_SCORES+1].flags|=D_DIRTY;
            }
         }

         sscanf(score_string[2], "%d", &c);
         n = game_cathedral_multi;
         if (n!=c) {
            if (is_server) {  /* Input field is correct */
               snprintf(comm_buffer_out, PACKET_SIZE, "%s%d,%d", PACKAGE_CSCORE, 2, c);
               broadcast_message(comm_buffer_out);
            } else {          /* Numerical value is correct */
               snprintf(score_string[2], 5, "%d", n);
               game_params_dialog[GP_SCORES+2].flags|=D_DIRTY;
            }
         }

         sscanf(score_string[3], "%d", &c);
         n = final_cathedral_multi;
         if (n!=c) {
            if (is_server) {  /* Input field is correct */
               snprintf(comm_buffer_out, PACKET_SIZE, "%s%d,%d", PACKAGE_CSCORE, 3, c);
               broadcast_message(comm_buffer_out);
            } else {          /* Numerical value is correct */
               snprintf(score_string[3], 5, "%d", n);
               game_params_dialog[GP_SCORES+3].flags|=D_DIRTY;
            }
         }

         sscanf(score_string[4], "%d", &c);
         n = game_inn_multi;
         if (n!=c) {
            if (is_server) {  /* Input field is correct */
               snprintf(comm_buffer_out, PACKET_SIZE, "%s%d,%d", PACKAGE_CSCORE, 4, c);
               broadcast_message(comm_buffer_out);
            } else {          /* Numerical value is correct */
               snprintf(score_string[4], 5, "%d", n);
               game_params_dialog[GP_SCORES+4].flags|=D_DIRTY;
            }
         }

         sscanf(score_string[5], "%d", &c);
         n = final_inn_multi;
         if (n!=c) {
            if (is_server) {  /* Input field is correct */
               snprintf(comm_buffer_out, PACKET_SIZE, "%s%d,%d", PACKAGE_CSCORE, 5, c);
               broadcast_message(comm_buffer_out);
            } else {          /* Numerical value is correct */
               snprintf(score_string[5], 5, "%d", n);
               game_params_dialog[GP_SCORES+5].flags|=D_DIRTY;
            }
         }

         sscanf(score_string[6], "%d", &c);
         n = tradegood_bonus;
         if (n!=c) {
            if (is_server) {  /* Input field is correct */
               snprintf(comm_buffer_out, PACKET_SIZE, "%s%d,%d", PACKAGE_CSCORE, 6, c);
               broadcast_message(comm_buffer_out);
            } else {          /* Numerical value is correct */
               snprintf(score_string[6], 5, "%d", n);
               game_params_dialog[GP_SCORES+6].flags|=D_DIRTY;
            }
         }
      }
      
      /* Make sure some tilesets are actually selected */
      /* Disable the start button if there aren't */
      if (!is_netgame || is_server) {
         c = 0;
         for(n=0; n<get_num_cts_files(); n++)
            if (tileset_flags[n])
               c++;
         if (!c) {
            if (!(game_params_dialog[2].flags & D_DISABLED))
               game_params_dialog[2].flags |= D_DIRTY;
            game_params_dialog[2].flags |= D_DISABLED;
         } else if (game_params_dialog[2].flags & D_DISABLED) {
            game_params_dialog[2].flags ^= D_DISABLED;
            game_params_dialog[2].flags |= D_DIRTY;
         }
      }

      /* Message input */
      if (is_netgame) {
         if (mouse_y>chat_y && keypressed()) {
            c = readkey();
            switch (c >> 8) {
               case KEY_ENTER:
                  if (strlen(user_msg)) {
                     /* Send chat message */
                     broadcast_text(user_msg);
                     user_msg[0] = '\0';
                     new_msg = TRUE;
                  }
                  break;
               case KEY_BACKSPACE:
                  if (enter_msg) {
                     user_msg[uoffset(user_msg, -1)] = '\0';
                     new_msg = TRUE;
                  }
               default:
                  if ((c&0xFF)>31 && (c&0xFF)<128) {    /* Append to string */
                     uszprintf(user_msg, MSG_LENGTH, "%s%c", user_msg, c&0xFF);
                     new_msg = TRUE;
                  }
            }
         }
         /* Message window */
         /* Recent messages */
         if (new_msg) {
            rectfill(screen, 0, 474, mw_width, SCREEN_H-text_height(font)-4, darkgrey);
            rect(screen, 0, 474, mw_width-1, SCREEN_H-1, grey);
            for(n=0; n<MAX_MSGS; n++) {
               textprintf(screen, gamemsg_font, 2+1, chat_y+1 + n*text_height(gamemsg_font), black, "%s", game_msg[(msg_offset+n)%MAX_MSGS].text);
               textprintf(screen, gamemsg_font, 2, chat_y + n*text_height(gamemsg_font), game_msg[(msg_offset+n)%MAX_MSGS].colour, "%s", game_msg[(msg_offset+n)%MAX_MSGS].text);
            }

            /* Input message */
            text_mode(-1);
            rectfill(screen, 0,SCREEN_H-text_height(font)-4, SCREEN_W-1,SCREEN_H-1, blue);
            textprintf(screen, font, 2, SCREEN_H-text_height(font)-2, yellow, "Msg:%s_", user_msg);
            rect(screen, 0,SCREEN_H-text_height(font)-4, SCREEN_W-1,SCREEN_H-1, grey);
            new_msg = FALSE;
         }
      }

      switch (game_type) {
         case 1:     /* Local game */
            num_players = 0;
            for(n=0; n<8; n++) {
               if (slot_used[n]) {
                  num_players++;
               }
            }
            break;

         case 2:     /* Network game - we are server */
            server_update();
            break;

         case 3:     /* Network game - we are client */
            client_update();
            if (game_started)
               game_close_dialog();
            break;
      }
   }

   show_mouse(NULL);

   if (ret == -1 || ret == 3 || close_button_pressed) {
      return 0;
   }

   if (!is_netgame) {
      if (!num_players) {
         slot_used[0] = 1;
         num_players = 1;
      }
      for(n=0; n<MAX_PLAYERS; n++) {
         if (slot_used[n])
            player[n].active = TRUE;
         else
            player[n].active = FALSE;
      }
   }

   if (!is_netgame || is_server) {
      /* Send scores */
      for(n=0; n<7; n++) {
         sscanf(score_string[n], "%d", &c);
         snprintf(comm_buffer_out, PACKET_SIZE, "%s%d,%d", PACKAGE_CSCORE, n, c);
         broadcast_message(comm_buffer_out);
      }
      
      /* Send men counts */
      for(n=0; n<MENTYPES; n++) {
         sscanf(mencount_string[n], "%d", &c);
         snprintf(comm_buffer_out, PACKET_SIZE, "%s%d,%d", PACKAGE_CHMEN, n, c);
         broadcast_message(comm_buffer_out);
      }

      /* Random number seed */      
      snprintf(comm_buffer_out, PACKET_SIZE, "%s%d", PACKAGE_SRAN, randseed);
      broadcast_message(comm_buffer_out);

      /* Start tileset */
      c = -1;
      for (n=0; n<get_num_cts_files(); n++) {
         CTS_FILE_INFO *cts = get_cts_info(n);
         if (cts->num_start_tiles) {
            c++;
         }
         if (c==game_params_dialog[GP_STARTTILE].d1) {
            snprintf(comm_buffer_out, PACKET_SIZE, "%s%s", PACKAGE_STILE, cts->name);
            broadcast_message(comm_buffer_out);
            break;
         }
      }
      
      /* Send tileset info */
      for(n=0; n<get_num_cts_files(); n++) {
         CTS_FILE_INFO *cts = get_cts_info(n);
         if (tileset_flags[n]!=0) {
            snprintf(comm_buffer_out, PACKET_SIZE, "%s\"%s\"", PACKAGE_TILESET, cts->name);
            broadcast_message(comm_buffer_out);
         }
      }

      snprintf(comm_buffer_out, PACKET_SIZE, "%s", PACKAGE_START);
      broadcast_message(comm_buffer_out);
   }

   /* Save options */   
   auto_end_turn = (game_params_dialog[GP_AUTO_ENDT].flags&D_SELECTED)!=0;
   set_config_int("game", "autoendturn", auto_end_turn);

   /* Save game score options if we're server */
   if (!is_netgame || is_server) {
      set_config_int("pawns", "peasants", startmen[PEASANTS]);
      set_config_int("pawns", "bigmen", startmen[BIGMEN]);
      set_config_int("pawns", "builders", startmen[MASTERBUILDERS]);
      set_config_int("pawns", "pigs", startmen[PIGS]);
   
      set_config_int("score", "farm", base_farm_multi);
      set_config_int("score", "farm_bonus", bonus_farm_multi);
      set_config_int("score", "cathedral", game_cathedral_multi);
      set_config_int("score", "cathedral_final", final_cathedral_multi);
      set_config_int("score", "inn", game_inn_multi);
      set_config_int("score", "inn_final", final_inn_multi);
      set_config_int("score", "tradegoods", tradegood_bonus);

      for(n=0; n<get_num_cts_files(); n++) {
         CTS_FILE_INFO *cts = get_cts_info(n);
         char *s;
         s = strdup(cts->name);
         for(c=0; s[c]; c++)
            if (s[c] == ' ')
               s[c] = '_';
         set_config_int("tileset", s, tileset_flags[n]!=0);
         free(s);
      }
   }

   return 1;
}

/* returns 0 when `exit' is chosen, or non-zero if gameplay can begin */
static int game_menu(void)
{
   BITMAP *bmp;
   PALETTE pal;
   FONT *f;
   int focus;
   int res;
   int mv;
   int v;

   /* Load title background */
   bmp = get_title_screen(pal);
   set_palette(*get_game_palette());
   select_palette(*get_game_palette());

   /* Select menu font */
   f = font;
   font = (FONT *)get_menu_font();

   /* Set the transparent blender */
   set_trans_blender(0, 0, 0, 128);

   /* Show the background */
   prepare_screen_update();
   stretch_blit(bmp, get_screen_bmp(), 0, 0, bmp->w, bmp->h, 0, 0, SCREEN_W,
                SCREEN_H);
   font = (FONT *)get_small_font();
   textprintf(get_screen_bmp(), font, 2, SCREEN_H - text_height(font), white,
              GAME_VERSTR);
   mark_rect(0, 0, SCREEN_W, SCREEN_H);
   screen_update_done();

   font = (FONT *)get_menu_font();

   /* Start title music */
   stop_music();
   mv = get_music_volume();
   set_music_playlist(title_playlist);

   if ((focus=find_dialog_focus(main_menu_dialog)) == -1) {
      focus = 1;
   }

   select_mouse_cursor(-1);
   do {
      show_mouse(get_monitor_bmp());

      res = 0;
      game_popup_dialog(main_menu_dialog, focus, FALSE);
      while (dialog_open() && !close_button_pressed) {
         res = game_update_dialog();
      }
      if (close_button_pressed)
         res = -1;
      switch (res) {
         case 1:               /* New game */
            show_mouse(NULL);
            stop_music();
            set_music_volume(mv);
            font = f;
            game_type = popup_dialog(newgame_menu_dialog, -1);
            if (game_type<4) {
               return -1;
            }
            /*
            if (game_type<4) {
               if (newgame_menu()!=0) {
                  return -1;
               }
            }
            */
            break;
         case 2:               /* options */
            popup_options();
            mv = get_music_volume();
            break;
         default:
            break;
      }                         /* end of switch */
      show_mouse(NULL);
   } while (res != 3 && res != -1 && settings.window_close_button == 0);

   /* Fade out title music and stop */
   while ((v = get_music_volume())>0) {
      set_music_volume(--v);
   }
   stop_music();
   /* Restore original settings */
   set_music_volume(mv);
   font = f;
   return 0;
}

/* Draw a leader-board */
static BITMAP *draw_leader_board(void)
{
   int compare_player_scores(const void *p1, const void *p2)
   {
      int n1 = *(int *)p1;
      int n2 = *(int *)p1;

      return get_team_score(player[n2].team) - get_team_score(player[n1].team);
   }
   int width, height;
   int players_sorted[MAX_PLAYERS];
   const RLE_SPRITE *sprite;
   BITMAP *bmp;
   const FONT *namefont;
   const FONT *scorefont;
   int n, x, y, c;
   
   width = 578;
   height = 104 + num_players*24;
   
   bmp = create_bitmap(width, height);
   clear_to_color(bmp, grey);
   
   rect(bmp, 0,0, width-1, height-1, black);
   
   for(n=0; n<MAX_PLAYERS; n++)
      players_sorted[n] = n;
   qsort(players_sorted, MAX_PLAYERS, sizeof *players_sorted, compare_player_scores);
   
   set_palette(*get_font_palette());
   set_palette(grey_palette);
   textprintf_centre(bmp, get_colour_font(), width/2, 4, -1, "Scores");
   
   namefont = get_colour_font();
   scorefont = get_colour_font();

   y = 32;
   x = 128;
   for(n=0; n<FEATURE_TYPES; n++) {
      sprite = get_team_man_sprite(0, 0);
      draw_rle_sprite(bmp, sprite, x + (64-sprite->w)/2, y);
      sprite = get_feature_man_attribute(n);
      draw_rle_sprite(bmp, sprite, x + (64-sprite->w)/2, y);
      x+=64;
   }

   for(n=0; n<3; n++) {
      BITMAP *spr = get_goods_bitmap(n+1);
      draw_sprite(bmp, spr, x + (32-spr->w)/2, y+2);
      x+=32;
   }
   
   y = 56;
   for(n=0; n<MAX_PLAYERS; n++) {
      if (player[players_sorted[n]].active) {
         //printf ("%d\n", players_sorted[n]);
         textprintf(bmp, namefont, 16, y, -1, "%s", player[players_sorted[n]].name);

         x = 128+64;
         for(c=0; c<FEATURE_TYPES; c++) {
            rect(bmp, x-64, y, x, y+24, black);
            textprintf_right(bmp, scorefont, x, y, -1, "%d", get_partial_team_score(player[players_sorted[n]].team, c));
            x+=64;
         }

         x-=32;
         for(c=0; c<3; c++) {
            rect(bmp, x-32, y, x, y+24, black);
            textprintf_right(bmp, scorefont, x, y, -1, "%d", get_team_good_count(player[players_sorted[n]].team, c));
            x+=32;
         }
         
         rect(bmp, x-64, y, x, y+24, black);
         textprintf_right(bmp, scorefont, x, y, -1, "%d", player[players_sorted[n]].score);
         y+=24;
      }
   }

   return bmp;   
}

/******************/
/* Initialization */
/******************/

inline void playgame_preamble(void)
{
   int n;

   /* Clear the screen */
   clear_bitmap(screen);
   statusbar = create_bitmap(SCREEN_W, 32);

   /* Set colour effects: blender function and fonr palettes */
   set_trans_blender(0,128,0,128);
   shift_palette_hue(*get_font_palette(), gold_palette, 45.00f);
   //shift_palette_hue(*get_font_palette(), grey_palette, 0.00f);
   for (n=128; n<256; n++) {
      grey_palette[n].r = 
         grey_palette[n].g = 
            grey_palette[n].b = 63*(164+92*(n-128)/127)/255;
   }
   

   /* Initialize memory pools */
   /* Initialize general linked list structure */
   init_lists();

   /* Set the music we want */
   set_music_playlist(game_playlist);

   /* Reset random number generator */
   sgenrand(randseed);

   /* Prepare tileset */
   theme_file = get_config_string("game", "theme", "deftheme.dat");
   load_theme(theme_file);
   initialize_tiles();
   theme_file = NULL;

   /* Clear message list */
   for(n=0; n<MAX_MSGS; n++)
      game_msg[n].text[0] = '\0';

   /* This game was just started */
   new_game = TRUE;

   game_state = GAME_STATE_NORMAL;
   game_state_counter = 0;
   game_state_options = 0;

   initialize_tileset();
   initialize_gameboard();
   initialize_teams(startmen[0], startmen[1], startmen[2], startmen[3]);

   for (n=0; n<get_num_team_names(); n++) {
      //snprintf(player[n].name, MSG_LENGTH, "%s", player_names[n]);
      player[n].team = n;
      player[n].team_name = get_team_names(n);
      player[n].score = 0;
   }

   /* Initialize map settings */
   map_x = (MAP_CX-7)*TILE_SIZE;
   map_y = (MAP_CY-5)*TILE_SIZE;
   
   map_cursor_x = map_x;
   map_cursor_y = map_y;

   map_mouse_down = FALSE;

   for(n=0; n<MAX_PLAYERS; n++)
      current_tile[n] = NOTILE;
   place_msg_sent = FALSE;

   last_tile_x = 0;
   last_tile_y = 0;
   put_man = FALSE;
   man_type = 0;
   
   bonus_turn = 0;
   had_bonus_turn = FALSE;

   turn_button_x = panel_x + 16;
   turn_button_y = chat_y-24;
   turn_button_w = 64;
   turn_button_h = 2+text_height(get_menu_bold_font());

   rol_button_x = panel_x + 21;
   rol_button_y = chat_y - 32-22;
   ror_button_x = rol_button_x + 32;
   ror_button_y = rol_button_y;

   man_button_x = panel_x + 1;
   man_button_y = 24;

   current_player = 0;
   try_place = FALSE;
   game_done = FALSE;
   display_score = FALSE;
   put_man_send = FALSE;

   for (n=0; n<8; n++) {
      if (player[n].connected)
         player[n].active = TRUE;
   }
   player[local_player].active = TRUE;

   map_cursor_mouse = 1;

   tilemap = create_bitmap(panel_x, chat_y);
   redraw_tilemap = TRUE;

   /* Clear the display */
   prepare_screen_update();
   clear_bitmap(get_screen_bmp());
   mark_rect(0, 0, SCREEN_W, SCREEN_H);
   screen_update_done();

   show_mouse(get_monitor_bmp());
}

/*****************/
/* Shutdown code */
/*****************/

inline void playgame_closing(void)
{
   show_mouse(NULL);

   prepare_screen_update();
   clear_bitmap(get_screen_bmp());
   mark_rect(0, 0, SCREEN_W, SCREEN_H);
   screen_update_done();

   unload_theme();
   unload_tileset();
   destroy_tiles();

   destroy_teams();

   destroy_bitmap(statusbar);
   statusbar = NULL;

   destroy_bitmap(tilemap);
   tilemap = NULL;
   redraw_tilemap = FALSE;

   free_lists();

   while (keypressed())
      readkey();
}

/*******************/
/* Graphics update */
/*******************/

inline void game_gfx_update_preamble(void)
{
}

inline void game_gfx_update(void)
{
   const RLE_SPRITE *sprite;
   BITMAP *bmp;
   int n;
   int x, y;
   int bg, fg;

   prepare_screen_update();


   /****************/
   /* Draw tilemap */
   /****************/
   if (redraw_tilemap) {
      draw_tilemap(tilemap, current_tile[local_player],
                     -abs(map_x)%TILE_SIZE, -abs(map_y)%TILE_SIZE,
                     map_x/TILE_SIZE,map_y/TILE_SIZE, SCREEN_W/TILE_SIZE, SCREEN_H/TILE_SIZE);
      redraw_tilemap = FALSE;
   }

   set_clip(get_screen_bmp(), 0,0, panel_x-1, chat_y-1);
   blit(tilemap, get_screen_bmp(), 0,0, 0,0, tilemap->w,tilemap->h);

   x = TILE_SIZE*(last_tile_x)-map_x;
   y = TILE_SIZE*(last_tile_y)-map_y;
   rect(get_screen_bmp(), x,y, x+TILE_SIZE,y+TILE_SIZE, blue);
   if (put_man && current_player==local_player) {
      draw_flags(get_screen_bmp(), x, y, last_tile_x, last_tile_y, local_player, man_type);
   }

   /* Draw mouse cursor on the tilemap */
   x = TILE_SIZE*(map_cursor_x)-map_x;
   y = TILE_SIZE*(map_cursor_y)-map_y;

   if (current_tile[local_player] != NOTILE)
      draw_trans_sprite(get_screen_bmp(), get_tile_bitmap(current_tile[local_player]), x,y);
   rect(get_screen_bmp(), x,y, x+TILE_SIZE,y+TILE_SIZE, yellow);

   /* Draw a hovering man if the player can place a man on a tile */
   if (put_man && current_player==local_player) {
      x = mouse_x;
      y = mouse_y;
      if (in_rect(map_mouse_x,map_mouse_y, last_tile_x*3, last_tile_y*3,
                                  2, 2)) {
         if (can_place_flag(last_tile_x, last_tile_y, map_mouse_x%3, map_mouse_y%3, player[current_player].team, man_type)) {
            const RLE_SPRITE *sprite = get_team_man_sprite(player[current_player].team, 0);
            draw_rle_sprite(get_screen_bmp(), sprite,
                            map_mouse_x*(TILE_SIZE/3) - map_x, (map_mouse_y+1)*(TILE_SIZE/3) - map_y - sprite->h);
         }
      }
   }
   set_clip(get_screen_bmp(), 0,0, SCREEN_W-1, SCREEN_H-1);
   mark_rect(0,0, panel_x-1, chat_y-1);

   /*****************/
   /* Draw controls */
   /*****************/

   /* Game controls */
   rectfill(get_screen_bmp(), panel_x, 0, SCREEN_W-1, chat_y-1, darkgrey);
   rect(get_screen_bmp(), panel_x, 0, SCREEN_W-1, chat_y-1, grey);
   mark_rect(panel_x, 0, SCREEN_W-1, chat_y-1);

   /* Score */
   set_palette(gold_palette);
   textprintf_right(get_screen_bmp(), get_colour_font(), SCREEN_W-4, 1, -1, "%d", player[local_player].score);

   if (display_score) {
      bmp = draw_leader_board();
      draw_sprite(get_screen_bmp(), bmp, (SCREEN_W-bmp->w)/2, (SCREEN_H-bmp->h)/3);
      destroy_bitmap(bmp);
   }

   /* Men remaining */
   for (n=0; n<4; n++) {
      if (n==man_type)
         bmp = get_button_bitmap(2);
      else
         bmp = get_button_bitmap(1);

      draw_sprite(get_screen_bmp(), bmp, man_button_x+n*24, man_button_y);
      sprite = get_team_man_sprite(player[local_player].team, n);
      draw_rle_sprite(get_screen_bmp(), sprite, man_button_x+2+n*24, man_button_y+8-sprite->h+text_height(font));
      textprintf(get_screen_bmp(), font, man_button_x+1+n*24+sprite->w, man_button_y+8, black,
                  "%d", get_team_men_count(player[local_player].team, n));
   }
   
   /* Trade goods */
   for (n=1; n<4; n++) {
      draw_sprite(get_screen_bmp(), get_empty_slot_bitmap(), man_button_x+n*24-12, man_button_y+24);
      bmp = get_goods_bitmap(n);
      draw_sprite(get_screen_bmp(), bmp, man_button_x+2+n*24-12, man_button_y+8+24-bmp->h+text_height(font));
      textprintf(get_screen_bmp(), font, man_button_x+1+n*24-12+bmp->w-4, man_button_y+24+8+4, white,
                  "%d", get_goods_count(player[local_player].team, n-1));
   }
   
   /* Other players */
   y = man_button_y + 32 + 32;
   for (n=0; n<MAX_PLAYERS; n++) {
      if (n!=local_player && player[n].active) {
         textprintf(get_screen_bmp(), get_menu_font(), panel_x + 4, y, 
                     get_team_colour(player[n].team), "%s", player[n].name);
         y+=text_height(get_menu_font());
         textprintf_right(get_screen_bmp(), get_menu_font(), SCREEN_W-4+1, y+1, 
                           black, "%d", player[n].score);
         textprintf_right(get_screen_bmp(), get_menu_font(), SCREEN_W-4, y, 
                           white, "%d", player[n].score);
         y+=text_height(get_menu_font());
      }
   }

   /* Colour indicator for current player and player name */
   rectfill(get_screen_bmp(), panel_x+1, turn_button_y-6, SCREEN_W-2, chat_y,
            get_team_colour(player[current_player].team));
   
   textprintf(get_screen_bmp(), get_menu_bold_font(), 5, 5, 
              black, "%s", player[current_player].name);
   textprintf(get_screen_bmp(), get_menu_bold_font(), 4, 4, 
              get_team_colour(player[current_player].team), "%s", player[current_player].name);

   /* Interface rotation buttons */
   draw_sprite(get_screen_bmp(), get_rol_button_bitmap(), rol_button_x, rol_button_y);
   draw_sprite(get_screen_bmp(), get_ror_button_bitmap(), ror_button_x, rol_button_y);

   /* Current tile(s) */
   for (y=0; y<3; y++) {
      rectfill(get_screen_bmp(),
               panel_x + (SCREEN_W-panel_x-TILE_SIZE)/2-1, chat_y - TILE_SIZE - 32-24 - y*(TILE_SIZE+2), 
               panel_x + (SCREEN_W-panel_x-TILE_SIZE)/2+TILE_SIZE+1, chat_y - TILE_SIZE - 32-24 - y*(TILE_SIZE+2)+TILE_SIZE+1,
               darkgrey);
   }

   if (current_tile[local_player] != NOTILE) {
      blit(get_tile_bitmap(current_tile[local_player]), get_screen_bmp(), 0,0,
                           panel_x + (SCREEN_W-panel_x-TILE_SIZE)/2,
                           chat_y - TILE_SIZE - 32-24, TILE_SIZE,TILE_SIZE);
   }
   
   /* Tiles remaining */
   textprintf(get_screen_bmp(), get_menu_font(), panel_x + 4, man_button_y + 32 + 16, 
              white, "%d tiles remaining", tiles_remaining());

   /* End-of-turn button */
   if (current_tile[current_player] == NOTILE && current_player==local_player) {
      /* Enabled */
      fg = white;
      bg = grey;
   } else {
      fg = grey;
      bg = darkgrey;
   }
   rectfill(get_screen_bmp(), turn_button_x, turn_button_y, turn_button_x+turn_button_w-1, turn_button_y+turn_button_h-1, bg);
   rect(get_screen_bmp(), turn_button_x, turn_button_y, turn_button_x+turn_button_w-1, turn_button_y+turn_button_h-1, black);
   hline(get_screen_bmp(), turn_button_x+1, turn_button_y+1, turn_button_x+turn_button_w-1, fg);
   vline(get_screen_bmp(), turn_button_x+1, turn_button_y+1, turn_button_y+turn_button_h-1, fg);
   textprintf_centre(get_screen_bmp(), get_menu_bold_font(),
                     turn_button_x + turn_button_w/2+1,
                     turn_button_y+2,
                     darkgrey, "End Turn");
   textprintf_centre(get_screen_bmp(), get_menu_bold_font(),
                     turn_button_x + turn_button_w/2,
                     turn_button_y+1,
                     fg, "End Turn");


   /* Message window */

   /* Recent messages */
   rectfill(get_screen_bmp(), 0, chat_y, mw_width, SCREEN_H-text_height(font)-4, darkgrey);
   rect(get_screen_bmp(), 0, chat_y, mw_width-1, SCREEN_H-1, grey);
   for(n=0; n<MAX_MSGS; n++) {
      textprintf(get_screen_bmp(), gamemsg_font, 2+1, chat_y+1 + n*text_height(gamemsg_font), black, "%s", game_msg[(msg_offset+n)%MAX_MSGS].text);
      textprintf(get_screen_bmp(), gamemsg_font, 2, chat_y + n*text_height(gamemsg_font), game_msg[(msg_offset+n)%MAX_MSGS].colour, "%s", game_msg[(msg_offset+n)%MAX_MSGS].text);
   }
   mark_rect(0,chat_y, mw_width+1, SCREEN_H-text_height(font)-3);

   /* Input message */
   text_mode(-1);
   rectfill(get_screen_bmp(), 0,SCREEN_H-text_height(font)-4, SCREEN_W-1,SCREEN_H-1, blue);
   textprintf(get_screen_bmp(), font, 2, SCREEN_H-text_height(font)-2, yellow, "Msg:%s_", user_msg);
   rect(get_screen_bmp(), 0,SCREEN_H-text_height(font)-4, SCREEN_W-1,SCREEN_H-1, grey);
   mark_rect(0,SCREEN_H-text_height(font)-4, SCREEN_W,SCREEN_H);

   screen_update_done();
}

inline void game_gfx_skip(void)
{
}

/**************/
/* Game logic */
/**************/

inline void game_state_update(void)
{
   int n;

   if (is_netgame && (global_cycles_passed&3) == 0) {
      if (is_server)
         server_update();
      else
         client_update();
   }

   /* Game logic updates, things that affect the local client and don't need */
   /* to be sent to the server */

   if (try_place) {
      if (!place_msg_sent && tile_fits(current_tile[current_player], map_cursor_x, map_cursor_y)) {
         /* Place tile */
         place_msg_sent = TRUE;
         snprintf(comm_buffer_out, PACKET_SIZE, "%s%d,%d,%d", PACKAGE_PTILE, current_tile[current_player],  map_cursor_x, map_cursor_y);
         broadcast_message(comm_buffer_out);
         start_sample(1, 0);
      } else if (put_man && !put_man_send) {
         if(in_rect(map_mouse_x,map_mouse_y, last_tile_x*3, last_tile_y*3, 2, 2)
                  &&
                  can_place_flag(last_tile_x, last_tile_y, map_mouse_x%3, map_mouse_y%3, player[current_player].team, man_type)
                  && get_team_men_count(player[current_player].team, man_type)>0) {
            start_sample(2, 0);
            /* Place man */
            snprintf(comm_buffer_out, PACKET_SIZE, "%s%d,%d,%d,%d,%d,%d", PACKAGE_PMAN, last_tile_x, last_tile_y, map_mouse_x%3, map_mouse_y%3, player[current_player].team, man_type);
            broadcast_message(comm_buffer_out);
            /* End turn */
            snprintf(comm_buffer_out, PACKET_SIZE, "%s", PACKAGE_ETURN);
            broadcast_message(comm_buffer_out);
            put_man_send = TRUE;
         } else {
            start_sample(3, 0);
         }
      } else {
         start_sample(0, 0);
      }
      try_place = FALSE;
   }

   if (!put_man && current_tile[current_player] == NOTILE && !place_msg_sent) {
      do {
         current_tile[current_player] = pop_tile();
         if (current_tile[current_player] == NOTILE)
            break;
      } while (!tile_fits_anywhere_anyway(current_tile[current_player]));

      if (current_tile[current_player] != NOTILE) {
         redraw_tilemap = TRUE;
         set_screen_changed();
      } else if (!game_done) {
         /* Last tile - time to wrap up */
         end_score();
         set_screen_changed();
         game_done = TRUE;
         display_score = TRUE;
      }
   }

   /* Update scores */
   for (n=0; n<MAX_PLAYERS; n++) {
      if (player[n].active && player[n].score != get_team_score(player[n].team)) {
         player[n].score = get_team_score(player[n].team);
         set_screen_changed();
         if (n==local_player || !is_netgame) {
            start_sample(4, 0);
         }
      }
   }
   
   /* End turn automatically if that is enabled */
   if (current_tile[current_player]==NOTILE && current_player==local_player && 
         auto_end_turn && !count_number_flags(last_tile_x, last_tile_y) && 
         get_team_men_count(player[local_player].team, MASTERBUILDERS)==0 && 
         get_team_men_count(player[local_player].team, PIGS)==0) {
      /* End of turn */

      snprintf(comm_buffer_out, PACKET_SIZE, "%s", PACKAGE_ETURN);
      broadcast_message(comm_buffer_out);
   }

   if (close_button_pressed) {
      set_game_done();
   }
}

/**************/
/* Game input */
/**************/

inline void game_input(void)
{
   int c;
   int mx, my, mb;

   /* Mouse input */
   mx = mouse_x;
   my = mouse_y;
   mb = mouse_b;

   if (!(mb&2) && old_mouse_b&2) {
      if (current_tile[local_player]!=NOTILE) {
         snprintf(comm_buffer_out, PACKET_SIZE, "%s%d", PACKAGE_ROTR, current_tile[local_player]);
         broadcast_message(comm_buffer_out);
      }
   }
   old_mouse_b = mb;

   if (in_rect(mx,my, 0,0, panel_x,chat_y)) {
      old_mouse_x = map_mouse_x;
      old_mouse_y = map_mouse_y;

      map_mouse_x = (map_x + mx)-map_x%(TILE_SIZE/3);
      map_mouse_y = (map_y + my)-map_y%(TILE_SIZE/3);
      map_mouse_x/=(TILE_SIZE/3);
      map_mouse_y/=(TILE_SIZE/3);
      
      if (map_cursor_mouse) {
         map_cursor_x = map_mouse_x/3;
         map_cursor_y = map_mouse_y/3;
      }

      /* Move map indicator */
      /* Tile indicator snaps to big tiles */
      if (old_mouse_x != map_mouse_x ||
          old_mouse_y != map_mouse_y) {
         set_screen_changed();
      }

      /* men indicator snaps to mini-tiles */
      if (put_man &&
            (map_mouse_x/3==last_tile_x && map_mouse_y/3==last_tile_y) &&
            (old_mouse_x != map_mouse_x || old_mouse_y != map_mouse_y)) {
         set_screen_changed();
      }

      if (mb&1) {
         map_mouse_down_x = map_mouse_x;
         map_mouse_down_y = map_mouse_y;
         map_mouse_down = TRUE;
      } else if (map_mouse_down) {
         if (map_mouse_down_x == map_mouse_x && map_mouse_down_y == map_mouse_y && current_player==local_player) {
            /* Try to place something on the map */
            try_place = TRUE;
         }
         map_mouse_down = FALSE;
      }
   } else if (in_rect(mx,my, rol_button_x,rol_button_y, 24, 24)) {
      if (mb&1) {
         mouse_down_x = mx;
         mouse_down_y = my;
         mouse_down = TRUE;
      } else if (mouse_down) {
         mouse_down = FALSE;
         if (current_tile[local_player]!=NOTILE) {
            /* Rotate tile */
            snprintf(comm_buffer_out, PACKET_SIZE, "%s%d", PACKAGE_ROTL, current_tile[local_player]);
            broadcast_message(comm_buffer_out);
         }
      }
   } else if (in_rect(mx,my, ror_button_x,ror_button_y, 24, 24)) {
      if (mb&1) {
         mouse_down_x = mx;
         mouse_down_y = my;
         mouse_down = TRUE;
      } else if (mouse_down) {
         mouse_down = FALSE;
         if (current_tile[local_player]!=NOTILE) {
            /* Rotate tile */
            snprintf(comm_buffer_out, PACKET_SIZE, "%s%d", PACKAGE_ROTR, current_tile[local_player]);
            broadcast_message(comm_buffer_out);
         }
      }
   } else if (in_rect(mx,my, turn_button_x,turn_button_y, turn_button_w,turn_button_h)) {
      /* End of turn button */
      if (mb&1) {
         mouse_down_x = mx;
         mouse_down_y = my;
         mouse_down = TRUE;
      } else if (mouse_down) {
         mouse_down = FALSE;
         if (current_tile[current_player]==NOTILE && current_player==local_player) {
            /* End of turn is OK from current player */
            play_sample(get_button_sample(), get_sfx_volume(), 127, 1000, FALSE);
            snprintf(comm_buffer_out, PACKET_SIZE, "%s", PACKAGE_ETURN);
            broadcast_message(comm_buffer_out);
         }
      }
   } else if (in_rect(mx,my, man_button_x,man_button_y, MENTYPES*24,24)) {
      if (mb&1) {
         mouse_down_x = mx;
         mouse_down_y = my;
         mouse_down = TRUE;
      } else if (mouse_down) {
         mouse_down = FALSE;
         if ((mx-man_button_x)/24 == (mouse_down_x-man_button_x)/24) {
            play_sample(get_button_sample(), get_sfx_volume(), 127, 1000, FALSE);
            man_type = (mouse_down_x-man_button_x)/24;
            set_screen_changed();
         }
      }
   } else {
      map_mouse_down = FALSE;
      mouse_down = FALSE;
   }

   /* Keyboard input */

   /* Save screen shot */
   if (key[KEY_PRTSCR]||key[KEY_F12])
      screenshot();

   /* Keep <esc> as a general exit key, just to be save */
   if (key[KEY_ESC]) {
      set_game_done();
      player[local_player].active = FALSE;
      game_started = FALSE;
      snprintf(comm_buffer_out, PACKET_SIZE, "%s%d", PACKAGE_LEAVE, local_player+1);
      broadcast_message(comm_buffer_out);
   }

   if (keypressed()) {
      c = readkey();
      switch (c >> 8) {
         case KEY_4_PAD:
            map_cursor_x--;
            redraw_tilemap = TRUE;
            set_screen_changed();
            break;
         case KEY_6_PAD:
            map_cursor_x++;
            redraw_tilemap = TRUE;
            set_screen_changed();
            break;
         case KEY_8_PAD:
            map_cursor_y--;
            redraw_tilemap = TRUE;
            set_screen_changed();
            break;
         case KEY_2_PAD:
            map_cursor_y++;
            redraw_tilemap = TRUE;
            set_screen_changed();
            break;
         case KEY_TAB:
            map_cursor_mouse = !map_cursor_mouse;
            redraw_tilemap = TRUE;
            set_screen_changed();
            break;
         case KEY_LEFT:
            map_x-=SCROLL_SIZE;
            redraw_tilemap = TRUE;
            set_screen_changed();
            break;
         case KEY_RIGHT:
            map_x+=SCROLL_SIZE;
            redraw_tilemap = TRUE;
            set_screen_changed();
            break;
         case KEY_UP:
            map_y-=SCROLL_SIZE;
            redraw_tilemap = TRUE;
            set_screen_changed();
            break;
         case KEY_DOWN:
            map_y+=SCROLL_SIZE;
            redraw_tilemap = TRUE;
            set_screen_changed();
            break;
         case KEY_HOME:
            map_x = (MAP_CX-7)*TILE_SIZE;
            map_y = (MAP_CY-5)*TILE_SIZE;
            redraw_tilemap = TRUE;
            set_screen_changed();
            break;
         case KEY_PGUP:
            if (current_tile[local_player]!=NOTILE) {
               snprintf(comm_buffer_out, PACKET_SIZE, "%s%d", PACKAGE_ROTL, current_tile[local_player]);
               broadcast_message(comm_buffer_out);
            }
            break;
         case KEY_PGDN:
            if (current_tile[local_player]!=NOTILE) {
               snprintf(comm_buffer_out, PACKET_SIZE, "%s%d", PACKAGE_ROTR, current_tile[local_player]);
               broadcast_message(comm_buffer_out);
            }
            break;
         case KEY_ENTER:
            if (strlen(user_msg)) {
               /* Send chat message */
               broadcast_text(user_msg);
               user_msg[0] = '\0';
               set_screen_changed();
            } else if (current_tile[current_player]==NOTILE && current_player==local_player) {
               /* End of turn */

               snprintf(comm_buffer_out, PACKET_SIZE, "%s", PACKAGE_ETURN);
               broadcast_message(comm_buffer_out);
            } else if (current_player==local_player) {
               /* Try to place something on the map */
               try_place = TRUE;
            }
            break;
         case KEY_BACKSPACE:
            if (enter_msg) {
               user_msg[uoffset(user_msg, -1)] = '\0';
               set_screen_changed();
            }
         default:
            if ((c&0xFF)>31 && (c&0xFF)<128) {    /* Append to string */
               uszprintf(user_msg, MSG_LENGTH, "%s%c", user_msg, c&0xFF);
               set_screen_changed();
            }
            break;
      }                         /* end of switch */
   }
}

void global_init(void)
{
   /* Message window settings */
   gamemsg_font = get_menu_font();
   mw_width = SCREEN_W;

   /* Get y coordinate of chat window */
   chat_y = SCREEN_H-text_height(font)-8-(MAX_MSGS)*text_height(gamemsg_font);

   /* get x coordinate of control/status panel */
   panel_x = SCREEN_W-98;

   /* Clear local name */
   localname[0] = '\0';
   
   /* Read .cts files */
   read_cts_file_info();
}

void start_game(void)
{
   global_init();
   while(game_menu()) {
      if (newgame_init()) {

         while (newgame_menu())
            playgame();
      
         game_shutdown();
      }
   }
}
