/********************************************************************** 
 Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
***********************************************************************/
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/time.h>

#include <map.h>
#include <game.h>
#include <shared.h>

#include <aitools.h>
void make_huts(int number);
void mapgenerator1(void);
void mapgenerator2(void);
void spread(int spr);
void smooth_map();
void add_height(int x,int y, int radius);
void adjust_map(int minval);
void init_workmap(void);
int *height_map;

int maxval=0;
int forests=0;

/**************************************************************************
 Just a wrapper function off the height_map, returns the height at x,y
**************************************************************************/

int full_map(int x, int y)
{
  return height_map[y*map.xsize+x];
}

/**************************************************************************
 is this y coordinate in the arctic zone?
**************************************************************************/
int arctic_y(int y)
{
  return (y < map.ysize*0.1 || y > map.ysize*0.9);
}

/**************************************************************************
 Is this y coordinate in the desert zone?
**************************************************************************/
int desert_y(int y)
{
  return (y > map.ysize*0.4  &&  y < map.ysize*0.6);
}

/**************************************************************************
  make_mountains will convert all squares that is heigher than thill to
  mountains and hills, notice that thill will be adjusted according to
  the map.mountains value, so increase map.mountains and you'll get more 
  hills and mountains, and vice versa  
**************************************************************************/

void make_mountains(int thill)
{
  int x,y;
  int mount;
  int j;
  for (j=0;j<10;j++) {
    mount=0;
    for (y=0;y<map.ysize;y++) 
      for (x=0;x<map.xsize;x++) 
	if (full_map(x, y)>thill) 
	    mount++;
    if (mount<((map.xsize*map.ysize)*map.mountains)/1000) 
      thill*=0.95;
    else 
      thill*=1.05;
  }
  
  for (y=0;y<map.ysize;y++) 
    for (x=0;x<map.xsize;x++) 
      if (full_map(x, y)>thill &&map_get_terrain(x,y)!=T_OCEAN) { 
	if (myrand(100)>75) 
	  map_set_terrain(x, y, T_MOUNTAINS);
	else if (myrand(100)>25) 
	  map_set_terrain(x, y, T_HILLS);
      }
}

/**************************************************************************
 add arctic and tundra squares in the arctic zone. 
 (that is the top 10%, and low 10% of the map)
**************************************************************************/
void make_polar()
{
  int y,x;

  for (y=0;y<map.ysize*0.1;y++) {
    for (x=0;x<map.xsize;x++) {
      if ((full_map(x, y)+(map.ysize*0.1-y*25)>myrand(maxval) && map_get_terrain(x,y)==T_GRASSLAND) || y==0) { 
	if (y<2)
	  map_set_terrain(x, y, T_ARCTIC);
	else
	  map_set_terrain(x, y, T_TUNDRA);
	  
      } 
    }
  }
  for (y=map.ysize*0.9;y<map.ysize;y++) {
    for (x=0;x<map.xsize;x++) {
      if ((full_map(x, y)+(map.ysize*0.1-(map.ysize-y)*25)>myrand(maxval) && map_get_terrain(x, y)==T_GRASSLAND) || y==map.ysize-1) {
	if (y>map.ysize-3)
	  map_set_terrain(x, y, T_ARCTIC);
	else
	  map_set_terrain(x, y, T_TUNDRA);
      }
    }
  }
}

/**************************************************************************
  recursively generate deserts, i use the heights of the map, to make the
  desert unregulary shaped, diff is the recursion stopper, and will be reduced
  more if desert wants to grow in the y direction, so we end up with 
  "wide" deserts. 
**************************************************************************/
void make_desert(int x, int y, int height, int diff) 
{
  if (abs(full_map(x, y)-height)<diff && map_get_terrain(x, y)==T_GRASSLAND) {
    map_set_terrain(x, y, T_DESERT);
    make_desert(x-1,y, height, diff-1);
    make_desert(x,y-1, height, diff-3);
    make_desert(x+1,y, height, diff-1);
    make_desert(x,y+1, height, diff-3);
  }
}

