#include <allegro.h>
#include <string.h>
#include "tileset.h"
#include "genrand.h"
#include "assert.h"
#include "global.h"
#include "rules.h"
#include "str.h"
#include "gfx.h"

/* Checkerboard flags (for mini-tilebitmap mappings) */
#define CHECKERBOARD_ODD   1
#define CHECKERBOARD_EVEN  2
#define CHECKERBOARD_ANY   3

/* Rule modifiers */
int rule_flags = 0;
int city_shield_bonus = 1;
int base_farm_multi = 3;
int bonus_farm_multi = 4;
int game_cathedral_multi = 3;
int final_cathedral_multi = 0;
int game_inn_multi = 2;
int final_inn_multi = 0;
int tradegood_bonus = 10;

/* Centre (x, y) values of mini-tiles in the order u, l, r, d*/
static const int xypairs[4][2] = { {1, 0}, {0, 1}, {2, 1}, {1, 2}};

/* Corners */
static const int xycorners[4][2] = { {0, 0}, {2, 0}, {2, 2}, {0, 2} };

/* Bits connecting cities on the border middle tiles to the outside */
static const int mid_excity_flags[4] = { 0xc0, 0x60, 0x90, 0x30 };

/* Default team colours */
static const RGB team_colours[] = {
   { 192,   0,   0 },   // Red
   { 150, 224, 255 },   // Blue
   { 250, 140,  40 },   // Orange
   { 128,   0, 128 },   // Purple
   {   0, 255,   0 },   // Green
   { 192, 192, 192 },   // Grey
   { 255, 180, 255 },   // Pink
   { 255, 255, 255 }    // White
};

/* Structure for holding map locations */
typedef struct {
   int x, y;
} TILE_LOCATION;

/* Structure holding a mini-tile */
typedef struct MINITILE {
   int connections;
   int conn_dirs[4]; /* Up, left, right, down */
   BITMAP *bmp;
   int transform;
   /* Structure ID - this will identify completed structures in the game */
   int structnum;
   /* Bonus associated with this mini-tile (for roads) */
   int flags;
   /* Man standing on this mini-tile */
   int man_team;
   int man_type;
} MINITILE;

/* Structure holding one large tile */
typedef struct TILE {
   MINITILE minitile[3][3];
   BITMAP *gfx;
   /* Map location this tile is located, or undefined if it is not on the board */
   int x, y;
   int flags;
   int angle;
} TILE;

/* minitile bitmaps - there can be several variants for each bitmap */
typedef struct MINIT_BMP {
   int ngfx;
   int conn_flags;
   int checkerboard_pattern;     /* Checkerboard pattern (odd/even) */
   BITMAP **bmp;
} MINIT_BMP;

typedef struct TEAM {
   int men[MENTYPES];
   int colour;
   int score[FEATURE_TYPES];
   int score_goods[GOODS_TYPES];
   int goods[GOODS_TYPES];
} TEAM;

typedef struct MAP_MEN {
   int men[MENTYPES];
} MAP_MEN;

typedef struct FEATURE_BONUS {
   int goods[3];
   int shields;
   int cathedrals;
   int inns;
} FEATURE_BONUS;

/* Transformations needed to transform a bitpattern to the corresponding */
/*  lower left corner pattern */
/* Bit 1 - Rotate 90 degrees left */
/* Bit 2 - Rotate 180 degrees left */
/* Bit 3 - flip vertical (top <==> down) */
/* Bit 4 - flip horizontal (left <==> right) */
static const int flip_to_ll[3][3] = {
   { 4, 2, 2 },
   { 3, 0, 1 },
   { 0, 0, 8 }
};

/* Rotation matrices */
static const int rot90[2][2] = {    /* 90 degrees anti-clockwise */
   { 0, 1 },
   {-1, 0 }
};

/* Rotation matrices */
static const int rot180[2][2] = {   /* 180 degrees */
   {-1, 0 },
   { 0,-1 }
};

/* Rotation matrices */
static const int rot270[2][2] = {   /* 270 degrees anti-clockwise */
   { 0,-1 },
   { 1, 0 }
};

/* Rotation angles */
static const fixed quarter = 64<<16;
static const fixed half = 128<<16;
static const fixed threequarter = 192<<16;

static const int city_mask = 0xf0f0f0f0;
static const int road_mask = 0x0f0f0f0f;
static const int river_mask = 0x00000f00;
static const int city_ur_mask = 0x00000080;
static const int city_ul_mask = 0x00000040;
static const int city_ll_mask = 0x00000020;
static const int city_lr_mask = 0x00000010;

static DATAFILE *theme_dat = NULL;
static DATAFILE *flags_dat = NULL;
static DATAFILE *goods_dat = NULL;
static DATAFILE *attrib_dat = NULL;
static DATAFILE *shadows_dat = NULL;
static DATAFILE *men_dat = NULL;
static DATAFILE *mapui_dat = NULL;
static DATAFILE *tilegfx_dat = NULL;
static DATAFILE *odd_tilegfx_dat = NULL;
static DATAFILE *even_tilegfx_dat = NULL;
static DATAFILE *common_tilegfx_dat = NULL;
static DATAFILE *bonus_dat = NULL;
static int num_mini_tiles = 0;
static MINITILE *mini_tile = NULL;

static MINIT_BMP *minitgfx = NULL;
static int minitgfx_count = 0;

static int feature_id_count;
static int *feature_open = NULL;
static int *feature_type = NULL;
static FEATURE_BONUS *feature_bonus = NULL;

static int num_teams;
static TEAM *team;
static MAP_MEN **feature_men;

static int num_tiles = 0;
static TILE *tile = NULL;
static BITMAP *tilerenderbmp = NULL;

static BITMAP *emptytile = NULL;
static BITMAP *grid = NULL;

static TILE_LOCATION *open_tiles = NULL;
static int *playboard = NULL;
static int num_open_tiles = 0;
static int tiles_in_play = 0;
static int tiles_stackp = 0;
static int board_width = 0;
static int board_height = 0;

/* Count total number of bitmaps in a datafile (including nested dfs) */
static int count_all_bitmaps(const DATAFILE *df)
{
   int n = 0;
   const DATAFILE *dc;

   if (!df)
      return 0;

   for (dc = df; dc->type!=DAT_END; dc++) {
      if (dc->type==DAT_BITMAP)
         n++;
      if (dc->type==DAT_FILE)
         n+=count_all_bitmaps(dc->dat);
   }

   return n;
}

/* Count number of bitmaps in a datafile (don't nest) */
static int count_bitmaps(const DATAFILE *df)
{
   int n = 0;
   const DATAFILE *dc;

   if (!df)
      return 0;

   for (dc = df; dc->type!=DAT_END; dc++) {
      if (dc->type==DAT_BITMAP)
         n++;
   }

   return n;
}

/* Count number of sub datafiles */
static int count_files(const DATAFILE *df)
{
   int n = 0;
   const DATAFILE *dc;

   if (!df)
      return 0;

   for (dc = df; dc->type!=DAT_END; dc++) {
      if (dc->type==DAT_FILE)
         n++;
   }

   return n;
}

static int convert_bitmap_list(const DATAFILE *df, MINIT_BMP *mtb, const int flag)
{
   const DATAFILE *dc;
   int n;

   mtb->ngfx = count_bitmaps(df);
   mtb->bmp = malloc(mtb->ngfx * sizeof *mtb->bmp);
   mtb->conn_flags = flag;

   for (n=0; n<mtb->ngfx; n++) {
      mtb->bmp[n] = NULL;
   }

   n = 0;
   for (dc = df; dc->type!=DAT_END; dc++) {
      if (dc->type==DAT_BITMAP) {
         char s[128];
         snprintf(s, 128, "%s",get_datafile_property(dc, DAT_ID('C', 'K', 'B', 'D')));
         sscanf(s, "%d", &(mtb->checkerboard_pattern));

         mtb->bmp[n] = dc->dat;
         n++;
      }
   }

   return n;
}

static BITMAP* find_minitbitmap(const int flag, const int checkerboard)
{
   int n, c;

   for (n=0; n<minitgfx_count; n++) {
      //printf("%x, %x\n", minitgfx[n].conn_flags, flag);
      if (minitgfx[n].conn_flags == flag && (minitgfx[n].checkerboard_pattern&checkerboard)) {
         c = genrandui_range(minitgfx[n].ngfx);
         return minitgfx[n].bmp[c];
      }
   }
   return NULL;
}

/* Flip a bit pattern               */
/* how&4 = Flip vertical            */
/* how&8 = Flip horizontal          */
static int flip_pattern(const int pattern, const int how)
{
   int city_pattern = pattern & city_mask;
   int road_pattern = pattern & road_mask;
   int flipped_pattern = 0;

   /* Flip vertical (top <==> down) */
   if (how&4) {
      /* Swap bits 1 and 4 and 2 and 3 of city-like patterns */
      flipped_pattern |= ((city_pattern >> 3) & 0x10101010) | ((city_pattern << 3) & 0x80808080);
      flipped_pattern |= ((city_pattern >> 1) & 0x20202020) | ((city_pattern << 1) & 0x40404040);
      /* Swap bits 1 and 3 of road-like pattern */
      flipped_pattern |= ((road_pattern >> 2) & 0x01010101) | ((road_pattern << 2) & 0x04040404);
      flipped_pattern |= road_pattern & 0x0a0a0a0a;
   }

   /* Flip horizontal (left <==> right) */
   if (how&8) {
      /* Swap bits 1 and 2 and 3 and 4 of city-like pattern */
      flipped_pattern |= ((city_pattern >> 1) & 0x50505050) | ((city_pattern << 1) & 0xa0a0a0a0);
      /* Swap bits 2 and 4 of road-like pattern */
      flipped_pattern |= ((road_pattern >> 2) & 0x02020202) | ((road_pattern << 2) & 0x08080808);
      flipped_pattern |= road_pattern & 0x05050505;
   }

   return flipped_pattern;
}

/* Rotate a bit pattern             */
/* how>0 = left, clockwise          */
/* how<0 = right, anti-clockwise    */
/* Measured in quarters of degrees  */
static int rotate_pattern(const int pattern, const int how)
{
   int shift_left, shift_right;
   int rotated_pattern = 0;
   assert(how<=4);
   assert(how>=-4);

   /* How many bits to shift left */
   shift_left = how;
   if (shift_left<0)
      shift_left+=4;
   /* How many bits to shift to the right */
   shift_right = 4-shift_left;

   /* Rotate cities */
   rotated_pattern |= ((pattern & city_mask)>>shift_right | (pattern & city_mask)<<shift_left) & city_mask;

   /* Rotate roads */
   rotated_pattern |= ((pattern & road_mask)>>shift_right | (pattern & road_mask)<<shift_left) & road_mask;

   return rotated_pattern;
}

static int mt_flags_to_ll(const int pattern, const int how)
{
   int transformed_pattern = pattern;

   if (how&3)
      transformed_pattern = rotate_pattern(transformed_pattern, how);

   if (how&12)
      transformed_pattern = flip_pattern(transformed_pattern, how);

   return transformed_pattern;
}

/* Map minitile graphics onto a large tile */
void map_minitile_graphics(int id)
{
   BITMAP *bmp;
   int x, y;
   int transform;
   int checkerboard;

   /* Draw bitmap */
   for(y=0; y<3; y++) {
      for (x=0; x<3; x++) {
         if (x==1 && y==1) {
            for (transform=0; transform<4; transform++) {
               bmp = find_minitbitmap(mt_flags_to_ll(tile[id].minitile[y][x].connections, transform), CHECKERBOARD_ODD);
               if (bmp)
                  break;
            }
         } else {
            transform = flip_to_ll[y][x];
            checkerboard = (y^x)&1 ? CHECKERBOARD_EVEN : CHECKERBOARD_ODD;
            bmp = find_minitbitmap(mt_flags_to_ll(tile[id].minitile[y][x].connections, transform), checkerboard);
            if (!bmp) {
               for (transform=0; transform<4; transform++) {
                  bmp = find_minitbitmap(mt_flags_to_ll(tile[id].minitile[y][x].connections, transform), checkerboard);
                  if (bmp)
                     break;
               }
            }
         }
         tile[id].minitile[y][x].bmp = bmp;
         tile[id].minitile[y][x].transform = transform;
      }
   }
}

