Complete.Org: Mailing Lists: Archives: freeciv-dev: September 2003:
[Freeciv-Dev] Re: (PR#3489) Patch to add new generator
Home

[Freeciv-Dev] Re: (PR#3489) Patch to add new generator

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
To: undisclosed-recipients: ;
Subject: [Freeciv-Dev] Re: (PR#3489) Patch to add new generator
From: "jjc@xxxxxxxxxxxxxxxxxx" <jjc@xxxxxxxxxxxxxxxxxx>
Date: Thu, 18 Sep 2003 17:50:34 -0700
Reply-to: rt@xxxxxxxxxxxxxx

This patch is the same as before, only it will now complile with c89 compilers
and not just c99 (tested with -pedantic -std=c89 ).  Thanks go to
Reinier Post for pointing this out.

The 1.14.1-beta1 version is currently running on pubserver so you can test 
it there with a normal client if you wish.


-- 
Josh Cogliati


Index: common/map.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/map.h,v
retrieving revision 1.152
diff -U9 -r1.152 map.h
--- common/map.h        2003/09/11 11:30:42     1.152
+++ common/map.h        2003/09/18 13:02:17
@@ -671,19 +671,19 @@
 #define MAP_MIN_RIVERS           0
 #define MAP_MAX_RIVERS           100
 
 #define MAP_DEFAULT_FORESTS      20
 #define MAP_MIN_FORESTS          0
 #define MAP_MAX_FORESTS          100
 
 #define MAP_DEFAULT_GENERATOR    1
 #define MAP_MIN_GENERATOR        1
-#define MAP_MAX_GENERATOR        5
+#define MAP_MAX_GENERATOR        7
 
 #define MAP_DEFAULT_TINYISLES    FALSE
 #define MAP_MIN_TINYISLES        FALSE
 #define MAP_MAX_TINYISLES        TRUE
 
 #define MAP_DEFAULT_SEPARATE_POLES   TRUE
 #define MAP_MIN_SEPARATE_POLES       FALSE
 #define MAP_MAX_SEPARATE_POLES       TRUE
 
Index: server/mapgen.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/mapgen.c,v
retrieving revision 1.116
diff -U9 -r1.116 mapgen.c
--- server/mapgen.c     2003/09/11 12:09:46     1.116
+++ server/mapgen.c     2003/09/18 13:02:41
@@ -37,18 +37,19 @@
 #define rmap(x, y) (river_map[map_pos_to_index(x, y)])
 
 static void make_huts(int number);
 static void add_specials(int prob);
 static void mapgenerator1(void);
 static void mapgenerator2(void);
 static void mapgenerator3(void);
 static void mapgenerator4(void);
 static void mapgenerator5(void);
+static void mapgenerator67(bool make_roads);
 static void smooth_map(void);
 static void adjust_map(int minval);
 static void adjust_terrain_param(void);
 
 #define RIVERS_MAXTRIES 32767
 enum river_map_type {RS_BLOCKED = 0, RS_RIVER = 1};
 
 /* Array needed to mark tiles as blocked to prevent a river from
    falling into itself, and for storing rivers temporarly.
@@ -1160,18 +1161,22 @@
   mysrand(map.seed);
   
   /* don't generate tiles with mapgen==0 as we've loaded them from file */
   /* also, don't delete (the handcrafted!) tiny islands in a scenario */
   if (map.generator != 0) {
     map_allocate();
     adjust_terrain_param();
     /* if one mapgenerator fails, it will choose another mapgenerator */
     /* with a lower number to try again */
+    if (map.generator == 7 )
+      mapgenerator67(TRUE);
+    if (map.generator == 6 )
+      mapgenerator67(FALSE);
     if (map.generator == 5 )
       mapgenerator5();
     if (map.generator == 4 )
       mapgenerator4();
     if (map.generator == 3 )
       mapgenerator3();
     if( map.generator == 2 )
       mapgenerator2();
     if( map.generator == 1 )
@@ -2116,10 +2121,541 @@
     if (hmap(x, y) < minval)
       minval = hmap(x, y);
   } whole_map_iterate_end;
   maxval -= minval;
   adjust_map(minval);
   
   make_land();
   free(height_map);
   height_map = NULL;
+}
+
+/****************************************************************************
+Overview of how Generator 6/7 works:
+
+Basically, the goal of the below enumeration and much of the code is
+to make land that is reasonably interesting, but can be constrained to
+have certain aspects that are carefully chosen.
+
+T6_PERM_LAND is land that is forced to be land and will not be changed by 
+any transformations.
+T6_PERM_OCEAN is land that is forced to be ocean and will not be changed by 
+any transformations. 
+T6_TEMP_LAND is currently land, but may be changed by later transformations.
+T6_TEMP_OCEAN is currently ocean, but may be change by later transformations.
+
+The first step is to build up all the perm land and perm ocean.  This is 
+done in the functions create_peninsula and mapgenerator6.  These create the
+basic peninsula structure and create permanent ocean seperating each 
+peninsula.  mapgenerator6 also creates the polar land and the connecting 
+isthmus out of permanent land (note that none of the transforms touch 
+the polar land so it is actually made out of what it will finally be).
+
+The second step is to add in randomness.  Basically this step first of 
+all creates quite a few seed islands randomly about the map in places with
+T6_TEMP_OCEAN.  Then, it uses random_new_land to place new land around the 
+map.  random_new_land is biased to place new land next to old land.   
+
+The third step is to make the map's coastlines more smooth.  This is 
+accomplished by applying a dilate_map, two erode_map and another dilate_map.
+The dilate/erode is sometimes called an closing and the erode/dilate is
+sometimes called a opening.  
+
+The dilate basically increases every beach by one tile.  I.e. if 
+old land is # and new land is + then the effect will be to add the new land:
+   ++
+  +##+
+  +#+#+
+ +#####+
+  ++#++
+    +
+
+The erode basically decreases every beach by one tile.  I.e. The # land 
+will stay, but the - land will be 'eroded' away:
+
+   --
+  -##-
+  -###-
+ -#####-
+  --#--
+    -
+
+So a dilate followed by an erode will result in the change of eliminating 
+the center ocean.  In the map generator, the dilate/erode will remove small
+oceans and small bays, and the erode/dilate will remove small islands.
+
+The last step is to relace all temp land with T_GRASSLAND and all 
+temp ocean with T_OCEAN and then let the standard map creation functions 
+create mountains, deserts etc.
+
+****************************************************************************/
+enum gen6_terrain { T6_PERM_LAND = T_GRASSLAND, T6_TEMP_LAND = T_ARCTIC, 
+                   T6_PERM_OCEAN = T_OCEAN, T6_TEMP_OCEAN = T_SWAMP, 
+                   T6_ERODE_LAND = T_DESERT, T6_DILATE_LAND = T_JUNGLE};
+
+/****************************************************************************
+ returns true if the terrain is generator 6 ocean. 
+****************************************************************************/
+static int g6_ocean(int terrain) 
+{
+  return terrain == T6_PERM_OCEAN || terrain == T6_TEMP_OCEAN;
+}
+
+/****************************************************************************
+ returns true if the terrain at point x,y is land. 
+****************************************************************************/
+static int land_terrain(int x, int y)
+{
+  int terrain;
+  normalize_map_pos(&x, &y);
+  terrain = map_get_terrain(x, y);
+  return !g6_ocean(terrain);
+}
+
+/****************************************************************************
+ returns true if a neighboring point should be eroded. 
+****************************************************************************/
+static int erode_terrain(int x, int y)
+{
+  int terrain;
+  normalize_map_pos(&x, &y);
+  terrain = map_get_terrain(x, y);
+  return g6_ocean(terrain);
+}
+
+/****************************************************************************
+ returns true if the terrain at point x,y should be eroded.  
+****************************************************************************/
+static int should_erode(int x, int y)
+{
+  int terrain = map_get_terrain(x, y);
+  if(g6_ocean(terrain) || terrain == T6_PERM_LAND) {
+    return FALSE;
+  }
+  if (erode_terrain(x - 1, y) 
+      || erode_terrain(x + 1, y)
+      || erode_terrain(x, y - 1)
+      || erode_terrain(x, y + 1)) {
+    return TRUE;
+  } else {
+    return FALSE;
+  }
+}
+
+/****************************************************************************
+ removes all temporary shores.      (I.e. shrink the land by one tile.)
+****************************************************************************/
+static void erode_map(int polar_height) 
+{
+  int x, y;
+  for (x = 0; x < map.xsize; x++) {
+    for (y = polar_height; y < map.ysize - polar_height; y++) {
+      if (should_erode(x, y)) {
+       map_set_terrain(x, y, T6_ERODE_LAND);
+      }
+    }
+  }
+  
+  for (x = 0; x < map.xsize; x++) {
+    for (y = polar_height; y < map.ysize - polar_height; y++) {
+      if (map_get_terrain(x, y) == T6_ERODE_LAND) {
+       map_set_terrain(x, y, T6_TEMP_OCEAN);
+      }
+    }
+  }
+}
+
+/****************************************************************************
+ returns true if a neighboring point should be dilated. 
+****************************************************************************/
+static int dilate_terrain(int x, int y)
+{
+  int terrain;
+  normalize_map_pos(&x, &y);
+  terrain = map_get_terrain(x, y);
+  return !g6_ocean(terrain) && terrain != T6_DILATE_LAND;
+}
+
+/****************************************************************************
+ returns true if the terrain at point x,y should be dilated.  
+****************************************************************************/
+static int should_dilate(int x, int y)
+{
+  int terrain = map_get_terrain(x, y);
+  if(!g6_ocean(terrain) || terrain == T6_PERM_OCEAN) {
+    return FALSE;
+  }
+  if (dilate_terrain(x - 1, y) 
+      || dilate_terrain(x + 1, y)
+      || dilate_terrain(x, y - 1)
+      || dilate_terrain(x, y + 1)) {
+    return TRUE;
+  } else {
+    return FALSE;
+  }
+}
+
+/****************************************************************************
+ removes all temporary beaches.   (I.e. shrink the ocean by one tile.) 
+****************************************************************************/
+static void dilate_map(int polar_height) 
+{
+  int x, y;
+  for (x = 0; x < map.xsize; x++) {
+    for (y = polar_height; y < map.ysize - polar_height; y++) {
+      if (should_dilate(x, y)) {
+       map_set_terrain(x, y, T6_DILATE_LAND);
+      }
+    }
+  }
+  
+  for (x = 0; x < map.xsize; x++) {
+    for (y = polar_height; y < map.ysize - polar_height; y++) {
+      if (map_get_terrain(x, y) == T6_DILATE_LAND) {
+       map_set_terrain(x, y, T6_TEMP_LAND);
+      }
+    }
+  }
+}
+
+
+/****************************************************************************
+ returns a score based on the amount of land surrounding the point x,y. 
+ No land is 0.  All eight neighbors is 12
+ ***************************************************************************/
+static int border_score(int x, int y)
+{
+  int adj_score = 2;
+  int diag_score = 1;
+  int score = 0;
+  score += land_terrain(x - 1, y) ? adj_score : 0;
+  score += land_terrain(x + 1, y) ? adj_score : 0;
+  score += land_terrain(x, y - 1) ? adj_score : 0;
+  score += land_terrain(x, y + 1) ? adj_score : 0;
+  score += land_terrain(x - 1, y - 1) ? diag_score : 0;
+  score += land_terrain(x + 1, y - 1) ? diag_score : 0;
+  score += land_terrain(x - 1, y + 1) ? diag_score : 0;
+  score += land_terrain(x + 1, y + 1) ? diag_score : 0;
+  return score;
+}
+
+enum terrain_status { TS_OCEAN, TS_LAND, TS_NEW_LAND};
+
+/*************************************************************************
+  Returns TS_NEW_LAND if the point is not already land and 
+  the random number is less than the border score.
+  More likely to return TS_NEW_LAND on a location that has
+  more land around it.  Will never return TS_NEW_LAND on a
+  location that has no land in any of the eight neighbors.
+ *************************************************************************/
+static int random_new_land(int x, int y)
+{
+  int score;
+  int random;
+  if (land_terrain(x, y)) {
+    return TS_LAND;
+  }
+  if (map_get_terrain(x, y) == T6_PERM_OCEAN) {
+    return TS_OCEAN;
+  }
+  score = border_score(x, y);
+  random = myrand(4 * 2 + 4 * 1);
+  return random + 1 > score ? TS_OCEAN : TS_NEW_LAND;
+
+}
+
+/*************************************************************************
+ Creates a peninsula and put the player's starting position
+ on it. The direction is either +1 or -1 depending on which 
+ direction the peninsula should go from the y position.
+ The remaining_count is the number of tiles to  fill with 
+ land.
+ *************************************************************************/
+static void create_peninsula(int x, int y, int player_number,
+                            int width, int height, int direction,
+                            int neck_height,int neck_width,
+                            int neck_displacement)
+{
+  int cx, cy;
+  int head_height = height-neck_height;
+  int neck_start = x + neck_displacement;
+  int ocean_distance = neck_height / 2;
+
+  /* make perm ocean */
+  { 
+    int top_location = y - (ocean_distance + 1) * direction;
+    int bottom_location = y + (height - ocean_distance) * direction;
+    int left_location = x - ocean_distance - 1;
+    int right_location = x + width + ocean_distance;
+    
+    /* top and bottom */
+    for (cx = left_location; cx < right_location + 1; cx++) {
+      map_set_terrain(cx, top_location, T6_PERM_OCEAN);
+      map_set_terrain(cx, bottom_location, T6_PERM_OCEAN);
+    }
+
+    /* left and right */
+    for (cy = top_location; cy != bottom_location + direction;
+        cy += direction) {
+      map_set_terrain(left_location, cy, T6_PERM_OCEAN);
+      map_set_terrain(right_location, cy, T6_PERM_OCEAN);
+    }
+
+    /* connect to central ocean */
+    for (cy = map.ysize / 2; cy != top_location; cy += direction) {
+      map_set_terrain(x + width / 2, cy, T6_PERM_OCEAN);
+    }
+  }
+
+  /* make head */
+  for (cx = x; cx < x + width; cx++) {
+    for (cy = y; cy != y + (head_height + 1) * direction; cy += direction) {
+      map_set_terrain(cx, cy, T6_PERM_LAND);
+      hmap(cx, cy) = myrand(7000) - 2000;
+    }
+  }
+
+  /* make neck */
+  for (cx = neck_start; cx < neck_start + neck_width; cx++) {
+    for (cy = y + (head_height + 1) * direction; 
+        cy != y + (height + 1) * direction; cy += direction ) {
+      map_set_terrain(cx, cy, T6_PERM_LAND);
+      hmap(cx, cy) = myrand(7000) - 2000;
+    }
+  }
+
+  map.start_positions[player_number].x = x + width / 2;
+  map.start_positions[player_number].y = y;
+}
+
+/*************************************************************************
+  This generator creates a map with one penisula for each 
+   player and an isthmus between.  It creates a central
+   ocean and puts the peninsulas around the edges.  It is 
+   intented for quicker games. Should look something like this:
+   *****************************
+   ****  *****  *****  *****    
+    **    ***    ***    ***     
+    **                          
+    **                           
+    **       ***    ***             
+   ****     *****  *****           
+   *****************************
+
+  If make_roads is true, it will generate roads on the polar regions and 
+   on the isthmus.
+ *************************************************************************/
+static void mapgenerator67(bool make_roads)
+{
+  int peninsulas = game.nplayers;
+  int peninsulas_on_one_side = (peninsulas + 1) / 2;
+  int isthmus_width = 10;
+  int neck_height = (map.ysize / 8) | 1; 
+  int polar_height = 3;
+  int peninsula_separation = neck_height;
+  int max_peninsula_width = (map.xsize - isthmus_width - peninsula_separation)
+    / (peninsulas_on_one_side) - peninsula_separation;
+  int max_peninsula_height = map.ysize / 2 - polar_height - neck_height;
+  int neck_width = MIN(6,max_peninsula_width - 1);
+  int min_peninsula_width = neck_width;
+  int min_peninsula_height = neck_height;
+  int peninsula_area = MAX((max_peninsula_width * max_peninsula_height * 
+                           map.landpercent) / 100, 
+                          min_peninsula_width * min_peninsula_height);
+  int i, x, y;
+  int isle = 1;
+
+  if(min_peninsula_width > max_peninsula_width 
+     || min_peninsula_height > max_peninsula_height
+     || min_peninsula_width < 2 || min_peninsula_height < 2)
+  {
+    freelog(LOG_NORMAL, 
+           "mapgen.c: unable to use generator 6/7. "\
+           "mw %d Mw %d mh %d Mh %d area %d",
+           min_peninsula_width,max_peninsula_width,
+           min_peninsula_height,max_peninsula_height,peninsula_area);
+    map.generator = 5;
+    return;
+  }
+
+  height_map = fc_malloc(sizeof(int) * map.xsize * map.ysize);
+
+  map.start_positions = fc_realloc(map.start_positions,
+                                  game.nplayers
+                                  * sizeof(*map.start_positions));
+
+  /* initialize everything to temp ocean */
+  for (y = 0; y < map.ysize; y++)
+    for (x = 0; x < map.xsize; x++) {
+      map_set_terrain(x, y, T6_TEMP_OCEAN);
+      hmap(x, y) = 0;
+    }
+
+  /* create central perm ocean */
+  y = map.ysize / 2;
+  for (x = isthmus_width; x < map.xsize; x++) {
+    map_set_terrain(x, y, T6_PERM_OCEAN);
+    hmap(x, y) = 0;
+  }
+
+  /* create polar regions */
+  for (x = 0; x < map.xsize; x++) {
+    for (y = 0; y < polar_height; y++) {
+      int rand_num = myrand(9);
+      map_set_terrain(x, y, rand_num > 7 ? T_ARCTIC :
+                     (rand_num < 2 ? T_MOUNTAINS : T_TUNDRA));
+      rand_num = myrand(9);
+      map_set_terrain(x, map.ysize - 1 - y, rand_num > 7 ? T_ARCTIC :
+                     (rand_num < 2 ? T_MOUNTAINS : T_TUNDRA));
+    }
+  }
+
+  /* build polar regions road */
+  if (make_roads) {
+    for (x = 0; x < map.xsize; x++) {
+      y = polar_height - 1;
+      if (map_build_road_time(x, y - 1) < map_build_road_time(x, y)) {
+       map_set_special(x, y - 1, S_ROAD);
+      } else {
+       map_set_special(x, y, S_ROAD);
+      }
+      y = map.ysize - polar_height;
+      if (map_build_road_time(x, y + 1) < map_build_road_time(x, y)) {
+       map_set_special(x, y + 1, S_ROAD);
+      } else {
+       map_set_special(x, y, S_ROAD);
+      }
+    }
+  } /* make_roads */
+
+  map.num_continents = 1;
+
+  /* create isthmus centeral strip */
+  x = isthmus_width / 2;
+  for (y = polar_height; y < map.ysize - polar_height; y++) {
+    map_set_terrain(x, y, T6_PERM_LAND);
+    hmap(x, y) = 100 * x * (isthmus_width - x) + (myrand(400) - 200);
+  }
+
+  /* setup peninsulas */
+  for (i = 0; i < game.nplayers; i++) {
+    /* direction is the direction to increment from the x and y location */
+    int direction = (i < peninsulas_on_one_side) ? -1 : 1;
+    int index = (direction == -1) ? i : i - peninsulas_on_one_side;
+    int width = min_peninsula_width + 
+      myrand(max_peninsula_width - min_peninsula_width + 1);
+    int height = CLIP(min_peninsula_height,
+                     peninsula_area / width + neck_height,
+                     max_peninsula_height);
+    int neck_displacement = myrand(width - neck_width + 1);
+    int x_displacement = myrand(max_peninsula_width - width + 1);
+    if(index == 0) {
+      x = peninsula_separation + isthmus_width;
+      if(direction == 1 && game.nplayers & 1) {
+       /* center the thing */
+       x = x + max_peninsula_width / 2;
+      } 
+    } 
+    y = (direction == -1)
+       ? height + polar_height : map.ysize - 1 - height - polar_height;
+    create_peninsula(x + x_displacement, y, i, width, height, direction,
+                    neck_height,neck_width,neck_displacement);
+    x = x + peninsula_separation + max_peninsula_width;
+  }
+
+  map.num_start_positions = game.nplayers;
+
+  { 
+    int consider_height = map.ysize - 2 * polar_height;
+    int desired_squares = 
+      (consider_height * map.xsize * map.landpercent) / 200;
+    int bailout_number = desired_squares * 30;
+    int seed = MIN(20, desired_squares / 20); /* seed islands */
+    desired_squares = MAX(0, desired_squares - seed);
+    while (bailout_number > 0 && seed > 0) {
+      x = myrand(map.xsize);
+      y = myrand(consider_height) + polar_height;
+      if (map_get_terrain(x, y) == T6_TEMP_OCEAN) {
+       map_set_terrain(x, y, T6_TEMP_LAND);
+       hmap(x, y) = myrand(7000) - 2000;
+       seed--;
+      }
+      bailout_number--;
+    }
+    while (bailout_number > 0 && desired_squares > 0) {
+      x = myrand(map.xsize);
+      y = myrand(consider_height) + polar_height;
+      if (random_new_land(x, y) == TS_NEW_LAND) {
+       map_set_terrain(x, y, T6_TEMP_LAND);
+       hmap(x, y) = myrand(7000) - 2000;
+       desired_squares--;
+      }
+      bailout_number--;
+    }
+  }
+
+  /* remove small oceans  */
+  dilate_map(polar_height);
+  erode_map(polar_height);
+  /* remove small islands */
+  erode_map(polar_height);
+  dilate_map(polar_height);
+
+  /* translate to real terrain */
+  for (x = 0; x < map.xsize; x++) {
+    for (y = polar_height; y < map.ysize - polar_height; y++) {
+      int terrain = map_get_terrain(x, y);
+      if (terrain == T6_TEMP_LAND) {
+       map_set_terrain(x, y, T_GRASSLAND);
+      } else if(terrain == T6_TEMP_OCEAN) {
+       map_set_terrain(x, y, T_OCEAN);
+      }
+    }
+  }
+
+  whole_map_iterate(x, y) {
+    map_set_continent(x, y, 0);
+  } whole_map_iterate_end;
+  
+  assign_continent_flood(0, 0, isle++);
+
+  whole_map_iterate(x, y) {
+    if (map_get_continent(x, y) == 0 
+        && !is_ocean(map_get_terrain(x, y))) {
+      assign_continent_flood(x, y, isle++);
+    }
+  } whole_map_iterate_end;
+
+  map.num_continents = isle-1;
+
+  /* setup terrain */
+  smooth_map();
+  make_mountains(1600);
+  make_swamps();
+  make_forests();
+  make_deserts();
+  make_plains();
+  make_fair();
+  make_rivers();
+
+  /* create isthmus road */
+  if (make_roads) {
+    int last_x, middle_x = isthmus_width / 2;
+    last_x = middle_x;
+    for (y = polar_height - 1; y < map.ysize - polar_height + 1; y++) {
+      int best_x = middle_x;
+      int min_build = map_build_road_time(middle_x, y);
+      for (x = MAX(last_x - 1, middle_x - 1);
+          x != MIN(last_x + 1, middle_x + 1) + 1; x++) {
+       if (land_terrain(x, y) && map_build_road_time(x, y) < min_build) {
+         best_x = x;
+         min_build = map_build_road_time(x, y);
+       }
+      }
+      map_set_special(best_x, y, S_ROAD);
+      last_x = best_x;
+    }
+  }
+
+  free(height_map);
+
 }
Index: server/stdinhand.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/stdinhand.c,v
retrieving revision 1.293
diff -U9 -r1.293 stdinhand.c
--- server/stdinhand.c  2003/09/15 19:40:53     1.293
+++ server/stdinhand.c  2003/09/18 13:03:07
@@ -242,19 +242,23 @@
        "3 = equally sized large islands with one player each, and "
        "a number of other\n"
        "    islands of similar size;\n"
        "4 = equally sized large islands with two players on every "
        "island (or one\n"
        "    with three players for an odd number of players), and "
        "additional\n"
        "    smaller islands;\n"
        "5 = one or more large earthlike continents with some scatter.\n"
-       "Note: values 2,3 and 4 generate \"fairer\" (but more boring) "
+       "6 = equally sized peninsulas with one player each surrounding  \n"
+       "    an inland sea.  An isthmus connects the polar regions.\n"
+       "7 = same as 6 except roads are generated in the polar regions\n"
+       "    and on the connecting isthmus.\n"
+       "Note: values 2,3,4,6 and 7 generate \"fairer\" (but more boring) "
        "maps.\n"
        "(Zero indicates a scenario map.)"), NULL,
          MAP_MIN_GENERATOR, MAP_MAX_GENERATOR, MAP_DEFAULT_GENERATOR)
 
   GEN_BOOL("tinyisles", map.tinyisles, SSET_MAP_GEN, SSET_TO_CLIENT,
           N_("Presence or absence of 1x1 islands"),
           N_("0 = no 1x1 islands; 1 = some 1x1 islands"), NULL,
           MAP_DEFAULT_TINYISLES)
 