/**************************************************************************
  a recursive function that adds forest to the current location and try
  to spread out to the neighbours, it's called from make_forests until
  enough forest has been planted. diff is again the block function.
  if we're close to equator it will with 50% chance generate jungle instead
**************************************************************************/
void make_forest(int x, int y, int height, int diff)
{
  if (x==0 || x==map.xsize-1 ||y==0 || y==map.ysize-1)
    return;

  if (map_get_terrain(x, y)==T_GRASSLAND) {
    if (y>map.ysize*0.42 && y<map.ysize*0.58 && myrand(100)>50)
      map_set_terrain(x, y, T_JUNGLE);
    else 
      map_set_terrain(x, y, T_FOREST);
      if (abs(full_map(x, y)-height)<diff) {
	if (myrand(10)>5) make_forest(x-1,y, height, diff-5);
	if (myrand(10)>5) make_forest(x,y-1, height, diff-5);
	if (myrand(10)>5) make_forest(x+1,y, height, diff-5);
	if (myrand(10)>5) make_forest(x,y+1, height, diff-5);
      }
    forests++;
  }
}

/**************************************************************************
  makeforest calls make_forest with random grassland locations until there
  has been made enough forests. (the map.forestsize value controls this) 
**************************************************************************/
void make_forests()
{
  int x,y;
  int forestsize=25;
  forestsize=(map.xsize*map.ysize*map.forestsize)/1000;
   do {
    x=myrand(map.xsize);
    y=myrand(map.ysize);
    if (map_get_terrain(x, y)==T_GRASSLAND) {
      make_forest(x,y, full_map(x, y), 25);
    }
    if (myrand(100)>75) {
      y=(myrand((int)(map.ysize*0.2)))+map.ysize*0.4;
      x=myrand(map.xsize);
      if (map_get_terrain(x, y)==T_GRASSLAND) {
	make_forest(x,y, full_map(x, y), 25);
      }
    }
  } while (forests<forestsize);
}

/**************************************************************************
  swamps, is placed on low lying locations, that will typically be close to
  the shoreline. They're put at random (where there is grassland)
  and with 50% chance each of it's neighbour squares will be converted to
  swamp aswell
**************************************************************************/
void make_swamps()
{
  int x,y,i;
  int forever=0;
  for (i=0;i<map.swampsize;) {
    forever++;
    if (forever>1000) return;
    y=myrand(map.ysize);
    x=myrand(map.xsize);
    if (map_get_terrain(x, y)==T_GRASSLAND && full_map(x, y)<(maxval*60)/100) {
      map_set_terrain(x, y, T_SWAMP);
      if (myrand(10)>5 && map_get_terrain(x-1, y)!=T_OCEAN) 
	map_set_terrain(x-1,y, T_SWAMP);
      if (myrand(10)>5 && map_get_terrain(x+1, y)!=T_OCEAN) 
	map_set_terrain(x+1,y, T_SWAMP);
      if (myrand(10)>5 && map_get_terrain(x, y-1)!=T_OCEAN) 
	map_set_terrain(x,y-1, T_SWAMP);
      if (myrand(10)>5 && map_get_terrain(x, y+1)!=T_OCEAN) 
	map_set_terrain(x, y+1, T_SWAMP);
      i++;
    }
  }
}

/*************************************************************************
  make_deserts calls make_desert until we have enough deserts actually
  there is no map setting for how many we want, what happends is that
  we choose a random coordinate in the equator zone and if it's a grassland
  square we call make_desert with this coordinate, we try this 1000 times
**************************************************************************/
void make_deserts()
{
  int x,y,i,j;
  i=map.deserts;
  j=0;
  while (i && j<1000) {
    j++;
    y=myrand((int)(map.ysize*0.1))+map.ysize*0.45;
    x=myrand(map.xsize);
    if (map_get_terrain(x, y)==T_GRASSLAND) {
      make_desert(x,y, full_map(x, y), 50);
      i--;
    }
  }
}
/*************************************************************************
 this recursive function try to make some decent rivers, that run towards
 the ocean, it does this by following a descending path on the map spiced
 with a bit of chance, if it fails to reach the ocean it rolls back.
**************************************************************************/