/* Fill a tile with the appropriate FARM id numbers */
static void floodfill_tile_farm(TILE *tile, int x, int y, int id)
{
   int conn;
   if (x<0 || y<0 || x>2 || y>2)
      return;

   conn = tile->minitile[y][x].connections;

   /* Ugly hack to detect odd-shaped tiles */   
   if ((x==2 && y==2 && conn == city_ul_mask) || (x==0 && y==0 && conn == city_lr_mask) ||
      (x==0 && y==2 && conn == city_ur_mask) || (x==2 && y==0 && conn == city_ll_mask))
      return;
      
   if (conn == 0xf0)
      return;
   
   if (conn & road_mask)
      return;
   
   if (tile->minitile[y][x].structnum == 0 && ((conn&city_mask) != 0xf0)) {
      if ((y!=1 || x!=1) || !tile->minitile[y][x].connections) {
         tile->minitile[y][x].structnum = id;

         if ((conn & 0x05050505) == 0) {
            /* Connect left */
            if ((tile->minitile[y][x].conn_dirs[1] & 0x60) != 0x60)
               floodfill_tile_farm(tile, x-1, y, id);
            /* Connect right */
            if ((tile->minitile[y][x].conn_dirs[2] & 0x90) != 0x90)
               floodfill_tile_farm(tile, x+1, y, id);
         }

         if ((conn & 0x0a0a0a0a) == 0) {
            /* Connect top */
            if ((tile->minitile[y][x].conn_dirs[0] & 0xc0) != 0xc0)
               floodfill_tile_farm(tile, x, y-1, id);
            /* Connect bottom */
            if ((tile->minitile[y][x].conn_dirs[3] & 0x30) != 0x30)
               floodfill_tile_farm(tile, x, y+1, id);
         }
      }
   }
}

/* Fill a tile with the appropriate road id numbers */
static void floodfill_tile_road(TILE *tile, int x, int y, int id)
{
   if (x<0 || y<0 || x>2 || y>2)
      return;
      
   if (x==1 && y==1 && (tile->flags & TILEF_MONASTERY)) {
      return;
   }

   if (tile->minitile[y][x].structnum == 0 && tile->minitile[y][x].connections&0x0f) {
      if ((tile->minitile[y][x].connections&road_mask) == 0x0a) {
         /* Horizontal connection */
         tile->minitile[y][x].structnum = id;
         floodfill_tile_road(tile, x-1, y, id);
         floodfill_tile_road(tile, x+1, y, id);
      } else if ((tile->minitile[y][x].connections&road_mask) == 0x05) {
         /* Vertical connection */
         tile->minitile[y][x].structnum = id;
         floodfill_tile_road(tile, x, y-1, id);
         floodfill_tile_road(tile, x, y+1, id);
      } else if ((tile->minitile[y][x].connections&road_mask) == 0x03 ||
          (tile->minitile[y][x].connections&road_mask) == 0x06 ||
          (tile->minitile[y][x].connections&road_mask) == 0x09 ||
          (tile->minitile[y][x].connections&road_mask) == 0x0c) {
         /* Bend */
         tile->minitile[y][x].structnum = id;
         floodfill_tile_road(tile, x, y-1, id);
         floodfill_tile_road(tile, x, y+1, id);
      }
      
      if ( (((x==0 || x==2) && (y==1)) || ((y==0 || y==2) && (x==1))) && !tile->minitile[y][x].structnum)
         tile->minitile[y][x].structnum = id;
   }
}

/* Fill a tile with the appropriate city id numbers */
static void floodfill_tile_city(TILE *tile, int x, int y, int id)
{
   int conn;
   if (x<0 || y<0 || x>2 || y>2)
      return;

   conn = tile->minitile[y][x].connections;

   if (tile->minitile[y][x].structnum == 0 && conn&0xf0) {
      if ( (x+y)&1 || (conn&0xf0) == 0xf0) {
         tile->minitile[y][x].structnum = id;

         if ((tile->minitile[y][x].conn_dirs[0] & 0xc0) == 0xc0)
            floodfill_tile_city(tile, x, y-1, id);
         if ((tile->minitile[y][x].conn_dirs[1] & 0x60) == 0x60)
            floodfill_tile_city(tile, x-1, y, id);
         if ((tile->minitile[y][x].conn_dirs[2] & 0x90) == 0x90)
            floodfill_tile_city(tile, x+1, y, id);
         if ((tile->minitile[y][x].conn_dirs[3] & 0x30) == 0x30)
            floodfill_tile_city(tile, x, y+1, id);
      }
   }
}