diff -Nurd -x'.when-*' -x'.newest-*' -Xdiff_ignore 
../freeciv.orig/./server/mapgen.c ./server/mapgen.c
--- ../freeciv.orig/./server/mapgen.c   Thu Sep 11 14:49:37 2003
+++ ./server/mapgen.c   Wed Sep 17 01:21:52 2003
@@ -41,6 +41,7 @@
 static void mapgenerator3(void);
 static void mapgenerator4(void);
 static void mapgenerator5(void);
+static void mapgenerator67(bool make_roads);
 static void smooth_map(void);
 static void adjust_map(int minval);
 
@@ -1196,6 +1197,10 @@
     map_allocate();
     /* if one mapgenerator fails, it will choose another mapgenerator */
     /* with a lower number to try again */
+    if (map.generator == 7 )
+      mapgenerator67(TRUE);
+    if (map.generator == 6 )
+      mapgenerator67(FALSE);
     if (map.generator == 5 )
       mapgenerator5();
     if (map.generator == 4 )
@@ -2212,3 +2217,536 @@
   free(height_map);
   height_map = NULL;
 }
+
+#define is_ocean(x) ((x) == T_OCEAN)
+
+/****************************************************************************
+Overview of how Generator 6/7 works:
+
+Basically, the goal of the below enumeration and much of the code is
+to make land that is reasonably interesting, but can be constrained to
+have certain aspects that are carefully chosen.
+
+T6_PERM_LAND is land that is forced to be land and will not be changed by 
+any transformations.
+T6_PERM_OCEAN is land that is forced to be ocean and will not be changed by 
+any transformations. 
+T6_TEMP_LAND is currently land, but may be changed by later transformations.
+T6_TEMP_OCEAN is currently ocean, but may be change by later transformations.
+
+The first step is to build up all the perm land and perm ocean.  This is 
+done in the functions create_peninsula and mapgenerator6.  These create the
+basic peninsula structure and create permanent ocean seperating each 
+peninsula.  mapgenerator6 also creates the polar land and the connecting 
+isthmus out of permanent land (note that none of the transforms touch 
+the polar land so it is actually made out of what it will finally be).
+
+The second step is to add in randomness.  Basically this step first of 
+all creates quite a few seed islands randomly about the map in places with
+T6_TEMP_OCEAN.  Then, it uses random_new_land to place new land around the 
+map.  random_new_land is biased to place new land next to old land.   
+
+The third step is to make the map's coastlines more smooth.  This is 
+accomplished by applying a dilate_map, two erode_map and another dilate_map.
+The dilate/erode is sometimes called an closing and the erode/dilate is
+sometimes called a opening.  
+
+The dilate basically increases every beach by one tile.  I.e. if 
+old land is # and new land is + then the effect will be to add the new land:
+   ++
+  +##+
+  +#+#+
+ +#####+
+  ++#++
+    +
+
+The erode basically decreases every beach by one tile.  I.e. The # land 
+will stay, but the - land will be 'eroded' away:
+
+   --
+  -##-
+  -###-
+ -#####-
+  --#--
+    -
+
+So a dilate followed by an erode will result in the change of eliminating 
+the center ocean.  In the map generator, the dilate/erode will remove small
+oceans and small bays, and the erode/dilate will remove small islands.
+
+The last step is to relace all temp land with T_GRASSLAND and all 
+temp ocean with T_OCEAN and then let the standard map creation functions 
+create mountains, deserts etc.
+
+****************************************************************************/
+enum gen6_terrain { T6_PERM_LAND = T_GRASSLAND, T6_TEMP_LAND = T_SWAMP, 
+                   T6_PERM_OCEAN = T_OCEAN, T6_TEMP_OCEAN = T_ARCTIC, 
+                   T6_ERODE_LAND = T_DESERT, T6_DILATE_LAND = T_JUNGLE};
+
+/****************************************************************************
+ returns true if the terrain is generator 6 ocean. 
+****************************************************************************/
+static int g6_ocean(int terrain) 
+{
+  return terrain == T6_PERM_OCEAN || terrain == T6_TEMP_OCEAN;
+}
+
+/****************************************************************************
+ returns true if the terrain at point x,y is land. 
+****************************************************************************/
+static int land_terrain(int x, int y)
+{
+  int terrain;
+  normalize_map_pos(&x, &y);
+  terrain = map_get_terrain(x, y);
+  return !g6_ocean(map_get_terrain(x, y));
+}
+
+/****************************************************************************
+ returns true if a neighboring point should be eroded. 
+****************************************************************************/
+static int erode_terrain(int x, int y)
+{
+  int terrain;
+  normalize_map_pos(&x, &y);
+  terrain = map_get_terrain(x, y);
+  return g6_ocean(terrain);
+}
+
+/****************************************************************************
+ returns true if the terrain at point x,y should be eroded.  
+****************************************************************************/
+static int should_erode(int x, int y)
+{
+  int terrain = map_get_terrain(x, y);
+  if(g6_ocean(terrain) || terrain == T6_PERM_LAND) {
+    return FALSE;
+  }
+  if (erode_terrain(x - 1, y) 
+      || erode_terrain(x + 1, y)
+      || erode_terrain(x, y - 1)
+      || erode_terrain(x, y + 1)) {
+    return TRUE;
+  } else {
+    return FALSE;
+  }
+}
+
+/****************************************************************************
+ removes all temporary shores.      (I.e. shrink the land by one tile.)
+****************************************************************************/
+static void erode_map(int polar_height) 
+{
+  int x, y;
+  for (x = 0; x < map.xsize; x++) {
+    for (y = polar_height; y < map.ysize - polar_height; y++) {
+      if (should_erode(x, y)) {
+       map_set_terrain(x, y, T6_ERODE_LAND);
+      }
+    }
+  }
+  
+  for (x = 0; x < map.xsize; x++) {
+    for (y = polar_height; y < map.ysize - polar_height; y++) {
+      if (map_get_terrain(x, y) == T6_ERODE_LAND) {
+       map_set_terrain(x, y, T6_TEMP_OCEAN);
+      }
+    }
+  }
+}
+
+/****************************************************************************
+ returns true if a neighboring point should be dilated. 
+****************************************************************************/
+static int dilate_terrain(int x, int y)
+{
+  int terrain;
+  normalize_map_pos(&x, &y);
+  terrain = map_get_terrain(x, y);
+  return !g6_ocean(terrain) && terrain != T6_DILATE_LAND;
+}
+
+/****************************************************************************
+ returns true if the terrain at point x,y should be dilated.  
+****************************************************************************/
+static int should_dilate(int x, int y)
+{
+  int terrain = map_get_terrain(x, y);
+  if(!g6_ocean(terrain) || terrain == T6_PERM_OCEAN) {
+    return FALSE;
+  }
+  if (dilate_terrain(x - 1, y) 
+      || dilate_terrain(x + 1, y)
+      || dilate_terrain(x, y - 1)
+      || dilate_terrain(x, y + 1)) {
+    return TRUE;
+  } else {
+    return FALSE;
+  }
+}
+
+/****************************************************************************
+ removes all temporary beaches.   (I.e. shrink the ocean by one tile.) 
+****************************************************************************/
+static void dilate_map(int polar_height) 
+{
+  int x, y;
+  for (x = 0; x < map.xsize; x++) {
+    for (y = polar_height; y < map.ysize - polar_height; y++) {
+      if (should_dilate(x, y)) {
+       map_set_terrain(x, y, T6_DILATE_LAND);
+      }
+    }
+  }
+  
+  for (x = 0; x < map.xsize; x++) {
+    for (y = polar_height; y < map.ysize - polar_height; y++) {
+      if (map_get_terrain(x, y) == T6_DILATE_LAND) {
+       map_set_terrain(x, y, T6_TEMP_LAND);
+      }
+    }
+  }
+}
+
+
+/****************************************************************************
+ returns a score based on the amount of land surrounding the point x,y. 
+ No land is 0.  All eight neighbors is 12
+ ***************************************************************************/
+static int border_score(int x, int y)
+{
+  int adj_score = 2;
+  int diag_score = 1;
+  int score = 0;
+  score += land_terrain(x - 1, y) ? adj_score : 0;
+  score += land_terrain(x + 1, y) ? adj_score : 0;
+  score += land_terrain(x, y - 1) ? adj_score : 0;
+  score += land_terrain(x, y + 1) ? adj_score : 0;
+  score += land_terrain(x - 1, y - 1) ? diag_score : 0;
+  score += land_terrain(x + 1, y - 1) ? diag_score : 0;
+  score += land_terrain(x - 1, y + 1) ? diag_score : 0;
+  score += land_terrain(x + 1, y + 1) ? diag_score : 0;
+  return score;
+}
+
+enum terrain_status { TS_OCEAN, TS_LAND, TS_NEW_LAND};
+
+/*************************************************************************
+  Returns TS_NEW_LAND if the point is not already land and 
+  the random number is less than the border score.
+  More likely to return TS_NEW_LAND on a location that has
+  more land around it.  Will never return TS_NEW_LAND on a
+  location that has no land in any of the eight neighbors.
+ *************************************************************************/
+static int random_new_land(int x, int y)
+{
+  int score;
+  int random;
+  if (land_terrain(x, y)) {
+    return TS_LAND;
+  }
+  if (map_get_terrain(x, y) == T6_PERM_OCEAN) {
+    return TS_OCEAN;
+  }
+  score = border_score(x, y);
+  random = myrand(4 * 2 + 4 * 1);
+  return random + 1 > score ? TS_OCEAN : TS_NEW_LAND;
+
+}
+
+/*************************************************************************
+ Creates a peninsula and put the player's starting position
+ on it. The direction is either +1 or -1 depending on which 
+ direction the peninsula should go from the y position.
+ The remaining_count is the number of tiles to  fill with 
+ land.
+ *************************************************************************/
+static void create_peninsula(int x, int y, int player_number,
+                            int width, int height, int direction,
+                            int neck_height,int neck_width,
+                            int neck_displacement)
+{
+  int cx, cy;
+  int head_height = height-neck_height;
+  int neck_start = x + neck_displacement;
+  int ocean_distance = neck_height / 2;
+
+  /* make perm ocean */
+  { 
+    int top_location = y - (ocean_distance + 1) * direction;
+    int bottom_location = y + (height - ocean_distance) * direction;
+    int left_location = x - ocean_distance - 1;
+    int right_location = x + width + ocean_distance;
+    
+    /* top and bottom */
+    for (cx = left_location; cx < right_location + 1; cx++) {
+      map_set_terrain(cx, top_location, T6_PERM_OCEAN);
+      map_set_terrain(cx, bottom_location, T6_PERM_OCEAN);
+    }
+
+    /* left and right */
+    for (cy = top_location; cy != bottom_location + direction;
+        cy += direction) {
+      map_set_terrain(left_location, cy, T6_PERM_OCEAN);
+      map_set_terrain(right_location, cy, T6_PERM_OCEAN);
+    }
+
+    /* connect to central ocean */
+    for (cy = map.ysize / 2; cy != top_location; cy += direction) {
+      map_set_terrain(x + width / 2, cy, T6_PERM_OCEAN);
+    }
+  }
+
+  /* make head */
+  for (cx = x; cx < x + width; cx++) {
+    for (cy = y; cy != y + (head_height + 1) * direction; cy += direction) {
+      map_set_terrain(cx, cy, T6_PERM_LAND);
+      hmap(cx, cy) = myrand(7000) - 2000;
+    }
+  }
+
+  /* make neck */
+  for (cx = neck_start; cx < neck_start + neck_width; cx++) {
+    for (cy = y + (head_height + 1) * direction; 
+        cy != y + (height + 1) * direction; cy += direction ) {
+      map_set_terrain(cx, cy, T6_PERM_LAND);
+      hmap(cx, cy) = myrand(7000) - 2000;
+    }
+  }
+
+  map.start_positions[player_number].x = x + width / 2;
+  map.start_positions[player_number].y = y;
+}
+
+/*************************************************************************
+  This generator creates a map with one penisula for each 
+   player and an isthmus between.  It creates a central
+   ocean and puts the peninsulas around the edges.  It is 
+   intented for quicker games. Should look something like this:
+   *****************************
+   ****  *****  *****  *****    
+    **    ***    ***    ***     
+    **                          
+    **                           
+    **       ***    ***             
+   ****     *****  *****           
+   *****************************
+
+  If make_roads is true, it will generate roads on the polar regions and 
+   on the isthmus.
+ *************************************************************************/
+static void mapgenerator67(bool make_roads)
+{
+  int peninsulas = game.nplayers;
+  int peninsulas_on_one_side = (peninsulas + 1) / 2;
+  int isthmus_width = 10;
+  int neck_height = (map.ysize / 8) | 1; 
+  int polar_height = 3;
+  int peninsula_separation = neck_height;
+  int max_peninsula_width = (map.xsize - isthmus_width - peninsula_separation)
+    / (peninsulas_on_one_side) - peninsula_separation;
+  int max_peninsula_height = map.ysize / 2 - polar_height - neck_height;
+  int neck_width = MIN(6,max_peninsula_width - 1);
+  int min_peninsula_width = neck_width;
+  int min_peninsula_height = neck_height;
+  int peninsula_area = MAX((max_peninsula_width * max_peninsula_height * 
+                           map.landpercent) / 100, 
+                          min_peninsula_width * min_peninsula_height);
+  int i, x, y;
+  int isle = 1;
+
+  if(min_peninsula_width > max_peninsula_width 
+     || min_peninsula_height > max_peninsula_height
+     || min_peninsula_width < 2 || min_peninsula_height < 2)
+  {
+    freelog(LOG_NORMAL, 
+           "mapgen.c: unable to use generator 6. "\
+           "mw %d Mw %d mh %d Mh %d area %d",
+           min_peninsula_width,max_peninsula_width,
+           min_peninsula_height,max_peninsula_height,peninsula_area);
+    map.generator = 5;
+    return;
+  }
+
+  height_map = fc_malloc(sizeof(int) * map.xsize * map.ysize);
+
+  /* map.start_positions = (fc_realloc(map.start_positions,
+                                  game.nplayers
+                                  * sizeof(*map.start_positions)); */
+
+  /* initialize everything to temp ocean */
+  for (y = 0; y < map.ysize; y++)
+    for (x = 0; x < map.xsize; x++) {
+      map_set_terrain(x, y, T6_TEMP_OCEAN);
+      hmap(x, y) = 0;
+    }
+
+  /* create central perm ocean */
+  y = map.ysize / 2;
+  for (x = isthmus_width; x < map.xsize; x++) {
+    map_set_terrain(x, y, T6_PERM_OCEAN);
+    hmap(x, y) = 0;
+  }
+
+  /* create polar regions */
+  for (x = 0; x < map.xsize; x++) {
+    for (y = 0; y < polar_height; y++) {
+      int rand_num = myrand(9);
+      map_set_terrain(x, y, rand_num > 7 ? T_ARCTIC :
+                     (rand_num < 2 ? T_MOUNTAINS : T_TUNDRA));
+      rand_num = myrand(9);
+      map_set_terrain(x, map.ysize - 1 - y, rand_num > 7 ? T_ARCTIC :
+                     (rand_num < 2 ? T_MOUNTAINS : T_TUNDRA));
+    }
+  }
+
+  /* build polar regions road */
+  if (make_roads) {
+    for (x = 0; x < map.xsize; x++) {
+      y = polar_height - 1;
+      if (map_build_road_time(x, y - 1) < map_build_road_time(x, y)) {
+       map_set_special(x, y - 1, S_ROAD);
+      } else {
+       map_set_special(x, y, S_ROAD);
+      }
+      y = map.ysize - polar_height;
+      if (map_build_road_time(x, y + 1) < map_build_road_time(x, y)) {
+       map_set_special(x, y + 1, S_ROAD);
+      } else {
+       map_set_special(x, y, S_ROAD);
+      }
+    }
+  } /* make_roads */
+
+  map.num_continents = 1;
+
+  /* create isthmus centeral strip */
+  x = isthmus_width / 2;
+  for (y = polar_height; y < map.ysize - polar_height; y++) {
+    map_set_terrain(x, y, T6_PERM_LAND);
+    hmap(x, y) = 100 * x * (isthmus_width - x) + (myrand(400) - 200);
+  }
+
+  /* setup peninsulas */
+  for (i = 0; i < game.nplayers; i++) {
+    /* direction is the direction to increment from the x and y location */
+    int direction = (i < peninsulas_on_one_side) ? -1 : 1;
+    int index = (direction == -1) ? i : i - peninsulas_on_one_side;
+    int width = min_peninsula_width + 
+      myrand(max_peninsula_width - min_peninsula_width + 1);
+    int height = CLIP(min_peninsula_height,
+                     peninsula_area / width + neck_height,
+                     max_peninsula_height);
+    int neck_displacement = myrand(width - neck_width + 1);
+    int x_displacement = myrand(max_peninsula_width - width + 1);
+    if(index == 0) {
+      x = peninsula_separation + isthmus_width;
+      if(direction == 1 && game.nplayers & 1) {
+       /* center the thing */
+       x = x + max_peninsula_width / 2;
+      } 
+    } 
+    y = (direction == -1)
+       ? height + polar_height : map.ysize - 1 - height - polar_height;
+    create_peninsula(x + x_displacement, y, i, width, height, direction,
+                    neck_height,neck_width,neck_displacement);
+    x = x + peninsula_separation + max_peninsula_width;
+  }
+
+  map.num_start_positions = game.nplayers;
+
+  { 
+    int consider_height = map.ysize - 2 * polar_height;
+    int desired_squares = 
+      (consider_height * map.xsize * map.landpercent) / 200;
+    int bailout_number = desired_squares * 30;
+    int seed = MIN(20, desired_squares / 20); /* seed islands */
+    desired_squares = MAX(0, desired_squares - seed);
+    while (bailout_number > 0 && seed > 0) {
+      x = myrand(map.xsize);
+      y = myrand(consider_height) + polar_height;
+      if (map_get_terrain(x, y) == T6_TEMP_OCEAN) {
+       map_set_terrain(x, y, T6_TEMP_LAND);
+       hmap(x, y) = myrand(7000) - 2000;
+       seed--;
+      }
+      bailout_number--;
+    }
+    while (bailout_number > 0 && desired_squares > 0) {
+      x = myrand(map.xsize);
+      y = myrand(consider_height) + polar_height;
+      if (random_new_land(x, y) == TS_NEW_LAND) {
+       map_set_terrain(x, y, T6_TEMP_LAND);
+       hmap(x, y) = myrand(7000) - 2000;
+       desired_squares--;
+      }
+      bailout_number--;
+    }
+  }
+
+  /* remove small oceans  */
+  dilate_map(polar_height);
+  erode_map(polar_height);
+  /* remove small islands */
+  erode_map(polar_height);
+  dilate_map(polar_height);
+
+  /* translate to real terrain */
+  for (x = 0; x < map.xsize; x++) {
+    for (y = polar_height; y < map.ysize - polar_height; y++) {
+      int terrain = map_get_terrain(x, y);
+      if (terrain == T6_TEMP_LAND) {
+       map_set_terrain(x, y, T_GRASSLAND);
+      } else if(terrain == T6_TEMP_OCEAN) {
+       map_set_terrain(x, y, T_OCEAN);
+      }
+    }
+  }
+
+  whole_map_iterate(x, y) {
+    map_set_continent(x, y, 0);
+  } whole_map_iterate_end;
+  
+  assign_continent_flood(0, 0, isle++);
+
+  whole_map_iterate(x, y) {
+    if (map_get_continent(x, y) == 0 
+        && !is_ocean(map_get_terrain(x, y))) {
+      assign_continent_flood(x, y, isle++);
+    }
+  } whole_map_iterate_end;
+
+  map.num_continents = isle-1;
+
+  /* setup terrain */
+  smooth_map();
+  make_mountains(1600);
+  make_swamps();
+  make_forests();
+  make_deserts();
+  make_plains();
+  make_fair();
+  make_rivers();
+
+  /* create isthmus road */
+  if (make_roads) {
+    int last_x, middle_x = isthmus_width / 2;
+    last_x = middle_x;
+    for (y = polar_height - 1; y < map.ysize - polar_height + 1; y++) {
+      int best_x = middle_x;
+      int min_build = map_build_road_time(middle_x, y);
+      for (x = MAX(last_x - 1, middle_x - 1);
+          x != MIN(last_x + 1, middle_x + 1) + 1; x++) {
+       if (land_terrain(x, y) && map_build_road_time(x, y) < min_build) {
+         best_x = x;
+         min_build = map_build_road_time(x, y);
+       }
+      }
+      map_set_special(best_x, y, S_ROAD);
+      last_x = best_x;
+    }
+  }
+
+  free(height_map);
+
+}

[Prev in Thread] Current Thread [Next in Thread]