int make_river(int x,int y) 
{
  int mini=10000;
  int mp;
  int res=0;
  mp=-1;
  if (x==0 || x==map.xsize-1 ||y==0 || y==map.ysize-1)
    return 0;
  if (map_get_terrain(x, y)==T_OCEAN)
    return 1;
  if (is_at_coast(x, y)) {
    map_set_terrain(x, y, T_RIVER);
    return 1;
  }

  if (map_get_terrain(x, y)==T_RIVER )
    return 0;
  map_set_terrain(x, y, map_get_terrain(x,y)+16);
  if (full_map(x, y-1)<mini+myrand(10) && map_get_terrain(x, y)<16) {
    mini=full_map(x, y-1);
    mp=0;
  }
  
  if (full_map(x, y+1)<mini+myrand(11) && map_get_terrain(x, y+1)<16) {
    mini=full_map(x, y+1);
    mp=1;
  }
  if (full_map(x+1, y)<mini+myrand(12) && map_get_terrain(x+1, y)<16) {
    mini=full_map(x+1, y);
    mp=2;
  }
  if (full_map(x-1, y)<mini+myrand(13) && map_get_terrain(x-1, y)<16) {
    mp=3;
  }
  if (mp==-1) {
    map_set_terrain(x, y, map_get_terrain(x, y)-16);
    return 0;
  }
  switch(mp) {
   case 0:
    res=make_river(x,y-1);
    break;
   case 1:
    res=make_river(x,y+1);
    break;
   case 2:
    res=make_river(x+1,y);
    break;
   case 3:
    res=make_river(x-1,y);
    break;
  }
  
  if (res) {
    map_set_terrain(x, y, T_RIVER);
  }
  else
    map_set_terrain(x, y, map_get_terrain(x ,y) - 16);
  return res;
}

/**************************************************************************
  calls make_river until we have enough river tiles (map.riverlength), 
  to stop this potentially never ending loop a miss counts as a river of 
  length one 
**************************************************************************/
void make_rivers()
{
  int x,y,i;
  i=0;

  while (i<map.riverlength) {
    y=myrand(map.ysize);
    x=myrand(map.xsize);
    if (map_get_terrain(x, y)==T_OCEAN ||map_get_terrain(x, y)==T_RIVER)
      continue;
    i+=make_river(x,y);
    i+=1;
  }
}

/**************************************************************************
  make_plains converts 50% of the remaining grassland to plains, this should
  maybe be lowered to 30% or done in batches, like the swamps?
**************************************************************************/
void make_plains()
{
  int x,y;
  for (y=0;y<map.ysize;y++)
    for (x=0;x<map.xsize;x++)
      if (map_get_terrain(x, y)==T_GRASSLAND && myrand(100)>50)
	map_set_terrain(x, y, T_PLAINS);
}

/**************************************************************************
  we want the map to be sailable east-west at least at north and south pole 
  and make it a bit jagged at the edge as well.
  So this procedure converts the second line and the second last line to
  ocean, and 50% of the 3rd and 3rd last line to ocean. 
**************************************************************************/
void make_passable()
{
  int x;
  
  for (x=0;x<map.xsize;x++) {
    map_set_terrain(x, 2, T_OCEAN);
    if (myrand(100)>50) map_set_terrain(x,1,T_OCEAN);
    if (myrand(100)>50) map_set_terrain(x,3,T_OCEAN);
    map_set_terrain(x, map.ysize-3, T_OCEAN);
    if (myrand(100)>50) map_set_terrain(x,map.ysize-2,T_OCEAN);
    if (myrand(100)>50) map_set_terrain(x,map.ysize-4,T_OCEAN);
  } 
  
}

/**************************************************************************
  we don't want huge areas of grass/plains, 
 so we put in a hill here and there, where it gets too 'clean' 
**************************************************************************/