/* Construct a large tile from a (subset) of smaller ones */
void construct_tile(int id, int u, int l, int r, int d, int bonus)
{
   int x, y, n, conn;
   /* Create and draw a large tile */

   tile[id].flags = bonus;
   tile[id].angle = 0;

   /* Clear tile list */
   for(y=0; y<3; y++) {
      for (x=0; x<3; x++) {
         tile[id].minitile[y][x].connections = 0;
         tile[id].minitile[y][x].conn_dirs[0] = 0;
         tile[id].minitile[y][x].conn_dirs[1] = 0;
         tile[id].minitile[y][x].conn_dirs[2] = 0;
         tile[id].minitile[y][x].conn_dirs[3] = 0;
         tile[id].minitile[y][x].structnum = 0;
         tile[id].minitile[y][x].flags = 0;
         tile[id].minitile[y][x].man_team = -1;
         tile[id].minitile[y][x].man_type = 0;
      }
   }

   /* Preset mini-tiles */
   tile[id].minitile[0][1].connections = u;
   tile[id].minitile[1][0].connections = l;
   tile[id].minitile[1][2].connections = r;
   tile[id].minitile[2][1].connections = d;
   
   /* set proper partial directions */
   for (n=0; n<4; n++) {
      tile[id].minitile[0][1].conn_dirs[n] = tile[id].minitile[0][1].connections & mid_excity_flags[n];
      tile[id].minitile[1][0].conn_dirs[n] = tile[id].minitile[1][0].connections & mid_excity_flags[n];
      tile[id].minitile[1][2].conn_dirs[n] = tile[id].minitile[1][2].connections & mid_excity_flags[n];
      tile[id].minitile[2][1].conn_dirs[n] = tile[id].minitile[2][1].connections & mid_excity_flags[n];
   }

   /* Calculate other connections */

   /* Centre */
   /* Upper bit 1 (city) becomes bit 4 (city) of centre */
   /* Upper bit 2 (city) becomes bit 3 (city) of centre */
   /* Lower bit 3 (city) becomes bit 2 (city) of centre */
   /* Lower bit 4 (city) becomes bit 1 (city) of centre */
   /* Upper bit 1 (road) becomes bit 3 (road) of centre */
   /* Lower bit 3 (road) becomes bit 1 (road) of centre */
   tile[id].minitile[1][1].conn_dirs[0] |= ((u & 0x10101010)<<3)&city_mask;
   tile[id].minitile[1][1].conn_dirs[0] |= ((u & 0x20202020)<<1)&city_mask;
   tile[id].minitile[1][1].conn_dirs[3] |= ((d & 0x40404040)>>1)&city_mask;
   tile[id].minitile[1][1].conn_dirs[3] |= ((d & 0x80808080)>>3)&city_mask;
   tile[id].minitile[1][1].conn_dirs[0] |= ((u & 0x01010101)<<2)&road_mask;
   tile[id].minitile[1][1].conn_dirs[3] |= ((d & 0x04040404)>>2)&road_mask;
   /* Left bit 1 (city) becomes bit 2 (city) of centre */
   /* Left bit 4 (city) becomes bit 3 (city) of centre */
   /* Right bit 3 (city) becomes bit 4 (city) of centre */
   /* Right bit 2 (city) becomes bit 1 (city) of centre */
   /* Left bit 4 (road) becomes bit 2 (road) of centre */
   /* Right bit 2 (road) becomes bit 4 (road) of centre */
   tile[id].minitile[1][1].conn_dirs[1] |= ((l & 0x10101010)<<1)&city_mask;
   tile[id].minitile[1][1].conn_dirs[1] |= ((l & 0x80808080)>>1)&city_mask;
   tile[id].minitile[1][1].conn_dirs[2] |= ((r & 0x40404040)<<1)&city_mask;
   tile[id].minitile[1][1].conn_dirs[2] |= ((r & 0x20202020)>>1)&city_mask;
   tile[id].minitile[1][1].conn_dirs[1] |= ((l & 0x08080808)>>2)&road_mask;
   tile[id].minitile[1][1].conn_dirs[2] |= ((r & 0x02020202)<<2)&road_mask;

   tile[id].minitile[1][1].connections = 0;
   for(n=0; n<4; n++)
      tile[id].minitile[1][1].connections |= tile[id].minitile[1][1].conn_dirs[n];

   /* Cities through the centre should always have at least two adjacent corners */
   conn = tile[id].minitile[1][1].connections;
   if (is_power_of_two(conn&city_mask) ||
       is_power_of_two( ((conn&city_mask)^((conn&city_mask)<<2)) &city_mask)) {
      tile[id].minitile[1][1].connections&=~city_mask;
   }
   
   /* Corners, no need to consider roads! */
   for (x=0; x<3; x+=2) {
      /* Get upper */
      tile[id].minitile[2][x].conn_dirs[0] |= ((tile[id].minitile[1][x].connections & 0x10101010)<<3)&city_mask;
      tile[id].minitile[2][x].conn_dirs[0] |= ((tile[id].minitile[1][x].connections & 0x20202020)<<1)&city_mask;
      /* Get lower */
      tile[id].minitile[0][x].conn_dirs[3] |= ((tile[id].minitile[1][x].connections & 0x40404040)>>1)&city_mask;
      tile[id].minitile[0][x].conn_dirs[3] |= ((tile[id].minitile[1][x].connections & 0x80808080)>>3)&city_mask;

   }
   for(y=0; y<3; y+=2) {
      /* Get left */
      tile[id].minitile[y][2].conn_dirs[1] |= ((tile[id].minitile[y][1].connections & 0x10101010)<<1)&city_mask;
      tile[id].minitile[y][2].conn_dirs[1] |= ((tile[id].minitile[y][1].connections & 0x80808080)>>1)&city_mask;
      /* Get right */
      tile[id].minitile[y][0].conn_dirs[2] |= ((tile[id].minitile[y][1].connections & 0x40404040)<<1)&city_mask;
      tile[id].minitile[y][0].conn_dirs[2] |= ((tile[id].minitile[y][1].connections & 0x20202020)>>1)&city_mask;
   }

   for(x=0; x<3; x+=2) {
      for(n=0; n<4; n++) {
         tile[id].minitile[2][x].connections |= tile[id].minitile[2][x].conn_dirs[n];
         tile[id].minitile[0][x].connections |= tile[id].minitile[0][x].conn_dirs[n];
         tile[id].minitile[x][2].connections |= tile[id].minitile[x][2].conn_dirs[n];
         tile[id].minitile[x][0].connections |= tile[id].minitile[x][0].conn_dirs[n];
      }
   }

   /* Fill up border tiles */
   if ((tile[id].minitile[0][0].connections & 0x00000090) == 0x00000090)
      tile[id].minitile[0][0].connections |=  0x00000040;
   if ((tile[id].minitile[0][0].connections & 0x00000030) == 0x00000030)
      tile[id].minitile[0][0].connections |=  0x00000040;
   if ((tile[id].minitile[0][2].connections & 0x00000060) == 0x00000060)
      tile[id].minitile[0][2].connections |=  0x00000080;
   if ((tile[id].minitile[0][2].connections & 0x00000030) == 0x00000030)
      tile[id].minitile[0][2].connections |=  0x00000080;

   if ((tile[id].minitile[2][0].connections & 0x00000090) == 0x00000090)
      tile[id].minitile[2][0].connections |=  0x00000020;
   if ((tile[id].minitile[2][0].connections & 0x000000c0) == 0x000000c0)
      tile[id].minitile[2][0].connections |=  0x00000020;
   if ((tile[id].minitile[2][2].connections & 0x00000060) == 0x00000060)
      tile[id].minitile[2][2].connections |=  0x00000010;
   if ((tile[id].minitile[2][2].connections & 0x000000c0) == 0x000000c0)
      tile[id].minitile[2][2].connections |=  0x00000010;

   /* Weird tiles: centre tiles with 1 city bit set should not count it at all */
   for (n=0; n<4; n++) {
      if (is_power_of_two(tile[id].minitile[xypairs[n][1]][xypairs[n][0]].connections&city_mask)) {
         tile[id].minitile[xypairs[n][1]][xypairs[n][0]].connections&=~city_mask;
      }
   }
   
   /* Tiles with an inner `garden' */
   for (n=0; n<4; n+=2) {
      if ((tile[id].minitile[xypairs[n][1]][xypairs[n][0]].connections&city_mask)==0 && 
          (tile[id].minitile[1][1].connections&city_mask)==mid_excity_flags[n]) {
         tile[id].minitile[1][1].connections |= 0x000000f0;
         tile[id].minitile[1][1].conn_dirs[2] |= 0x00000090;
         tile[id].minitile[1][1].conn_dirs[1] |= 0x00000060;
         tile[id].minitile[1][0].connections |= 0x000000f0;
         tile[id].minitile[1][2].connections |= 0x000000f0;
         tile[id].minitile[1][0].conn_dirs[2] |= 0x00000090;
         tile[id].minitile[1][2].conn_dirs[1] |= 0x00000060;
      }
   }

   /* Calculate structure IDs */

   /* Fill farms */
   floodfill_tile_farm(tile+id, 0,1, feature_id_count++);
   floodfill_tile_farm(tile+id, 2,1, feature_id_count++);
   floodfill_tile_farm(tile+id, 1,0, feature_id_count++);
   floodfill_tile_farm(tile+id, 1,2, feature_id_count++);

   /* Fill cities */
   for (n=0; n<4; n++) {
      x = xypairs[n][0];
      y = xypairs[n][1];
      
      if ((tile[id].minitile[y][x].connections&mid_excity_flags[n]) == mid_excity_flags[n])
         tile[id].minitile[y][x].structnum = 0;
   }

   /* City Bridge */
   if (tile[id].flags & TILEF_CBRIDGE) {
      tile[id].minitile[2][1].structnum = tile[id].minitile[0][1].structnum = feature_id_count++;
      tile[id].minitile[1][0].structnum = tile[id].minitile[1][2].structnum = feature_id_count++;
   } else {
      floodfill_tile_city(tile+id, 0,1, feature_id_count++);
      floodfill_tile_city(tile+id, 2,1, feature_id_count++);
      floodfill_tile_city(tile+id, 1,0, feature_id_count++);
      floodfill_tile_city(tile+id, 1,2, feature_id_count++);
   }

   /* Connect roads (edges) */
   if ((tile[id].minitile[0][1].connections&0x0f) == 0x04)
      tile[id].minitile[0][1].structnum = 0;
   if ((tile[id].minitile[2][1].connections&0x0f) == 0x01)
      tile[id].minitile[2][1].structnum = 0;
   if ((tile[id].minitile[1][0].connections&0x0f) == 0x02)
      tile[id].minitile[1][0].structnum = 0;
   if ((tile[id].minitile[1][2].connections&0x0f) == 0x08)
      tile[id].minitile[1][2].structnum = 0;

   floodfill_tile_road(tile+id, 0,1, feature_id_count++);
   floodfill_tile_road(tile+id, 2,1, feature_id_count++);
   floodfill_tile_road(tile+id, 1,0, feature_id_count++);
   floodfill_tile_road(tile+id, 1,2, feature_id_count++);

   /* Fix meadow numbers on corner tiles */
   /* (we actually need a rewrite of this...) */
   if (!(tile[id].flags & TILEF_CBRIDGE)) {
      if (!tile[id].minitile[0][0].structnum && !tile[id].minitile[0][0].conn_dirs[0] && tile[id].minitile[0][0].connections != 0x10)
         tile[id].minitile[0][0].structnum = feature_id_count++;
      if (!tile[id].minitile[2][0].structnum && !tile[id].minitile[2][0].conn_dirs[3] && tile[id].minitile[2][0].connections != 0x20)
         tile[id].minitile[2][0].structnum = feature_id_count++;
      if (!tile[id].minitile[0][2].structnum && !tile[id].minitile[0][2].conn_dirs[0] && tile[id].minitile[0][2].connections != 0x80)
         tile[id].minitile[0][2].structnum = feature_id_count++;
      if (!tile[id].minitile[2][2].structnum && !tile[id].minitile[2][2].conn_dirs[3] && tile[id].minitile[2][2].connections != 0x40)
         tile[id].minitile[2][2].structnum = feature_id_count++;
   
      if (!tile[id].minitile[0][0].structnum && !tile[id].minitile[0][0].conn_dirs[1] && tile[id].minitile[0][0].connections != 0x10)
         tile[id].minitile[0][0].structnum = feature_id_count++;
      if (!tile[id].minitile[2][0].structnum && !tile[id].minitile[2][0].conn_dirs[1] && tile[id].minitile[2][0].connections != 0x20)
         tile[id].minitile[2][0].structnum = feature_id_count++;
      if (!tile[id].minitile[0][2].structnum && !tile[id].minitile[0][2].conn_dirs[2] && tile[id].minitile[0][2].connections != 0x80)
         tile[id].minitile[0][2].structnum = feature_id_count++;
      if (!tile[id].minitile[2][2].structnum && !tile[id].minitile[2][2].conn_dirs[2] && tile[id].minitile[2][2].connections != 0x40)
         tile[id].minitile[2][2].structnum = feature_id_count++;
   }

   /* Connect farms around a bent in the road */
   if ((tile[id].minitile[1][1].connections&road_mask) == 0x03 ||
       (tile[id].minitile[1][1].connections&road_mask) == 0x0c)
       if ((tile[id].minitile[0][0].connections&city_mask) != 0xf0)
         tile[id].minitile[0][0].structnum = tile[id].minitile[2][2].structnum;
   if ((tile[id].minitile[1][1].connections&road_mask) == 0x06 ||
       (tile[id].minitile[1][1].connections&road_mask) == 0x09)
       if ((tile[id].minitile[2][0].connections&city_mask) != 0xf0)
         tile[id].minitile[2][0].structnum = tile[id].minitile[0][2].structnum;

   /* Connect farms around a bent in the river */
   if ((tile[id].minitile[1][1].connections&road_mask) == 0x300 ||
       (tile[id].minitile[1][1].connections&road_mask) == 0xc00)
      tile[id].minitile[0][0].structnum = tile[id].minitile[2][2].structnum;
   if ((tile[id].minitile[1][1].connections&road_mask) == 0x600 ||
       (tile[id].minitile[1][1].connections&road_mask) == 0x900)
      tile[id].minitile[2][0].structnum = tile[id].minitile[0][2].structnum;
      
   /* Bridge */
   if (tile[id].flags & TILEF_BRIDGE) {
      tile[id].minitile[2][1].structnum = tile[id].minitile[0][1].structnum;
      tile[id].minitile[1][0].structnum = tile[id].minitile[1][2].structnum;
   }

   /* Ugly hack */
   /* Double bend */
   if (tile[id].flags & TILEF_DBLBEND) {
      if ((tile[id].minitile[2][0].connections & city_mask) ==  (tile[id].minitile[0][2].connections & city_mask))
         tile[id].minitile[2][0].structnum = tile[id].minitile[0][2].structnum;
      
      if ((tile[id].minitile[0][1].connections & city_mask) ==  (tile[id].minitile[1][0].connections & city_mask))
         tile[id].minitile[0][1].structnum = tile[id].minitile[1][0].structnum;
      if ((tile[id].minitile[2][1].connections & city_mask) ==  (tile[id].minitile[1][2].connections & city_mask))
         tile[id].minitile[2][1].structnum = tile[id].minitile[1][2].structnum;
   }

   /* Monastery */
   if (bonus & TILEF_MONASTERY)
      tile[id].minitile[1][1].structnum = feature_id_count++;
}

/* Render a big tile */
void render_tile(int id)
{
   DATAFILE *df;
   char s[128];
   BITMAP *bmp;
   int x, y;
   int transform;
   /* Create and draw a large tile */

   /* Clear tile list */
   for(y=0; y<3; y++) {
      for (x=0; x<3; x++) {
         //bmp = find_minitbitmap(0);
         //blit(bmp, tile[id].gfx, 0,0, x*16,y*16, 16, 16);
         
         bmp = tile[id].minitile[y][x].bmp;
         transform = tile[id].minitile[y][x].transform;

         if (bmp) {
            if (transform&3) {
               rotate_sprite(tile[id].gfx, bmp, x*16,y*16, quarter*(4-(transform&3)));
            } else if (transform&8) {
               draw_sprite_h_flip(tile[id].gfx, bmp, x*16,y*16);
            } else if (transform&4) {
               draw_sprite_v_flip(tile[id].gfx, bmp, x*16,y*16);
            } else {
               draw_sprite(tile[id].gfx, bmp, x*16,y*16);
            }
         }
      }
   }

   if (tile[id].flags & TILEF_INN) {
      snprintf(s, 128, "%08X", tile[id].flags & TILEF_INN);
      df = find_datafile_object(bonus_dat, s);
      if (df) {
         bmp = df->dat;
         for(y=0; y<3; y++) {
            for (x=0; x<2; x++) {
               if ((tile[id].minitile[y][x+1].connections & 0x0f)) {
                  tile[id].minitile[y][x].flags |= tile[id].flags;
                  draw_sprite(tile[id].gfx, bmp, x*16,y*16);
                  goto break_double_loop1;
               }
            }
         }
      }
   }
break_double_loop1:

   if (tile[id].flags & TILEF_SHIELD) {
      snprintf(s, 128, "%08X", tile[id].flags & TILEF_SHIELD);
      df = find_datafile_object(bonus_dat, s);
      if (df) {
         bmp = df->dat;
         for(y=0; y<3; y++) {
            for (x=0; x<3; x++) {
               if ((tile[id].minitile[y][x].connections & 0xf0) == 0xf0) {
                  draw_sprite(tile[id].gfx, bmp, x*16,y*16);
                  goto break_double_loop;
               }
            }
         }
      }
   }
break_double_loop:

   if (tile[id].flags & TILEF_TRADEANY) {
      bmp = get_goods_bitmap((tile[id].flags & TILEF_TRADEANY)>>8);
      for(y=0; y<3; y++) {
         for (x=0; x<3; x++) {
            if ((tile[id].minitile[y][x].connections & 0xf0) == 0xf0) {
               draw_sprite(tile[id].gfx, get_goods_bitmap(0), x*16,y*16);
               draw_sprite(tile[id].gfx, bmp, x*16,y*16);
               goto break_double_loop2;
            }
         }
      }
   }
break_double_loop2:
   
   if (tile[id].flags & TILEF_MONASTERY || tile[id].flags & TILEF_CATHEDRAL) {
      snprintf(s, 128, "%08X", tile[id].flags);
      df = find_datafile_object(bonus_dat, s);
      if (df) {
         bmp = df->dat;
         draw_sprite(tile[id].gfx, bmp, (TILE_SIZE-bmp->w)/2, (TILE_SIZE-bmp->h)/2);
      }
   }

   if (tile[id].flags & TILEF_BRIDGE && (tile[id].minitile[1][1].connections&road_mask) == 0x0f) {
      snprintf(s, 128, "%08X", tile[id].flags & TILEF_BRIDGE);
      df = find_datafile_object(bonus_dat, s);
      if (df) {
         bmp = df->dat;
         draw_sprite(tile[id].gfx, bmp, (TILE_SIZE-bmp->w)/2, (TILE_SIZE-bmp->h)/2);
      }
   }

   if (tile[id].flags & TILEF_DBLBEND && (tile[id].minitile[1][1].connections&road_mask) == 0x0f) {
      snprintf(s, 128, "%08X", tile[id].flags & TILEF_DBLBEND);
      df = find_datafile_object(bonus_dat, s);
      if (df) {
         bmp = df->dat;
         draw_sprite(tile[id].gfx, bmp, (TILE_SIZE-bmp->w)/2, (TILE_SIZE-bmp->h)/2);
      }
   }

   if (tile[id].flags & TILEF_CBRIDGE) {
      snprintf(s, 128, "%08X", tile[id].flags & TILEF_CBRIDGE);
      df = find_datafile_object(bonus_dat, s);
      if (df) {
         bmp = df->dat;
         draw_sprite(tile[id].gfx, bmp, (TILE_SIZE-bmp->w)/2, (TILE_SIZE-bmp->h)/2);
      }
   }
}