void make_fair()
{
  int x,y;
  for (y=2;y<map.ysize-3;y++) 
    for (x=0;x<map.xsize;x++) {
      if (terrain_is_clean(x,y)) {
	map_set_terrain(x,y, T_HILLS);
	if (myrand(100)>66 && map_get_terrain(x-1, y)!=T_OCEAN)
	  map_set_terrain(x-1,y, T_HILLS);
	if (myrand(100)>66 && map_get_terrain(x+1, y)!=T_OCEAN)
	  map_set_terrain(x+1,y, T_HILLS);
	if (myrand(100)>66 && map_get_terrain(x, y-1)!=T_OCEAN) 
	  map_set_terrain(x,y-1, T_HILLS);
	if (myrand(100)>66 && map_get_terrain(x, y+1)!=T_OCEAN) 
	  map_set_terrain(x,y+1, T_HILLS);
      }
    }
}

/**************************************************************************
  make land simply does it all based on a generated heightmap
  1) with map.landpercent it generates a ocean/grassland map 
  2) it then calls the above functions to generate the different terrains
**************************************************************************/
void make_land()
{
  int x, y;
  int tres=(maxval*map.landpercent)/100;
  int count=0;
  int total=(map.xsize*map.ysize*map.landpercent)/100;
  int forever=0;
  do {
    forever++;
    if (forever>50) break; /* loop elimination */
    count=0;
    for (y=0;y<map.ysize;y++)
      for (x=0;x<map.xsize;x++) {
	if (full_map(x, y)<tres)
	  map_set_terrain(x, y, T_OCEAN);
	else {
	  map_set_terrain(x, y, T_GRASSLAND);
	  count++;
	}
      }
    if (count>total)
      tres=tres*1.1;
    else
      tres=tres*0.9;
  } while (abs(total-count)> maxval/40);
  make_passable();
  make_mountains(maxval*0.8);
  make_forests();
  make_swamps();
  make_deserts();
  make_rivers(); 
  make_plains();
  make_polar();
  make_fair();
}

/**************************************************************************
 returns if this is a 1x1 island (which we remove)
**************************************************************************/
int tiny_island(int x, int y) 
{
  if (map_get_terrain(x,y)==T_OCEAN) return 0;
  if (map_get_terrain(x-1,y)!=T_OCEAN) return 0;
  if (map_get_terrain(x+1,y)!=T_OCEAN) return 0;
  if (map_get_terrain(x,y-1)!=T_OCEAN) return 0;
  if (map_get_terrain(x,y+1)!=T_OCEAN) return 0;
  return 1;
}

/**************************************************************************
  removes all 1x1 islands
**************************************************************************/
void filter_land()
{
  int x,y;
  
  for (y=0;y<map.ysize;y++)
    for (x=0;x<map.xsize;x++) {
      /* Remove Islands that is only 1x1 */
      if (tiny_island(x,y)) {
	map_set_terrain(x,y, T_OCEAN);
      }
    }
}

/**************************************************************************
 a basic floodfill used to give every square a continent number, so we later
 on can ask which continent a given square belongs to.
**************************************************************************/
void flood_fill(int x, int y, int nr)
{
  if (x==-1) x=map.xsize-1;
  if (x==map.xsize) x=0;
  if (y==-1) return;
  if (y==map.ysize) return;
  if (map_get_continent(x, y)) 
    return;

  if (map_get_terrain(x, y)==T_OCEAN) return;
  islands[nr].goodies+=is_good_tile(x, y);
  map_set_continent(x, y, nr);
  flood_fill(x-1,y,nr);
  flood_fill(x-1,y+1,nr);
  flood_fill(x-1,y-1,nr);
  flood_fill(x,y-1,nr);
  flood_fill(x,y+1,nr);
  flood_fill(x+1,y-1,nr);
  flood_fill(x+1,y,nr);
  flood_fill(x+1,y+1,nr);
}