/* Rotate a big tile 270 deg anti-clockwise */
void rotate_big_tile_270(int id)
{
   MINITILE minitile[3][3];
   BITMAP *bmp;
   int x, y;
   int new_x, new_y;

   for (y=0; y<3; y++) {
      for (x=0; x<3; x++) {
         minitile[y][x] = tile[id].minitile[y][x];
      }
   }

   for (y=0; y<3; y++) {
      for (x=0; x<3; x++) {
         new_x = 1+(x-1)*rot270[0][0]+(y-1)*rot270[0][1];
         new_y = 1+(x-1)*rot270[1][0]+(y-1)*rot270[1][1];

         //printf("(%d, %d) -> (%d, %d)\n", x,y, new_x, new_y);

         tile[id].minitile[new_y][new_x].connections =
            rotate_pattern(minitile[y][x].connections, 1);
      }
   }

   for (y=0; y<3; y++) {
      for (x=0; x<3; x++) {
         new_x = 1+(x-1)*rot270[0][0]+(y-1)*rot270[0][1];
         new_y = 1+(x-1)*rot270[1][0]+(y-1)*rot270[1][1];

         tile[id].minitile[new_y][new_x].structnum = minitile[y][x].structnum;
      }
   }

   bmp = tilerenderbmp;
   rotate_sprite(bmp, tile[id].gfx, 0,0, quarter);
   tilerenderbmp = tile[id].gfx;
   tile[id].gfx = bmp;

   /* Re-render tile */
   tile[id].angle = (tile[id].angle+270)%360;
}

/* Rotate a big tile 90 deg anti-clockwise */
void rotate_big_tile_90(int id)
{
   rotate_big_tile_270(id);
   rotate_big_tile_270(id);
   rotate_big_tile_270(id);
}

/* Rotate a big tile 180 deg (anti-clockwise, but doesn't matter) */
void rotate_big_tile_180(int id)
{
   rotate_big_tile_270(id);
   rotate_big_tile_270(id);
}

/* Initialize a loaded tileset */
void initialize_tileset(void)
{
   int c, n;
   int tiles_constructed;

   /* Get number of tiletypes */

   tiles_in_play = 0;
   tiles_stackp = 0;
   num_tiles = 0;
   feature_id_count = 1;

   /* Get actual number of tiles in play */
   for (c=0; c<num_tile_types && game_tileset[c].amount; c++) {
      num_tiles += game_tileset[c].amount;
   }

   for (c=0; c<num_start_tile_types && start_tiles[c].amount; c++) {
      num_tiles += start_tiles[c].amount;
   }
   
   /* Allocate memory for tileset */
   tile = realloc(tile, num_tiles * sizeof *tile);
   playboard = malloc(num_tiles * sizeof *playboard);
   for (n=0; n<num_tiles; n++) {
      tile[n].gfx = create_bitmap(48, 48);
      playboard[n] = -1;
   }

   open_tiles = malloc((2*num_tiles+2) * sizeof *open_tiles);
   num_open_tiles = 0;

   tilerenderbmp = create_bitmap(48, 48);

   /* Construct tiles */
   tiles_constructed = 0;
   
   /* Start tiles */
   for (c=0; c<num_start_tiles && start_tiles[c].amount; c++) {
      for (n=0; n<start_tiles[c].amount; n++) {

         construct_tile(tiles_constructed, start_tiles[c].up,
                           start_tiles[c].left, start_tiles[c].right,
                              start_tiles[c].down, start_tiles[c].flags);
         map_minitile_graphics(tiles_constructed);
         render_tile(tiles_constructed);

         tiles_constructed++;
      }
   }

   /* Regular tiles */
   for (c=0; c<num_tile_types && game_tileset[c].amount; c++) {
      for (n=0; n<game_tileset[c].amount; n++) {

         construct_tile(tiles_constructed, game_tileset[c].up,
                           game_tileset[c].left, game_tileset[c].right,
                              game_tileset[c].down, game_tileset[c].flags);
         map_minitile_graphics(tiles_constructed);
         render_tile(tiles_constructed);

         tiles_constructed++;
      }
   }
}

/* Load graphics theme */
void load_theme(const char *filename)
{
   /* Load datafiles */
   theme_dat = load_datafile(get_game_path(filename));
   if (!theme_dat) {
      set_gfx_mode(GFX_TEXT, 0,0, 0,0);
      allegro_message("Error loading theme %s!\n", filename);
      exit(EXIT_FAILURE);
   }

   flags_dat = find_datafile_object(theme_dat, "Flags");
   goods_dat = find_datafile_object(theme_dat, "Goods");
   attrib_dat = find_datafile_object(theme_dat, "MenAttributes");
   shadows_dat = find_datafile_object(theme_dat, "MenShadow");
   men_dat = find_datafile_object(theme_dat, "Men");
   mapui_dat = find_datafile_object(theme_dat, "MapUI");
   tilegfx_dat = find_datafile_object(theme_dat, "Tiles");


   if (!flags_dat || !goods_dat || !attrib_dat || !shadows_dat || !men_dat || !mapui_dat || !tilegfx_dat) {
      set_gfx_mode(GFX_TEXT, 0,0, 0,0);
      allegro_message("Error loading theme %s!\n", filename);
      exit(EXIT_FAILURE);
   }
   flags_dat = flags_dat->dat;
   goods_dat = goods_dat->dat;
   attrib_dat = attrib_dat->dat;
   shadows_dat = shadows_dat->dat;
   men_dat = men_dat->dat;
   mapui_dat = mapui_dat->dat;
   tilegfx_dat = tilegfx_dat->dat;

   odd_tilegfx_dat = find_datafile_object(tilegfx_dat, "Odd");
   even_tilegfx_dat = find_datafile_object(tilegfx_dat, "Even");
   common_tilegfx_dat = find_datafile_object(tilegfx_dat, "Common");
   bonus_dat = find_datafile_object(tilegfx_dat, "Bonus");

   if (!odd_tilegfx_dat || !even_tilegfx_dat || !common_tilegfx_dat || !bonus_dat) {
      set_gfx_mode(GFX_TEXT, 0,0, 0,0);
      allegro_message("Error loading theme %s!\n", filename);
      exit(EXIT_FAILURE);
   }

   odd_tilegfx_dat = odd_tilegfx_dat->dat;
   even_tilegfx_dat = even_tilegfx_dat->dat;
   common_tilegfx_dat = common_tilegfx_dat->dat;
   bonus_dat = bonus_dat->dat;
}

void unload_theme(void)
{
   /* Unload datafiles */
   unload_datafile(theme_dat);
   theme_dat = NULL;

   flags_dat = NULL;
   goods_dat = NULL;
   attrib_dat = NULL;
   shadows_dat = NULL;
   men_dat = NULL;
   mapui_dat = NULL;
   tilegfx_dat = NULL;
}

void initialize_tiles(void)
{
   int n, c;
   DATAFILE *dc;
   char s[128];

   if (!theme_dat) {
      set_gfx_mode(GFX_TEXT, 0,0, 0,0);
      allegro_message("No theme loaded!\n");
      abort();
   }

   /* Initialize mini-tiles */
   /* Convert list */
   /* Allocate memory and initialize data structure */
   minitgfx_count = count_files(odd_tilegfx_dat) + count_files(even_tilegfx_dat) + count_files(common_tilegfx_dat);
   minitgfx = malloc(minitgfx_count * sizeof *minitgfx);
   for (n=0; n<minitgfx_count; n++) {
      minitgfx[n].ngfx = 0;
      minitgfx[n].conn_flags = 0;
      minitgfx[n].checkerboard_pattern = 0;
      minitgfx[n].bmp = NULL;
   }
   
   /* Convert bitmaps */
   n = 0;
   
   /* Load odd-checkerboard minitiles */
   for (dc = odd_tilegfx_dat; dc->type!=DAT_END; dc++) {
      if (dc->type==DAT_FILE) {
         snprintf(s, 128, "%s",get_datafile_property(dc, DAT_ID('N', 'A', 'M', 'E')));
         sscanf(s, "%x_DAT", &c);
         //printf("%s: %d, %d[%d]\n", s, c, n, minitgfx_count);
         
         minitgfx[n].checkerboard_pattern = CHECKERBOARD_ODD;
         
         convert_bitmap_list(dc->dat, minitgfx+n, c);
         n++;
      }
   }

   /* Load even-checkerboard minitiles */
   for (dc = even_tilegfx_dat; dc->type!=DAT_END; dc++) {
      if (dc->type==DAT_FILE) {
         snprintf(s, 128, "%s",get_datafile_property(dc, DAT_ID('N', 'A', 'M', 'E')));
         sscanf(s, "%x_DAT", &c);
         //printf("%s: %d, %d[%d]\n", s, c, n, minitgfx_count);
         
         minitgfx[n].checkerboard_pattern = CHECKERBOARD_EVEN;
         
         convert_bitmap_list(dc->dat, minitgfx+n, c);
         n++;
      }
   }

   /* Load common (both odd and even) checkerboard minitiles */
   for (dc = common_tilegfx_dat; dc->type!=DAT_END; dc++) {
      if (dc->type==DAT_FILE) {
         snprintf(s, 128, "%s",get_datafile_property(dc, DAT_ID('N', 'A', 'M', 'E')));
         sscanf(s, "%x_DAT", &c);
         //printf("%s: %d, %d[%d]\n", s, c, n, minitgfx_count);
         
         minitgfx[n].checkerboard_pattern = CHECKERBOARD_ANY;
         
         convert_bitmap_list(dc->dat, minitgfx+n, c);
         n++;
      }
   }

   dc = find_datafile_object(mapui_dat, "TILE");
   emptytile = (dc)?dc->dat:NULL;

   dc = find_datafile_object(mapui_dat, "GRID");
   grid = (dc)?dc->dat:NULL;
}

/* Set bonus count */
static void initialize_feature(int id)
{
   TILE *t;
   MINITILE *mt;
   int n, x, y;
   int inn_flag, shield_flag, trade_flag, cathedral_flag;

   feature_open[id] = 0;
   feature_type[id] = FEATURE_NONE;

   feature_bonus[id].cathedrals = 0;
   feature_bonus[id].shields = 0;
   feature_bonus[id].inns = 0;
   for (n=0; n<3; n++)
      feature_bonus[id].goods[n] = 0;
   
   for (n=0; n<num_tiles; n++) {
      inn_flag = 0;
      shield_flag = 0;
      trade_flag = 0;
      cathedral_flag = 0;
      t = &(tile[n]);
            
      for(y=0; y<3; y++) {
         for(x=0; x<3; x++) {
            mt = &(t->minitile[y][x]);
            
            /* Don't count bonuses that are already taken */
            /* Inn already taken... */
            if (t->flags&TILEF_INN && feature_bonus[mt->structnum].inns) {
               inn_flag = 1;
            }
            
            /* Shield already taken... */
            if (t->flags&TILEF_SHIELD && feature_bonus[mt->structnum].shields) {
               shield_flag = 1;
            }
            
            /* Tradegoods already taken... */
            if (t->flags&TILEF_TRADEANY && feature_bonus[mt->structnum].goods[((t->flags & TILEF_TRADEANY)>>8) - 1]) {
               trade_flag = 1;
            }
            
            if (mt->structnum==id) {
               /* Check shields */
               /* Make sure we don't accidentally count the shield twice */
               if (t->flags & TILEF_SHIELD && !shield_flag && (mt->connections&city_mask) == 0xf0) {
                  feature_bonus[id].shields++;
                  shield_flag = 1;
               }
               
               /* Check tradegoods */
               /* Make sure we don't accidentally count the goods twice */
               if (t->flags & TILEF_TRADEANY && !trade_flag && (mt->connections&0xf0) == 0xf0) {
                  feature_bonus[id].goods[((t->flags & TILEF_TRADEANY)>>8) - 1]++;
                  trade_flag = 1;
               }

               /* Check cathedrals */
               /* Make sure we don't accidentally count it twice */
               if (t->flags & TILEF_CATHEDRAL && !cathedral_flag) {
                  feature_bonus[id].cathedrals++;
                  cathedral_flag = 1;
               }
               
               /* Check inns */
               /* Make sure we don't accidentally count it twice */
               if (t->flags&TILEF_INN && !inn_flag && (mt->connections&0x0f)) {
                  feature_bonus[id].inns++;
                  inn_flag = 1;
               }
            }
         }
      }
   }
}

/* Shuffle tiles and place starting tile */
void initialize_gameboard(void)
{
   int n, c;
   int x, y;
   TILE t;
   
   /* Shuffle starting tiles */
   /* That is, all but the first and last starting tile */
   for (n=1; n<num_start_tiles-1; n++) {
      c = genrandui_range(num_start_tiles-2)+1;

      /* Swap tiles */
      t = tile[c];
      tile[c] = tile[n];
      tile[n] = t;
   }
   
   /* Shuffle pile */
   for (n=num_start_tiles; n<num_tiles; n++) {
      c = num_start_tiles+genrandui_range(num_tiles-num_start_tiles);

      /* Swap tiles */
      t = tile[c];
      tile[c] = tile[n];
      tile[n] = t;
   }

   /* Initialize all feature closure code */
   feature_open = calloc(1, feature_id_count * sizeof *feature_open);
   feature_type = calloc(1, feature_id_count * sizeof *feature_type);
   feature_bonus = calloc(1, feature_id_count * sizeof *feature_bonus);
   for(n=0; n<feature_id_count; n++) {
      initialize_feature(n);
   }

   /* Place starting tile at the front of the list and count feature openings */
   for (n=0; n<num_tiles; n++) {
      if (tile[n].minitile[0][1].connections == start_tiles[0].up &&
          tile[n].minitile[2][1].connections == start_tiles[0].down &&
          tile[n].minitile[1][0].connections == start_tiles[0].left &&
          tile[n].minitile[1][2].connections == start_tiles[0].right) {
         t = tile[0];
         tile[0] = tile[n];
         tile[n] = t;
         break;
      }
   }

   /* Count remaining openings for each feature */
   for (n=0; n<num_tiles; n++) {
      for (c=0; c<4; c++) {
         x = xypairs[c][0];
         y = xypairs[c][1];
         if (tile[n].minitile[y][x].connections && y!=x)
            feature_open[tile[n].minitile[y][x].structnum]++;
      }
   }

   place_tile(pop_tile(),  MAP_CX, MAP_CY);
}

/* Initialize a team */
void initialize_teams(const int peasants, const int bigmen, const int builders, const int pigs)
{
   int c, n;

   for (num_teams = 0; men_dat[num_teams].type != DAT_END; num_teams++);

   team = malloc(num_teams * sizeof *team);

   /* Initialize datastructure to keep track of peasants in each structure */
   feature_men = malloc(feature_id_count * sizeof *feature_men);
   for(c=0; c<feature_id_count; c++) {
      feature_men[c] = malloc(num_teams * sizeof **feature_men);
      for (n=0; n<num_teams; n++) {
         feature_men[c][n].men[PEASANTS] = 0;
         feature_men[c][n].men[BIGMEN] = 0;
         feature_men[c][n].men[MASTERBUILDERS] = 0;
         feature_men[c][n].men[PIGS] = 0;
      }
   }

   /* Carcasonne default game set */
   for(c=0; c<num_teams; c++) {
      for (n=0; n<MENTYPES; n++)
         team[c].men[n] = 0;

      team[c].men[PEASANTS] = peasants;
      team[c].men[BIGMEN] = bigmen;
      team[c].men[MASTERBUILDERS] = builders;
      team[c].men[PIGS] = pigs;

      team[c].colour = makecol(team_colours[c].r, team_colours[c].g, team_colours[c].b);

      for (n=0; n<FEATURE_TYPES; n++)
         team[c].score[n] = 0;

      for (n=0; n<GOODS_TYPES; n++) {
         team[c].goods[n] = 0;
         team[c].score_goods[n] = 0;
      }
   }

   /* Count maximum number of men that can fit on the map */
   n = 0;
   for(c=0; c<num_teams; c++) {
      n += team[c].men[0] + team[c].men[1] + team[c].men[2] + team[c].men[3];
   }
}

void destroy_teams(void)
{
   int c;

   for(c=0; c<feature_id_count; c++)
      free(feature_men[c]);
   free(feature_men);
   feature_men = NULL;

   free(team);
   team = NULL;
   num_teams = 0;
}

void destroy_tiles(void)
{
   int n;

   /* Destroy playingfield datastructure */
   free(playboard);
   playboard = NULL;
   free(open_tiles);
   open_tiles = NULL;

   num_open_tiles = 0;
   tiles_in_play = 0;
   tiles_stackp = 0;
   board_width = 0;
   board_height = 0;

   free(feature_open);
   free(feature_type);
   free(feature_bonus);
   
   feature_open = NULL;
   feature_type = NULL;
   feature_bonus = NULL;

   /* Unload minitile graphics */
   for (n=0; n<minitgfx_count; n++) {
      free(minitgfx[n].bmp);
   }
   free(minitgfx);
   minitgfx_count = 0;

   for (n=0; n<num_tiles; n++) {
      destroy_bitmap(tile[n].gfx);
   }

   free (tile);
   tile = NULL;

   destroy_bitmap(tilerenderbmp);
   tilerenderbmp = NULL;

   num_tiles = 0;

   free (mini_tile);
   mini_tile = NULL;

   num_mini_tiles = 0;
}

BITMAP *get_tile_bitmap(int id)
{
   assert(id<num_tiles);
   return tile[id].gfx;
}

/* Pop a tile off the pile. Returns NOTILE if there are no more tiles waiting */
int pop_tile(void)
{
   if (tiles_stackp==num_tiles)
      return NOTILE;
   else
      return tiles_stackp++;
}

int tiles_remaining(void)
{
   return num_tiles - tiles_stackp;
}

/* Does a binary search in the list of tiles placed on the map. */
/*  The code could use an actual hashing algorithm */
static int inline find_tile(int x, int y)
{
   int n;

   for (n=0; n<tiles_in_play && tile[playboard[n]].y <= y; n++)
      if (tile[playboard[n]].x == x && tile[playboard[n]].y == y) {
         return playboard[n];
      }

   return NOTILE;
#if 0
   int u, l, n;

   u = tiles_in_play-1;
   l = 0;
   n = (u+l)/2;

   while (u>=l) {
      n = (u+l)/2;
      if (tile[playboard[n]].y < y) {
         /* Lower half */
         l = n+1;
      } else if (tile[playboard[n]].y > y) {
         /* Lower half */
         u = n-1;
      } else {
         if (tile[playboard[n]].x < x) {
            /* Lower half */
            l = n+1;
         } else if (tile[playboard[n]].x > x) {
            /* Lower half */
            u = n-1;
         } else {
            return playboard[n];
         }
      }
   }

   if (tile[playboard[n]].x == x && tile[playboard[n]].y == y) {
      return playboard[n];
   }

   if (tile[playboard[u]].x == x && tile[playboard[u]].y == y) {
      return playboard[u];
   }

   if (tile[playboard[l]].x == x && tile[playboard[l]].y == y) {
      return playboard[l];
   }


   return NOTILE;
#endif
}

/********************************************/
/*  Dealing with the set of open tiles      */
/********************************************/

/* Does a binary search in the list of open board locations */
static int inline find_open_tile(int x, int y)
{
   int u, l, n;

   u = num_open_tiles-1;
   l = 0;
   n = (u-l)/2;

   while (u>=l) {
      n = (u+l)/2;
      if (open_tiles[n].y < y) {
         /* Lower half */
         l = n+1;
      } else if (open_tiles[n].y > y) {
         /* Lower half */
         u = n-1;
      } else {
         if (open_tiles[n].x < x) {
            /* Lower half */
            l = n+1;
         } else if (open_tiles[n].x > x) {
            /* Lower half */
            u = n-1;
         } else {
            return n;
         }
      }
   }

   if (open_tiles[n].x == x && open_tiles[n].y == y) {
      return n;
   }

   return -1;
}

/* Sort the list of open tiles */
static void sort_opentile_list(void)
{
   /* Tile sorter callback. Sort first by y, then by x */
   static int tile_sort_callback(const void *t1, const void *t2)
   {
      const TILE_LOCATION *tn1 = (const TILE_LOCATION *)(t1);
      const TILE_LOCATION *tn2 = (const TILE_LOCATION *)(t2);
      if (tn1->y > tn2->y)
         return 1;
      if (tn1->y < tn2->y)
         return -1;

      return tn1->x > tn2->x;
   }
   qsort(open_tiles, num_open_tiles, sizeof *open_tiles, tile_sort_callback);
}

static void inline mark_open_tile(int x, int y)
{
   /* Don't insert tiles that are already in the list */
   if (find_open_tile(x, y)==-1 && find_tile(x, y)==NOTILE) {
      open_tiles[num_open_tiles].x = x;
      open_tiles[num_open_tiles].y = y;
      num_open_tiles++;
      sort_opentile_list();
   }
}

static void inline remove_open_tile(int x, int y)
{
   int c;
   c = find_open_tile(x, y);
   if (c!=-1) {
      num_open_tiles--;
      open_tiles[c].x = open_tiles[num_open_tiles].x;
      open_tiles[c].y = open_tiles[num_open_tiles].y;
      sort_opentile_list();
   }
}

/********************************************/
/*          Code dealing with teams         */
/********************************************/
int get_partial_team_score(const int team_nr, const int ft)
{
   return team[team_nr].score[ft];
}

int get_team_good_count(const int team_nr, const int ft)
{
   return team[team_nr].goods[ft];
}

int get_team_score(const int nr)
{
   int n, score;
   
   score = 0;
   
   for (n=0; n<FEATURE_TYPES; n++)
      score += team[nr].score[n];

   for (n=0; n<3; n++)
      score += team[nr].score_goods[n];
   return score;
}