/**************************************************************************
 find start points for continents and call flood_fill for all of them 
**************************************************************************/
void flood_it(int loaded)
{
  int x,y;
  int good;
  int isles=3;

  for (y=0;y<map.ysize;y++)
    for (x=0;x<map.xsize;x++)
      map_set_continent(x, y, 0);
  flood_fill(0,0,1);                   /* want to know where the rim is */
  flood_fill(0,map.ysize-1,2);         /* ... */
  for (y=0;y<map.ysize;y++)
    for (x=0;x<map.xsize;x++) 
      if (!map_get_continent(x, y) && map_get_terrain(x, y)!=T_OCEAN) {
	islands[isles].goodies=0;
	flood_fill(x,y,isles);
	islands[isles].starters=0;
	islands[isles].x=x;
	islands[isles].y=y;
	isles++;
      }
 
/* fnord -- Syela */

  if (loaded)                 /* only make the continents if loaded game*/
    return;

  good=0;
  for (x=0;x<isles;x++) {
    if (islands[x].goodies>(60-game.nplayers*3)) 
      good+=islands[x].goodies;
  }
  for (x=0;x<isles;x++) {
    if (islands[x].goodies>(60-game.nplayers*3)) {
      islands[x].starters=1+islands[x].goodies/(good/game.nplayers);
    } 
  }
}
/**************************************************************************
  where do the different races start on the map? well this function tries
  to spread them out on the different islands.
**************************************************************************/

void choose_start_positions(void)
{
  int nr=0;
  int dist=40;
  int x, y, j=0;

  while (nr<game.nplayers) {
    x=myrand(map.xsize);
    y=myrand(map.ysize);
    if (islands[(int)map_get_continent(x, y)].starters) {
      j++;
      if (!is_starter_close(x, y, nr, dist)) {
	islands[(int)map_get_continent(x, y)].starters--;
	map.start_positions[nr].x=x;
	map.start_positions[nr].y=y;
	nr++;
      }
      if (j>500) {
	  dist--;
	j=0;
      } 
    }
  } 
}

/**************************************************************************
  we have 2 ways of generation a map, 
  1) generates normal maps like in civ
  2) generates 7 equally sized and with equally number of specials islands
**************************************************************************/
void map_fractal_generate(void)
{
  if (map.seed==0)
    mysrand(time(NULL));
  else 
    mysrand(map.seed);
  
  init_workmap();

  if (map.generator == 1 || (map.xsize < 80 || map.ysize < 50)) {
    mapgenerator1();
  } else {
    mapgenerator2();
  }


  filter_land();
  add_specials(map.riches); /* hvor mange promiller specials oensker vi*/
  
  /* print_map(); */
  make_huts(map.huts);
}

/**************************************************************************
  this next block is only for debug purposes and could be removed
**************************************************************************/
char terrai_chars[] = {'a','D', 'F', 'g', 'h', 'j', 'M', '*', 'p', 'R', '%','T', 'U' };

void print_map(void)
{
  int x,y;
  for (y=0;y<map.ysize;y++) {
    for (x=0;x<map.xsize;x++) {
      putchar(terrai_chars[map_get_terrain(x, y)]);
    }
    puts("");
  }
}

void print_hmap(void)
{
  int x,y;
  for (y=0;y<20;y++) {
    for (x=0;x<20;x++) {
      putchar(terrai_chars[height_map[y*20+x]]);
    }
    puts("");
  }
}

/**************************************************************************
  since the generated map will always have a positive number as minimum height
  i reduce the height so the lowest height is zero, this makes calculations
  easier
**************************************************************************/
void adjust_map(int minval)
{
  int x,y;
  for (y=0;y<map.ysize;y++) {
    for (x=0;x<map.xsize;x++) {
      height_map[y*map.xsize+x]-=minval;
    }
  }
}

/**************************************************************************
  this function will create a map.
**************************************************************************/
void init_workmap(void)
{
  int x,y;
  if(!(map.tiles=(struct tile*)malloc(map.xsize*map.ysize*
				      sizeof(struct tile)))) {
    fprintf(stderr, "malloc failed in mapgen\n");
    exit(1);
  }
  for(y=0;y<map.ysize;y++)
     for(x=0;x<map.xsize;x++)
       tile_init(map_get_tile(x, y));
}

/*****************************************************************************
  The rest of this file is only used in  mapgenerator 2, and should not be 
  confused with the previous stuff.
*****************************************************************************/
int ymax = 20;
int xmax = 20;
int size = 100;
int xs; 
int ys;
int dx;
int dy;

/**************************************************************************
  a wrapper
**************************************************************************/
int mini_map(int x, int y)
{
  return height_map[y*xmax + x];
}

/**************************************************************************
  fill an island with different types of terrain,
**************************************************************************/
void fillisland(int xp, int yp)
{
  int mountains = 3;
  int hills = 7;
  int forest = 10;
  int mixed;
  int x,y;
  int dir;
  mixed = 5 + size;
  if (mixed < 0) 
    mixed = 0;
  x = xp + xmax / 2;
  y = yp + ymax / 2;

  dir = myrand(4);
  map_set_terrain(x,y, T_RIVER);
  while (!is_terrain_near_tile(x, y, T_OCEAN)) {
    switch (dir) {
    case 0:
      x--;
      break;
    case 1:
      y--;
      break;
    case 2:
      x++;
      break;
    case 3:
      y++;
      break;
    }
      
    map_set_terrain(x,y, T_RIVER);
    if (is_terrain_near_tile(x, y, T_OCEAN)) 
      break;

    switch ((dir + myrand(3) - 1 + 4)%4) {
    case 0:
      x--;
      break;
    case 1:
      y--;
      break;
    case 2:
      x++;
      break;
    case 3:
      y++;
    }
    
    map_set_terrain(x,y, T_RIVER);
  }


  while (mountains) {
    x = myrand(xmax) + xp;
    y = myrand(ymax) + yp;
    if (map_get_terrain(x,y) == T_GRASSLAND && !is_terrain_near_tile(x, y, T_OCEAN)) {
      map_set_terrain(x, y, T_MOUNTAINS);
      mountains--;
    }    
  }
  while (hills) {
    x = myrand(xmax) + xp;
    y = myrand(ymax) + yp;
    if (map_get_terrain(x,y) == T_GRASSLAND && !is_terrain_near_tile(x, y, T_OCEAN)) {
      map_set_terrain(x, y, T_HILLS);
      hills--;
    }    
  }
  while (forest >0) {
    x = myrand(xmax-1) + xp;
    y = myrand(ymax-1) + yp;
    if (map_get_terrain(x,y) == T_GRASSLAND) {
      map_set_terrain(x, y, T_FOREST);
      forest--;
      if (map_get_terrain(x+1,y) == T_GRASSLAND && myrand(3)) {
	map_set_terrain(x+1, y, T_FOREST);
	forest--;
      }
      if (map_get_terrain(x,y+1) == T_GRASSLAND && myrand(3)) {
	map_set_terrain(x, y+1, T_FOREST);
	forest--;
      }
      if (map_get_terrain(x+1,y+1) == T_GRASSLAND && myrand(3)) {
	map_set_terrain(x+1, y+1, T_FOREST);
	forest--;
      }
    }    
  }
  while (mixed > 0) {
    x = myrand(xmax) + xp;
    y = myrand(ymax) + yp;
    if (map_get_terrain(x,y) == T_GRASSLAND) {
      if (arctic_y(y)) {
	map_set_terrain(x, y, T_TUNDRA);
	mixed--;
      } else if (desert_y(y) && is_terrain_near_tile(x, y, T_OCEAN)) {
	map_set_terrain(x, y, T_DESERT);
	mixed--;
	if (map_get_terrain(x+1,y) == T_GRASSLAND && myrand(3)) {
	  map_set_terrain(x, y, T_DESERT);
	  mixed--;
	}
	if (map_get_terrain(x,y+1) == T_GRASSLAND && myrand(3)) {
	  map_set_terrain(x, y, T_DESERT);
	  mixed--;
	}
	if (map_get_terrain(x+1,y+1) == T_GRASSLAND && myrand(3)) {
	  map_set_terrain(x, y, T_DESERT);
	  mixed--;
	}
      } else {
	if (desert_y(y))
	  map_set_terrain(x, y, T_JUNGLE);
	else 
	  map_set_terrain(x, y, T_SWAMP);
	mixed--;
      }
    }
  }
  for (y = 0; y < ymax; y++)
    for (x = 0; x < ymax; x++) {
      if (map_get_terrain(xp +x, yp+y) == T_GRASSLAND && !myrand(3)) {
	map_set_terrain(xp+x, y+yp, T_PLAINS);
      }
    }
}