int get_team_colour(const int nr)
{
   return team[nr].colour;
}

int get_num_team_names(void)
{
   return num_teams;
}

const char *get_team_names(const int c)
{
   /* Skip first character that is only used for ordering in the datafile */
   return get_datafile_property(men_dat + c, DAT_ID('N', 'A', 'M', 'E'))+1;
}

const RLE_SPRITE *get_feature_man_attribute(int feature_type)
{
   return attrib_dat[2+feature_type].dat;
}

const RLE_SPRITE *get_team_man_sprite(const int team, const int type)
{
   DATAFILE *df;

   df = men_dat[team].dat;
   return df[type].dat;
}

/* Return all men from a certain feature to the player */
static void remove_men(const int id)
{
   int n, c;
   int x, y;

   /* id=0 is special */
   if (id==0)
      return;

   n = 0;

   for (c=0; c<num_teams; c++) {
      for (n=0; n<MENTYPES; n++) {
         team[c].men[n] += feature_men[id][c].men[n];
         feature_men[id][c].men[n] = 0;
      }
   }

   for (n=0; n<tiles_in_play; n++) {
      for(y=0; y<3; y++) {
         for(x=0; x<3; x++) {
            if (tile[playboard[n]].minitile[y][x].structnum==id) {
               tile[playboard[n]].minitile[y][x].man_team=-1;
               tile[playboard[n]].minitile[y][x].man_type=0;
            }
         }
      }
   }
}

/* Get number of men of a specific type on features of tile id */
int get_tile_feature_men(const int tile_id, const int team, const int man_type)
{
   int x, y;
   int n, count;
   
   count = 0;
   for(y=0; y<3; y++) {
      for(x=0; x<3; x++) {
         n = tile[tile_id].minitile[y][x].structnum;
         count += feature_men[n][team].men[man_type];
      }
   }
   
   return count;
}

/* Return the number of men belonging to one team on a feature */
static int get_feature_team_men_count(const int id, const int team)
{
   /* id=0 is special */
   if (id==0)
      return 0;

   return feature_men[id][team].men[PEASANTS] + 2*feature_men[id][team].men[BIGMEN];
}

/* Returns the largest number of men belonging to a single player on a feature */
static int get_feature_largest_force(const int id)
{
   int n,c;
   int max = 0;

   for (c=0; c<num_teams; c++) {
      n = get_feature_team_men_count(id, c);
      if (n>max)
         max = n;
   }
   return max;
}

/* Returns the total number of men standing on a feature */
static int get_feature_men_count(const int id)
{
   int n,c;

   /* id=0 is special - since this function is used to know if we can place */
   /*  a man somewhere, we treat this as a special case here */
   if (id==0)
      return 1;

   if (team<0)
      return 0;

   n = 0;

   for (c=0; c<num_teams; c++)
      n+=get_feature_team_men_count(id, c);

   return n;
}

/* Returns the total number of men still available to a player */
int get_team_men_count(const int t, const int type)
{
   return team[t].men[type];
}

void place_man(const int tx, const int ty, const int mtx, const int mty, const int t, const int type)
{
   int n, id;

   n = find_tile(tx, ty);

   if (n == NOTILE)
      return;

   id = tile[n].minitile[mty][mtx].structnum;
   feature_men[id][t].men[type]++;
   team[t].men[type]--;

   tile[n].minitile[mty][mtx].man_type = type;
   tile[n].minitile[mty][mtx].man_team = t;
}

/********************************************/
/*             Trade goods                  */
/********************************************/

BITMAP *get_goods_bitmap(const int type)
{
   return goods_dat[type].dat;
}

int get_goods_count(const int team_nr, const int type)
{
   return team[team_nr].goods[type];
}

/********************************************/
/*             Tilemap code                 */
/********************************************/

/* Connect features */
static void replace_feature_id(int old_id, int new_id)
{
   int n, c;
   int x, y;

   if (!old_id || !new_id)
      return;

   /* Calculate new number of open sides                                    */
   /* This is the sum of the previous two, minus the sides that now connect */
   /* This must be calculated even if old_id==new_id, because two sides of  */
   /*  the placed tilemay beconnected `the long way round' as well as on    */
   /*  the tile itself */
   if (old_id == new_id) {
      feature_open[new_id]-=2;

      /* Don't bother with the rest, as it wouldn't change anything and only */
      /*  slows things down. */
      return;
   }
   
   feature_open[new_id]+=feature_open[old_id]-2;
   feature_type[old_id] = FEATURE_NONE;
   
   /* Join bonuses */
   for (n=0; n<3; n++)
      feature_bonus[new_id].goods[n] += feature_bonus[old_id].goods[n];

   feature_bonus[new_id].shields += feature_bonus[old_id].shields;
   feature_bonus[new_id].cathedrals += feature_bonus[old_id].cathedrals;
   feature_bonus[new_id].inns += feature_bonus[old_id].inns;

   for (n=0; n<3; n++)
      feature_bonus[old_id].goods[n] = 0;

   feature_bonus[old_id].shields = 0;
   feature_bonus[old_id].cathedrals = 0;
   feature_bonus[old_id].inns = 0;

   /* Join men */
   for (n=0; n<num_teams; n++)
      for (c=0; c<MENTYPES; c++)
         feature_men[new_id][n].men[c] += feature_men[old_id][n].men[c];

   /* Paint tiles */
   for (n=0; n<tiles_in_play; n++) {
      for(y=0; y<3; y++) {
         for(x=0; x<3; x++) {
            if (tile[playboard[n]].minitile[y][x].structnum==old_id)
               tile[playboard[n]].minitile[y][x].structnum = new_id;
         }
      }
   }
}

/* Add an integer to a list of integers if it isn't already there */
static int inline store_integer(const int value, int *list, int list_size)
{
   int c;

   for (c=0; c<list_size; c++) {
      if (list[c]==value)
         return list_size;
   }
   list[list_size] = value;
   list_size++;

   return list_size;
}

/* Find a list of all farms adjacent to a city. *argv should be large */
/*  enough to hold all of them */
static int find_farm_list(const int city_id, int *argv)
{
   int x, y;
   int count;
   int num;
   int n;

   count = 0;

   for (n=0; n<tiles_in_play; n++) {
      if ((tile[playboard[n]].flags&TILEF_MONASTERY)==0)
         for(y=0; y<3; y++) {
            for(x=0; x<3; x++) {
               if (tile[playboard[n]].minitile[y][x].structnum==city_id) {
                  if (x>0) {
                     num = tile[playboard[n]].minitile[y][x-1].structnum;
                     if (feature_type[num] == FEATURE_FARM)
                        count = store_integer(num, argv, count);
                  }
                  if (x<2) {
                     num = tile[playboard[n]].minitile[y][x+1].structnum;
                     if (feature_type[num] == FEATURE_FARM)
                        count = store_integer(num, argv, count);
                  }
                  if (y>0) {
                     num = tile[playboard[n]].minitile[y-1][x].structnum;
                     if (feature_type[num] == FEATURE_FARM)
                        count = store_integer(num, argv, count);
                  }
                  if (y<2) {
                     num = tile[playboard[n]].minitile[y+1][x].structnum;
                     if (feature_type[num] == FEATURE_FARM)
                        count = store_integer(num, argv, count);
                  }
               }
            }
         }
   }

   return count;
}

/* Award points for the player with the most farmers in a set of farms */
/* Base score is the score without a pig, bonus score is the score with a pig */
static void score_farms(int *list, int count, int base_score, int bonus_score)
{
   int *team_men;
   int *team_pigs;
   int num_men;
   int c;
   int id;

   team_pigs = malloc(num_teams * sizeof *team_pigs);
   team_men = malloc(num_teams * sizeof *team_men);

   /* Find the total number of men each player has in the farms */
   for(c=0; c<num_teams; c++) {
      team_pigs[c] = 0;
      team_men[c] = 0;
   }

   for(id=0; id<count; id++) {
      for(c=0; c<num_teams; c++) {
         team_pigs[c] += feature_men[list[id]][c].men[PIGS];
         team_men[c] += get_feature_team_men_count(list[id], c);
      }
   }

   num_men = 0;
   for(c=0; c<num_teams; c++)
      if (team_men[c] > num_men)
         num_men=team_men[c];

   /* No men here at all */
   if (!num_men)
      return;

   /* Find teams that deserve points for this city */
   for (c=0; c<num_teams; c++) {
      if (team_men[c]==num_men) {
         if (team_pigs[c])
            team[c].score[FEATURE_FARM]+=bonus_score;
         else
            team[c].score[FEATURE_FARM]+=base_score;
      }
   }
}

/* Count monastery */
static int score_monastery(const int x, const int y)
{
   int score=0;
   int tx, ty;

   for(tx=-1; tx<2; tx++) {
      for(ty=-1; ty<2; ty++) {
         if (find_tile(x+tx, y+ty)!=NOTILE)
            score++;
      }
   }

   return score;
}

/* Award tradegoods from the completed city id to the trader */
static void score_tradegoods(int id, int trader)
{
   int n;
   
   for (n=0; n<3; n++) {
      team[trader].goods[n] += feature_bonus[id].goods[n];
      feature_bonus[id].goods[n] = 0;
   }
}

/* Count city points. */
/* The base and bonus multipliers affect Cathedrals and such */
static void score_city(const int id, const int base_multi, const int bonus_multi)
{
   int n, c, num_men;
   int x, y;
   int score = 0;
   int multi;

   /* Don't count unfinished cities with cathedrals */
   if (bonus_multi==0 && feature_bonus[id].cathedrals)
      return;

   /* Count number of tiles */
   for (n=0; n<tiles_in_play; n++) {
      for(y=0; y<3; y++) {
         for(x=0; x<3; x++) {
            if (tile[playboard[n]].minitile[y][x].structnum==id) {
               score++;
               goto break_double_loop;
            }
         }
      }
break_double_loop:;
   }
   
   /* Bonus Shields */
   score += feature_bonus[id].shields*city_shield_bonus;

   /* Find the largest force */
   num_men = get_feature_largest_force(id);

   /* No men here at all */
   if (!num_men)
      return;

   if (score==2)  /* City of two tiles only counts for 2 points */
      multi = 1;
   else if (feature_bonus[id].cathedrals)
      multi = bonus_multi;
   else
      multi = base_multi;

   /* Find teams that deserve points for this city */
   for (c=0; c<num_teams; c++) {
      if (get_feature_team_men_count(id, c)==num_men) {
         team[c].score[FEATURE_CITY]+=multi*score;
      }
   }
}

/* Count road points. */
/* The base and bonus multipliers affect inns and such */
static void score_road(const int id, const int base_multi, const int bonus_multi)
{
   int n, c, num_men;
   int x, y;
   int score = 0;
   int multi;
   int terminal_count;

   /* Don't count unfinished roads with inns */
   if ((bonus_multi==0 && feature_bonus[id].inns))
      return;

   /* Count number of tiles */
   terminal_count = 0;
   for (n=0; n<tiles_in_play; n++) {
      for(y=0; y<3; y++) {
         for(x=0; x<3; x++) {
            if (tile[playboard[n]].minitile[y][x].structnum==id) {
               if (tile[playboard[n]].minitile[1][1].structnum!=id && !(tile[playboard[n]].flags&TILEF_MOD))
                  terminal_count++;
               score++;
               goto break_double_loop;
            }
         }
      }
break_double_loop:;
   }

//   if (terminal_count==0)
//      return;

   /* Find the largest force */
   num_men = get_feature_largest_force(id);

   /* No men here at all */
   if (!num_men)
      return;

   if (feature_bonus[id].inns)
      multi = bonus_multi;
   else
      multi = base_multi;

   /* Find teams that deserve points for this city */
   for (c=0; c<num_teams; c++) {
      if (get_feature_team_men_count(id, c)==num_men) {
         team[c].score[FEATURE_ROAD]+=multi*score;
      }
   }
}