/**************************************************************************
  copy the newly created island to the real map, notice that T_OCEAN is
  used as mask so other island won't get destroyed.
**************************************************************************/
void placeisland(int xp, int yp)
{
  int x,y;
  for (y = 0 ; y < dy ; y++) 
    for (x = 0 ; x < dx ; x++) {
      if (mini_map(xs+x, y+ys) != T_OCEAN) 
	map_set_terrain(xp + x, yp + y, mini_map(xs+x, ys+y));
    }
}

/**************************************************************************
  is there a neighbour square in the 4 basic directions?
**************************************************************************/
int neighbour(int x, int y)
{
  if ( x == 0 || x == xmax - 1 || y==0 || y == ymax -1) return 0;
  return (mini_map(x+1, y) + mini_map(x-1, y) + mini_map(x, y+1) + mini_map(x, y-1));
}

/**************************************************************************
  is there a neighbour square in any of the 8 directions?
**************************************************************************/
int neighbour8(int x, int y)
{
  if ( x == 0 || x == xmax - 1 || y==0 || y == ymax -1) return 0;
  return(neighbour(x,y) + mini_map(x+1, y-1) + mini_map(x-1, y-1) + mini_map(x+1, y+1) + mini_map(x-1, y+1));
}

/**************************************************************************
  we need this to convert tbe 0 based ocean representation to 2 
**************************************************************************/
void floodocean(int x, int y)
{
  if (x < 0 || x == xmax) return;
  if (y < 0 || y == ymax) return;
  if (mini_map(x,y) == 0) {
    height_map[y*xmax + x] = 2;
    floodocean(x - 1, y);
    floodocean(x + 1, y);
    floodocean(x , y - 1);
    floodocean(x , y + 1);
  }
}

/**************************************************************************
  this procedure will generate 1 island
**************************************************************************/
void makeisland(void)
{
  int x,y;
  
  xs = xmax/2 -1;
  ys = ymax/2 -1;
  dx = 3;
  dy = 3;
  for (y = 0 ; y < ymax ; y++) {
    for (x = 0 ; x < xmax ; x++) {
      height_map[y * xmax + x]=0;
    }
  }
  height_map[(ymax/2)*xmax + xmax/2] = 1;
  while (size > 0) {
    x = myrand(dx) + xs;
    y = myrand(dy) + ys;
    if (mini_map(x, y) == 0 && neighbour(x,y)) {
      height_map[y*xmax + x] = 1;
      size--;
      if (x <= xs) {
	xs--;
	dx++;
      }
      if (x == xs+dx - 1) dx++;
      if (y <= ys) {
	ys--;
	dy++;
      }
      if (y == ys+dy - 1) dy++;
    }
  }
  for (y = 0 ; y < ymax - 1; y++) 
    for (x = 0; x < xmax - 1; x++) {
      if (neighbour8(x, y) == 1 && mini_map(x, y) == 1) {
	height_map[y*xmax + x] = 0;
	size--;
      }
    }
  floodocean(0,0);
  for (y = 0 ; y < ymax; y++) 
    for (x = 0; x < xmax; x++) {
      if (mini_map(x, y) == 0) {
	height_map[y*xmax + x] = 1;
	size++;
      }
      if (mini_map(x, y) == 1) 
	height_map[y*xmax + x] = T_GRASSLAND;
      else 
	height_map[y*xmax + x] = T_OCEAN;
    }
}

/**************************************************************************
  this procedure finds a place to drop the current island
**************************************************************************/
void findislandspot(int *xplace, int *yplace)
{
  int x, y;
  int rx, ry;
  int taken;
  while (1) {
    rx = myrand(map.xsize);
    ry = myrand(map.ysize-dy-6) + 3;
    taken = 0;
    for (y = 0; y < dy && !taken; y++)
      for (x = 0; x < dx && !taken; x++) {
	  if (map_get_terrain(rx + x, ry + y) != T_OCEAN) {
	    taken = 1;
	  }
	}
    if (!taken) {
      *xplace = rx;
      *yplace = ry;
      return;
    }
  }
}