/* Calculate score for a tile */
/* Should only be called once for each tile */
/* trader is the player the tradegoods should be awarded to. */
void score_tile(int x, int y, int trader)
{
   int conn, id;
   int tx, ty;
   int n, c;

   n = find_tile(x, y);

   if (n==NOTILE)
      return;

   /* Check roads and cities */
   for (c=0; c<4; c++) {
      conn = tile[n].minitile[xypairs[c][1]][xypairs[c][0]].connections;
      id = tile[n].minitile[xypairs[c][1]][xypairs[c][0]].structnum;

      if (feature_open[id]==0) {
         if (feature_type[id] == FEATURE_ROAD) {
            /* Score road */
            score_road(id, 1, game_inn_multi);
            remove_men(id);
         } else if (feature_type[id] == FEATURE_CITY) {
            /* Score city */
            score_city(id, 2, game_cathedral_multi);
            score_tradegoods(id, trader);
            remove_men(id);
         }
      }
   }

   /* Score monasteries */
   for(tx=-1; tx<2; tx++) {
      for(ty=-1; ty<2; ty++) {
         n = find_tile(x+tx, y+ty);
         if (n!=NOTILE && tile[n].flags&TILEF_MONASTERY && tile[n].minitile[1][1].man_team!=-1) {
            /* Check if it is completed */
            if (score_monastery(x+tx, y+ty)==9) {
               /* Add score */
               team[tile[n].minitile[1][1].man_team].score[FEATURE_MONASTERY]+=9;
               remove_men(tile[n].minitile[1][1].structnum);
            }
         }
      }
   }
}

/* End score for partially build structures and farms */
void end_score(void)
{
   int n, c, id;
   int *farm_list;
   int m_count;

   for (id = 0; id<feature_id_count; id++) {
      /* Only score unfinished items */
      if (feature_open[id]>0) {
         switch (feature_type[id]) {
            case FEATURE_ROAD:
               score_road(id, 1, final_inn_multi);
               remove_men(id);
               break;
            case FEATURE_CITY:
               score_city(id, 1, final_cathedral_multi);
               remove_men(id);
               break;
         }
      }
   }

   /* Monasteries */
   for (n = 0; n<tiles_in_play; n++) {
      if (tile[n].flags&TILEF_MONASTERY && tile[n].minitile[1][1].man_team!=-1) {
         team[tile[n].minitile[1][1].man_team].score[FEATURE_MONASTERY] += score_monastery(tile[n].x, tile[n].y);
         remove_men(tile[n].minitile[1][1].structnum);
      }
   }

   /* Farms */
   farm_list = malloc(feature_id_count * sizeof *farm_list);

   for (id = 0; id<feature_id_count; id++) {
      /* Only score unfinished items */
      if (feature_open[id]==0 && feature_type[id]==FEATURE_CITY) {
         m_count = find_farm_list(id, farm_list);
         /* Award score for peasants */
         score_farms(farm_list, m_count, base_farm_multi, bonus_farm_multi);
      }
   }
   free(farm_list);
   
   /* Tradegoods */
   for (n=0; n<GOODS_TYPES; n++) {
      /* Find majority */
      m_count = 0;
      for (c=0; c<num_teams; c++) {
         if (team[c].goods[n]>m_count)
            m_count = team[c].goods[n];
      }
      
      /* Award points */
      if (m_count) {
         for (c=0; c<num_teams; c++) {
            if (team[c].goods[n]==m_count)
               team[c].score_goods[n] += tradegood_bonus;
         }
      }
   }
   

   for (id = 0; id<feature_id_count; id++)
      if (feature_type[id]!=FEATURE_NONE)
         remove_men(id);
}

void connect_tile(int x, int y)
{
   int id, upper, lower, left, right;

   id = find_tile(x, y);
   upper = find_tile(x, y-1);
   lower = find_tile(x, y+1);
   left = find_tile(x-1, y);
   right = find_tile(x+1, y);

   if (id == NOTILE)
      return;

   //printf ("%d\t%d\t%d\t%d\t%d\n", id, upper, lower, left, right);

   if (right != NOTILE) {
      replace_feature_id(tile[right].minitile[1][0].structnum, tile[id].minitile[1][2].structnum);
      if ((tile[right].minitile[1][0].connections&city_mask) == 0) {
         replace_feature_id(tile[right].minitile[0][0].structnum, tile[id].minitile[0][2].structnum);
         replace_feature_id(tile[right].minitile[2][0].structnum, tile[id].minitile[2][2].structnum);
      }
   }

   if (left != NOTILE) {
      replace_feature_id(tile[left].minitile[1][2].structnum, tile[id].minitile[1][0].structnum);
      if ((tile[left].minitile[1][2].connections&city_mask) == 0) {
         replace_feature_id(tile[left].minitile[0][2].structnum, tile[id].minitile[0][0].structnum);
         replace_feature_id(tile[left].minitile[2][2].structnum, tile[id].minitile[2][0].structnum);
      }
   }

   if (upper != NOTILE) {
      replace_feature_id(tile[upper].minitile[2][1].structnum, tile[id].minitile[0][1].structnum);
      if ((tile[upper].minitile[2][1].connections&city_mask) == 0) {
         replace_feature_id(tile[upper].minitile[2][0].structnum, tile[id].minitile[0][0].structnum);
         replace_feature_id(tile[upper].minitile[2][2].structnum, tile[id].minitile[0][2].structnum);
      }
   }

   if (lower != NOTILE) {
      replace_feature_id(tile[lower].minitile[0][1].structnum, tile[id].minitile[2][1].structnum);
      if ((tile[lower].minitile[0][1].connections&city_mask) == 0) {
         replace_feature_id(tile[lower].minitile[0][0].structnum, tile[id].minitile[2][0].structnum);
         replace_feature_id(tile[lower].minitile[0][2].structnum, tile[id].minitile[2][2].structnum);
      }
   }
}

/* Place a tile on the game board */
void place_tile(int id, int map_x, int map_y)
{
   int x, y;
   
   /* Tile sorter callback. Sort first by y, then by x */
   static int tile_sort_callback(const void *t1, const void *t2)
   {
      const int tn1 = *(const int *)(t1);
      const int tn2 = *(const int *)(t2);
      if (tile[tn1].y > tile[tn2].y)
         return 1;
      if (tile[tn1].y < tile[tn2].y)
         return -1;

      return tile[tn1].x > tile[tn2].x;
   }
   int c, num, conn;
   
   /* This can be done more efficient... */
   playboard[tiles_in_play] = id;
   tile[playboard[tiles_in_play]].x = map_x;
   tile[playboard[tiles_in_play]].y = map_y;
   tiles_in_play++;

   /* Update the list of open tiles */
   remove_open_tile(map_x, map_y);
   mark_open_tile(map_x+1, map_y);
   mark_open_tile(map_x-1, map_y);
   mark_open_tile(map_x, map_y+1);
   mark_open_tile(map_x, map_y-1);

   /* Sort the in-game tiles */
   qsort(playboard, tiles_in_play, sizeof *playboard, tile_sort_callback);

   /* Record the type of features on this tile */
   for (c=0; c<4; c++) {
      y = xypairs[c][1];
      x = xypairs[c][0];
      num = tile[id].minitile[y][x].structnum;
      conn = tile[id].minitile[y][x].connections;
      if (conn==0)
         feature_type[num] = FEATURE_FARM;
      else if ((conn&mid_excity_flags[c]) == mid_excity_flags[c])
         feature_type[num] = FEATURE_CITY;
      else if (conn&road_mask)
         feature_type[num] = FEATURE_ROAD;
   }
   if (tile[id].flags & TILEF_MONASTERY) {
      num = tile[id].minitile[1][1].structnum;
      feature_type[num] = FEATURE_MONASTERY;
   } else if (tile[id].minitile[1][1].connections==0) {
      num = tile[id].minitile[1][1].structnum;
      feature_type[num] = FEATURE_FARM;
   }

   /* Properly assign FARM numbers to corner tiles */
   for (c=0; c<4; c++) {
      y = xycorners[c][1];
      x = xycorners[c][0];

      num = tile[id].minitile[y][x].structnum;

      if (feature_type[num] != FEATURE_CITY && feature_type[num] != FEATURE_ROAD)
         feature_type[num] = FEATURE_FARM;

      if ((tile[id].minitile[y][x].connections&city_mask) == 0xf0)
         feature_type[num] = FEATURE_CITY;         
   }
}

/* Helper function: Returns TRUE if a rivertile will fit */
static int rivertile_fits(const int id, const int x, const int y, const int upper, const int lower, const int left, const int right)
{
   int pattern;
   int flag;

   /* River must connect, but a river tile may not be adjacent to more than */
   /*  one horizontal or vertical river tile when placed */
   
   if (right!=NOTILE) {
      pattern = tile[right].minitile[1][0].connections & 0x62626262;
      flag = flip_pattern(tile[id].minitile[1][2].connections, 8) & 0x62626262;

      if (pattern!=flag)
         return 0;
         
      pattern = tile[right].minitile[1][0].connections & 0x00000200;
      
      if (!pattern)
         return 0;
         
      /* Check if placing the tile here will force the next river tile to be */
      /*  adjacent to more than one tile */
      /* (Check only needed if current tile is a bend) */
      if ((tile[id].minitile[1][1].connections & 0x00000500) && (tile[id].minitile[1][1].connections & 0x00000a00)) {
         if ((tile[right].minitile[2][1].connections & 0x00000500) && (tile[id].minitile[2][1].connections & 0x00000500))
            return 0;
         if ((tile[right].minitile[0][1].connections & 0x00000500) && (tile[id].minitile[0][1].connections & 0x00000500))
            return 0;
      }
         
      return 1;
   }

   if (left!=NOTILE) {
      pattern = tile[left].minitile[1][2].connections & 0x98989898;
      flag = flip_pattern(tile[id].minitile[1][0].connections, 8) & 0x98989898;

      if (pattern!=flag)
         return 0;

      pattern = tile[left].minitile[1][2].connections & 0x00000800;

      if (!pattern)
         return 0;

      /* Check if placing the tile here will force the next river tile to be */
      /*  adjacent to more than one tile */
      /* (Check only needed if current tile is a bend) */
      if ((tile[id].minitile[1][1].connections & 0x00000500) && (tile[id].minitile[1][1].connections & 0x00000a00)) {
         if ((tile[left].minitile[2][1].connections & 0x00000500) && (tile[id].minitile[2][1].connections & 0x00000500))
            return 0;
         if ((tile[left].minitile[0][1].connections & 0x00000500) && (tile[id].minitile[0][1].connections & 0x00000500))
            return 0;
      }

      return 1;
   }

   if (upper!=NOTILE) {
      pattern = tile[upper].minitile[2][1].connections & 0x31313131;
      flag = flip_pattern(tile[id].minitile[0][1].connections, 4) & 0x31313131;

      if (pattern!=flag)
         return 0;

      pattern = tile[upper].minitile[2][1].connections & 0x00000100;

      if (!pattern)
         return 0;

      /* Check if placing the tile here will force the next river tile to be */
      /*  adjacent to more than one tile */
      /* (Check only needed if current tile is a bend) */
      if ((tile[id].minitile[1][1].connections & 0x00000500) && (tile[id].minitile[1][1].connections & 0x00000a00)) {
         if ((tile[upper].minitile[1][2].connections & 0x00000a00) && (tile[id].minitile[1][2].connections & 0x00000a00))
            return 0;
         if ((tile[upper].minitile[1][0].connections & 0x00000a00) && (tile[id].minitile[1][0].connections & 0x00000a00))
            return 0;
      }

      return 1;
   }

   if (lower!=NOTILE) {
      pattern = tile[lower].minitile[0][1].connections & 0xc4c4c4c4;
      flag = flip_pattern(tile[id].minitile[2][1].connections, 4) & 0xc4c4c4c4;

      if (pattern!=flag)
         return 0;

      pattern = tile[lower].minitile[0][1].connections & 0x00000400;

      if (!pattern)
         return 0;

      /* Check if placing the tile here will force the next river tile to be */
      /*  adjacent to more than one tile */
      /* (Check only needed if current tile is a bend) */
      if ((tile[id].minitile[1][1].connections & 0x00000500) && (tile[id].minitile[1][1].connections & 0x00000a00)) {
         if ((tile[lower].minitile[1][0].connections & 0x00000a00) && (tile[id].minitile[1][0].connections & 0x00000a00))
            return 0;
         if ((tile[lower].minitile[1][2].connections & 0x00000a00) && (tile[id].minitile[1][2].connections & 0x00000a00))
            return 0;
      }

      return 1;
   }
   
   return 0;
}

/* Returns TRUE if a tile will fit *in its current orientation* */
int tile_fits(const int id, const int x, const int y)
{
   int upper, lower, left, right;
   int pattern;
   int flag;

   if (id == NOTILE)
      return 0;

   if (find_tile(x, y) != NOTILE)
      return 0;

   upper = find_tile(x, y-1);
   lower = find_tile(x, y+1);
   left = find_tile(x-1, y);
   right = find_tile(x+1, y);

   if (upper==lower && left==right) {
      /* Only possible if all are -1, as tile IDs are unique */
      /* Tiles must be placed adjacent to each other */
      return 0;
   }

   /* The river must be placed continuously */
   if (tile[id].minitile[1][1].connections & river_mask)
      return rivertile_fits(id, x, y, upper, lower, left, right);

   /* See if it will fit on the right */
   if (right!=NOTILE) {
      pattern = tile[right].minitile[1][0].connections & 0x62626262;
      flag = flip_pattern(tile[id].minitile[1][2].connections, 8) & 0x62626262;

      /*
      printf("%02x, %02x (%02x)\n", tile[right].minitile[1][0].connections,
                                    flip_pattern(tile[id].minitile[1][2].connections, 8),
                                    tile[id].minitile[1][2].connections);
      */

      if (pattern!=flag)
         return 0;
   }

   if (left!=NOTILE) {
      pattern = tile[left].minitile[1][2].connections & 0x98989898;
      flag = flip_pattern(tile[id].minitile[1][0].connections, 8) & 0x98989898;

      if (pattern!=flag)
         return 0;
   }

   if (upper!=NOTILE) {
      pattern = tile[upper].minitile[2][1].connections & 0x31313131;
      flag = flip_pattern(tile[id].minitile[0][1].connections, 4) & 0x31313131;

      if (pattern!=flag)
         return 0;
   }

   if (lower!=NOTILE) {
      pattern = tile[lower].minitile[0][1].connections & 0xc4c4c4c4;
      flag = flip_pattern(tile[id].minitile[2][1].connections, 4) & 0xc4c4c4c4;

      if (pattern!=flag)
         return 0;
   }
   
   return 1;
}

/* Returns TRUE if a tile will fit anywhere *in its current orientation* */
int tile_fits_anywhere(const int id)
{
   int n;

   if (id == NOTILE)
      return 0;

   for (n=0; n<num_open_tiles; n++)
      if (tile_fits(id, open_tiles[n].x, open_tiles[n].y))
         return 1;

   return 0;
}

/* Returns TRUE if a tile will fit anywhere *in any orientation* */
int tile_fits_anywhere_anyway(const int id)
{
   int n;

   if (id == NOTILE)
      return 0;

   for (n=0; n<4; n++) {
      if (tile_fits_anywhere(id))
         return 1;
      rotate_big_tile_270(id);
   }

   return 0;
}


int can_place_flag(int tile_x, int tile_y, int mx, int my, int team, int type)
{
   int n;
   int structnum;

   n = find_tile(tile_x, tile_y);
   if (n==NOTILE)
      return FALSE;
      
   structnum = tile[n].minitile[my][mx].structnum;
   switch (type) {
      case PEASANTS:
      case BIGMEN:
         return get_feature_men_count(structnum)==0;
      case MASTERBUILDERS:
         if (feature_type[structnum] == FEATURE_CITY || feature_type[structnum] == FEATURE_ROAD)
            return get_feature_team_men_count(tile[n].minitile[my][mx].structnum, team)!=0;
      case PIGS:
         if (feature_type[structnum] != FEATURE_CITY && feature_type[structnum] != FEATURE_ROAD)
            return get_feature_team_men_count(tile[n].minitile[my][mx].structnum, team)!=0;
   }
   
   return FALSE;
}

void draw_flags(BITMAP *dest, int dest_x, int dest_y, int tx, int ty, int team, int type)
{
   int mtx, mty;
   int structnum;
   int n;

   n = find_tile(tx, ty);
   if (n>NOTILE) {
      for(mtx=0; mtx<3; mtx++) {
         for (mty=0; mty<3; mty++) {
         
            structnum = tile[n].minitile[mty][mtx].structnum;
            
            switch (type) {
               case PEASANTS:
               case BIGMEN:
                  if (!get_feature_men_count(structnum))
                     draw_sprite(dest, flags_dat[0].dat, dest_x+mtx*TILE_SIZE/3, 
                                    dest_y+mty*TILE_SIZE/3-21+8);
                  break;
               case MASTERBUILDERS:
                  if ((feature_type[structnum] == FEATURE_CITY || feature_type[structnum] == FEATURE_ROAD) && get_feature_team_men_count(tile[n].minitile[mty][mtx].structnum, team)!=0)
                     draw_sprite(dest, flags_dat[0].dat,
                                 dest_x+mtx*TILE_SIZE/3,
                                 dest_y+mty*TILE_SIZE/3-21+8);
                  break;
               case PIGS:
                  if (feature_type[structnum] != FEATURE_CITY && feature_type[structnum] != FEATURE_ROAD && get_feature_team_men_count(tile[n].minitile[mty][mtx].structnum, team)!=0)
                     draw_sprite(dest, flags_dat[0].dat,
                                 dest_x+mtx*TILE_SIZE/3,
                                 dest_y+mty*TILE_SIZE/3-21+8);
                  break;
            }
         }
      }
   }
}

/* Return the number of men that might be placed on a tile */
int count_number_flags(int tx, int ty)
{
   int mtx, mty;
   int n;
   int num = 0;

   n = find_tile(tx, ty);
   if (n>NOTILE) {
      for(mtx=0; mtx<3; mtx++) {
         for (mty=0; mty<3; mty++) {
            if (!get_feature_men_count(tile[n].minitile[mty][mtx].structnum))
               num++;
         }
      }
   }
   
   return num;
}

static inline void draw_men(BITMAP *dest, int tile_num, int dest_x, int dest_y)
{
   int mtx, mty;
   int n;

   n = tile_num;

   for(mtx=0; mtx<3; mtx++) {
      for (mty=0; mty<3; mty++) {
         if (tile[n].minitile[mty][mtx].man_team!=-1) {
            const RLE_SPRITE *sprite = get_team_man_sprite(tile[n].minitile[mty][mtx].man_team, tile[n].minitile[mty][mtx].man_type);
            draw_rle_sprite(dest, sprite, dest_x+mtx*TILE_SIZE/3,
                                          dest_y+(mty+1)*TILE_SIZE/3 - sprite->h);
            /* Shadow outline */
            sprite = shadows_dat[tile[n].minitile[mty][mtx].man_type].dat;
            draw_rle_sprite(dest, sprite, dest_x+mtx*TILE_SIZE/3,
                                          dest_y+(mty+1)*TILE_SIZE/3 - sprite->h);

            /* Modifiers */
            if (tile[n].minitile[mty][mtx].man_type==0) {
               if ((tile[n].flags & TILEF_MONASTERY) && mty==1 && mtx==1)
                  sprite = attrib_dat[4].dat;
               else if (feature_type[tile[n].minitile[mty][mtx].structnum] == FEATURE_CITY)
                  sprite = attrib_dat[5].dat;
               else if (feature_type[tile[n].minitile[mty][mtx].structnum] == FEATURE_ROAD)
                  sprite = attrib_dat[2].dat;
               else //if (feature_type[tile[n].minitile[mty][mtx].structnum] == FEATURE_FARM)
                  sprite = attrib_dat[3].dat;
               draw_rle_sprite(dest, sprite, dest_x+mtx*TILE_SIZE/3,
                                             dest_y+(mty+1)*TILE_SIZE/3 - sprite->h);
            }
         }
      }
   }
}

void draw_tilemap(BITMAP *dest, int float_tile, int dest_x, int dest_y, int tx, int ty, int w, int h)
{
   int x, y;
   int n;

   for (y=0; y<h; y++) {
      for (x=0; x<w; x++) {
         blit(emptytile, dest, 0,0, dest_x+x*TILE_SIZE, dest_y+y*TILE_SIZE, TILE_SIZE, TILE_SIZE);
      }
   }
   
   for (n=0; n<tiles_in_play; n++) {
      x = tile[playboard[n]].x - tx;
      y = tile[playboard[n]].y - ty;
      if (in_rect(x,y, 0,0, w,h)) {
         blit(tile[playboard[n]].gfx, dest, 0,0, dest_x+x*TILE_SIZE, dest_y+y*TILE_SIZE, TILE_SIZE, TILE_SIZE);
         draw_trans_sprite(dest, grid, dest_x+x*TILE_SIZE, dest_y+y*TILE_SIZE);
         draw_men(dest, playboard[n], dest_x+x*TILE_SIZE, dest_y+y*TILE_SIZE);

/*            
      {
         int tx, ty;

         for(tx=0; tx<3; tx++) {
            for (ty=0; ty<3; ty++) {
               textprintf_centre_ex(dest, get_menu_bold_font(), dest_x+x*TILE_SIZE+8+tx*TILE_SIZE/3, dest_y+y*TILE_SIZE+ty*TILE_SIZE/3, blue, -1, "%x", tile[playboard[n]].minitile[ty][tx].structnum);
               //textprintf_centre_ex(dest, get_menu_bold_font(), dest_x+x*TILE_SIZE+8+tx*TILE_SIZE/3, dest_y+y*TILE_SIZE+ty*TILE_SIZE/3, black, -1, "%x", tile[playboard[n]].minitile[ty][tx].connections);
               //textprintf_centre_ex(dest, get_menu_bold_font(), dest_x+x*TILE_SIZE+8+tx*TILE_SIZE/3, dest_y+y*TILE_SIZE+ty*TILE_SIZE/3, white, -1, "%x", (tile[playboard[n]].minitile[ty][tx].connections&0xf0)>>4);
               //textprintf_centre_ex(dest, get_menu_bold_font(), dest_x+x*TILE_SIZE+8+tx*TILE_SIZE/3, dest_y+y*TILE_SIZE+ty*TILE_SIZE/3, black, -1, "%d", feature_bonus[tile[playboard[n]].minitile[ty][tx].structnum].goods[1]);
               //textprintf_centre_ex(dest, get_menu_bold_font(), dest_x+x*TILE_SIZE+8+tx*TILE_SIZE/3, dest_y+y*TILE_SIZE+ty*TILE_SIZE/3, black, -1, "%d", feature_type[tile[playboard[n]].minitile[ty][tx].structnum]);
            }
         }
      }
//            */
      }
   }

   for (n=0; n<num_open_tiles; n++) {
      x = open_tiles[n].x - tx;
      y = open_tiles[n].y - ty;
      if (in_rect(x,y, 0,0, w,h)) {
         if (tile_fits(float_tile, tx+x, ty+y))
            draw_lit_sprite(dest, emptytile, dest_x+x*TILE_SIZE, dest_y+y*TILE_SIZE, 128);
      }
   }
}