/**************************************************************************
  this is mapgenerators2. The highlevel routine that ties the knots together
**************************************************************************/

void mapgenerator2(void)
{
  int i;
  int xp,yp;
  int x,y;
  height_map =(int *) malloc (sizeof(int)*xmax*ymax);
  for (y = 0 ; y < map.ysize ; y++) 
    for (x = 0 ; x < map.xsize ; x++) {
      map_set_terrain(x,y, T_OCEAN);
    }
  for (x = 0 ; x < map.xsize; x++) {
    map_set_terrain(x,0, T_ARCTIC);
    if (myrand(100)>50) 
      map_set_terrain(x,1, T_ARCTIC);
    map_set_terrain(x, map.ysize-1, T_ARCTIC);
    if (myrand(100)>50) 
      map_set_terrain(x,map.ysize-2, T_ARCTIC);
  }
    for (i=0;i<7;i++) {
      size = 100;
      makeisland();
      findislandspot(&xp, &yp);
      placeisland(xp, yp);
      fillisland(xp, yp);
    }

  
    /*  print_map();*/
  free(height_map);
}

/**************************************************************************
  mapgenerator1, highlevel function, that calls all the previous functions
**************************************************************************/
void mapgenerator1(void)
{
  int x,y, i;
  int minval=5000000;
  height_map=(int *) malloc (sizeof(int)*map.xsize*map.ysize);

  
  for (y=0;y<map.ysize;y++) {
    for (x=0;x<map.xsize;x++) {
      height_map[y*map.xsize+x]=myrand(40)+((500-abs(map.ysize/2)-y)/10);
    }
  }
  for (i=0;i<1500;i++) {
    height_map[myrand(map.ysize*map.xsize)]+=myrand(5000);
    if (!(i%100)) {
      smooth_map(); 
    }
  }

  smooth_map(); 
  smooth_map(); 
  smooth_map(); 

  for (y=0;y<map.ysize;y++)
    for (x=0;x<map.xsize;x++) {
      if (full_map(x, y)>maxval) 
	maxval=full_map(x, y);
      if (full_map(x, y)<minval)
	minval=full_map(x, y);
    }
  maxval-=minval;
  adjust_map(minval);

  make_land();
  free(height_map);
}

/**************************************************************************
  smooth_map should be viewed  as a  corrosion function on the map, it levels
  out the differences in the heightmap.
**************************************************************************/

void smooth_map()
{
  int x,y;
  int mx,my,px,py;
  int a;
  
  for (y=0;y<map.ysize;y++) {
    my=map_adjust_y(y-1);
    py=map_adjust_y(y+1);
    for (x=0;x<map.xsize;x++) {
      mx=map_adjust_x(x-1);
      px=map_adjust_x(x+1);
      a=full_map(x, y)*2;
      
      a+=full_map(px, my);
      a+=full_map(mx, my);
      a+=full_map(mx, py);
      a+=full_map(px, py);

      a+=full_map(x, my);
      a+=full_map(mx, y);
      
      a+=full_map(x, py);
      a+=full_map(px, y);

      a+=myrand(60);
      a-=30;
      if (a<0) a=0;
      height_map[y*map.xsize +x]=a/10;
    }
  }
}

/**************************************************************************
  this function spreads out huts on the map, a position can be used for a
  hut if there isn't another hut close and if it's not on the ocean.
**************************************************************************/
void make_huts(int number)
{
  int x,y;
  int count=0;
  while ((number*map.xsize*map.ysize)/2000 && count++<map.xsize*map.ysize*2) {
    x=myrand(map.xsize);
    y=myrand(map.ysize);
    if (map_get_terrain(x, y)!=T_OCEAN) {
      if (!is_hut_close(x,y)) {
	number--;
	map_get_tile(x,y)->special|=S_HUT;
      }
    }
  }
}
