Complete.Org: Mailing Lists: Archives: freeciv-dev: March 2003:
[Freeciv-Dev] Re: (PR#2749) mapgen patch 2 of 3 - land placement
Home

[Freeciv-Dev] Re: (PR#2749) mapgen patch 2 of 3 - land placement

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
To: kayeats@xxxxxxxxxxxx
Subject: [Freeciv-Dev] Re: (PR#2749) mapgen patch 2 of 3 - land placement
From: "kayeats@xxxxxxxxxxxxxxxxxxxxxxxxx" <kayeats@xxxxxxxxxxxxxxxxxxxxxxxxx>
Date: Sat, 22 Mar 2003 17:02:12 -0800
Reply-to: rt@xxxxxxxxxxxxxx

Sorry for disappearing, I got a bit busy with other things.  Most of this
email I wrote before I disappeared so I hope I haven't missed something
by forgetting what I had and had not done.


On Tue, 18 Feb 2003, rwetmore@xxxxxxxxxxxx wrote:

> Actually, I think you saved calculating the function distribution twice
> by storing it the first time. This can become a significant part, while
> whole_map indexing and lookup becomes trivial in comparison.

True, yes.

> > Ross, I hope you will see that though I chose a very simple version of
> > local effects to use for forests much more is now possible, including
> > recursive stuff like current cvs.
>
> Well, not quite up to CVS capabilities yet ...

Well, you called me, but it's no bluff.  Here is how you can think of cvs.
First here's the idea done non-sequentially: select a map position in the
same manner as in the cvs make_forests and give it the lowest value, then
perform the same algorithm as in make_forest centered at this spot, but in
places where you would place a forest instead set successively slightly
less low values and do nothing elsewhere.  Now returning to make_forests
repeat with slightly higher values until the value of every tile has been
set.  Now if we fill up until we have enough forests a la place_ter_type
we will get the same result as current cvs.  Now how do we make this idea
work under a whole map iterate?  Just pick a random value for each tile,
then follow make_forest centered at that tile placing values progressively
slightly higher, and to make this equivalent to the former formulation you
just have to only replace a value which has already been set if the new
value is lower.  This will give function map values indisguishable from
the first formulation, and so will satisfy the requirement of meeting
current cvs and of being doable through a whole map iterate.

> It probably works better if you first generate the single-valued
> function(x,y) map, then do a random order localization/smoothing function.
> using it as input. This is sort of the code flavour in smooth_map()
> and in fact is how height_maps are generated with feedback effects in
> CVS. Helga also used this sort of technique to generate rainfall
> patterns.

Ok, I'll make the function map in the make_<terrain> functions, then you
can do what you like with it, even if they only thing I want to do with it
is what I'm doing already.  A separate smoothing step could be useful.

> +    whole_map_iterate(x, y){
> +      if ((fmap(x, y) < level) && not_placed(x, y)){
> +       map_set_terrain(x, y, ter);
> +      }
> +      else if ((fmap(x, y) == level) && (myrand(100) < percent)
> +              && not_placed(x, y)) {
> +       map_set_terrain(x, y, ter);
> +      }
> +    } whole_map_iterate_end;
> +  }
>
>     Note, you again have an order dependency in the above because
> while the <level part is rigorously defined, the ==level is not.

Certainly this is error, but I'm missing how it is order dependent error.

> This is also a source of error in your placement accuracy since
> there is no guarantee you will place all tiles, and you can even
> place more than allowed.

Yes, and on average it will come out ok.  I don't see why it should place
at most what you ask for rather than about what you ask for.

>     Normally this would not be a problem, but the only real advantage
> you claim is an accurate numerical placement and this code makes
> that claim somewhat less than truthful. CVS may actually be better
> in that it usually tracks the actual counts.

Have you ever actually tried counting how many tiles CVS places, it is
almost never actually enough.  Tracking actual counts would be fine if you
had some reason to believe that you'd find enough suitable locations in a
reasonable time.  Sure you can hack some preprocessing on to make it
easier to get the counts, but that won't be generalizable to whatever
bizarre terrains someone might decide they want to play with, nor to
whatever bizarre new height-map makers someone might want.  Tracking
counts isn't good enough as an algorithm.

>    You can probably do something like this to collect the set of
> valid indicies/coordinates in the first pass as well. In the final
> distribution, you then don't need to do a whole_map, nor call things
> like not_placed().

I could do that.  That's naturally a listish sort of set; what would you
think to be the best way to implement it without unduly obfuscating what
is really a minor thing?

> If your distribution counts tiles
> you can't place that will also screw things up in weird and wonderful
> ways.

Um, calculating the distribution is precisely where I check not_placed,
doing it earlier may be better under the hood, but won't change the end
result.

> This
> will lead to array overflows. At least check the value before
> or after returning and again, make comments about the dangers.

I check when I fill in the array.  Not checking earlier means that the
function can add and subtract to its heart's desire without losing
information until the end.

>     This is just a smoothing function. It does not generate the
> sort of N-S elongated forest tracks that CVS does, since the
> final hard distribution choice you use in the last step may
> still only select a few bumps in the smoothed distribution.

I know its just a smoothing function -- but it looks nice.  That's what
matters.  You can copy CVS if you'd rather, but I don't see the point.
I'm making it possible if you want to which is what you were asking for I
thought.




Index: server/mapgen.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/mapgen.c,v
retrieving revision 1.110
diff -u -r1.110 mapgen.c
--- server/mapgen.c     2003/02/20 09:45:22     1.110
+++ server/mapgen.c     2003/03/23 00:59:26
@@ -34,6 +34,7 @@
 /* Wrapper for easy access.  It's a macro so it can be a lvalue. */
 #define hmap(x, y) (height_map[map_pos_to_index(x, y)])
 #define rmap(x, y) (river_map[map_pos_to_index(x, y)])
+#define fmap(x, y) (function_map[map_pos_to_index(x, y)])
 
 static void make_huts(int number);
 static void add_specials(int prob);
@@ -56,7 +57,6 @@
 
 static int *height_map;
 static int maxval=0;
-static int forests=0;
 
 struct isledata {
   int goodies;
@@ -68,210 +68,346 @@
 #define MAP_NCONT 255
 
 /**************************************************************************
-  make_mountains() will convert all squares that are higher 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).
-**************************************************************************/
-static void make_mountains(int thill)
-{
-  int mount;
-  int j;
-  for (j=0;j<10;j++) {
-    mount=0;
-    whole_map_iterate(x, y) {
-      if (hmap(x, y)>thill) 
-       mount++;
-    } whole_map_iterate_end;
-    if (mount < (map_num_tiles() * map.mountains) / 1000)
-      thill*=95;
-    else 
-      thill*=105;
-    thill/=100;
-  }
+  Checks if land has not yet been placed on tile at (x, y) (because land is 
+  plains based this just means checking if there are plains at (x, y))
+**************************************************************************/
+static bool not_placed(x, y)
+{
+  return map_get_terrain(x, y) == T_PLAINS;
+}
+
+/**************************************************************************
+  Places land according to a function.  Values placed by 
+  "function" into its third argument are taken to be 0 if less than 0 and
+  100 is greater than 100.
+*************************************************************************/
+static void place_ter_type(enum tile_terrain_type ter, int* function_map,
+                          int total)
+{
+  int count = 0;
+  int percent;
+  int level = - 1;
+  int max = 100;
+  int *dist;
+
+  /* make the distribution of the placing function */
+  dist = fc_calloc (max + 1, sizeof(int));
   
   whole_map_iterate(x, y) {
-    if (hmap(x, y) > thill && !is_ocean(map_get_terrain(x,y))) { 
-      if (myrand(100)>75) 
-       map_set_terrain(x, y, T_MOUNTAINS);
-      else if (myrand(100)>25) 
-       map_set_terrain(x, y, T_HILLS);
+    if (not_placed(x, y)) {
+      if (fmap(x, y) > 100) {
+       fmap(x, y) = 100;
+      }
+      if (fmap(x, y) < 0) {
+       fmap(x, y) = 0;
+      }
+      dist[fmap(x, y)]++;
     }
   } whole_map_iterate_end;
+  
+  
+  /* Now place land according to the distribution. */
+  
+  /* First determine the maximum value (level) of function such that
+   * the number of tiles that have function value less than level is
+   * at most total  */
+  while ((level < max) && (count <= total)) {
+    level++;
+    count += dist[level];
+  }
+  
+  /* now place land of type ter at all tiles with function value
+   * less than level along with the appropriate fraction of tiles with
+   * function value equal to level so as to get the correct total */
+  if (level < 0) {
+    /* didn't go through the while loop successfully, so we want to place
+     * none of this tile type, thus do nothing */
+  } else{
+    percent = dist[level] ? 100 - (count - total) * 100 / dist[level] : 0;
+    
+    whole_map_iterate(x, y){
+      if ((fmap(x, y) < level) && not_placed(x, y)){
+       map_set_terrain(x, y, ter);
+      }
+      else if ((fmap(x, y) == level) && (myrand(100) < percent) 
+              && not_placed(x, y)) {
+       map_set_terrain(x, y, ter);
+      }
+    } whole_map_iterate_end;
+  }
 }
 
 /**************************************************************************
- add arctic and tundra squares in the arctic zone. 
- (that is the top 10%, and low 10% of the map)
-**************************************************************************/
-static void make_polar(void)
+  One widely used function for place_ter_type is the height recalibrated
+  this assumes that the minimum value of hmap is 0 as is the case if 
+  adjust_map has been called.
+*************************************************************************/
+static void height_fn(int x, int y, int *function_map)
 {
-  int y,x;
+  fmap(x, y) = hmap(x, y) * 100 / maxval;
+}
 
-  for (y=0;y<map.ysize/10;y++) {
-    for (x=0;x<map.xsize;x++) {
-      if ((hmap(x, y)+(map.ysize/10-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 use in make_mountains and make_swamps, gets the maximum difference in 
+  height between the given tile and 8 surrounding tiles
+*************************************************************************/
+static int tile_max_steepness(int x, int y)
+{
+  int slope;
+  
+  slope = -FC_INFINITY;
+  adjc_iterate(x, y, a, b){
+    if (hmap(x, y) - hmap(a, b) > slope)
+      slope = hmap(x, y) - hmap(a, b);
+  } adjc_iterate_end;
+  
+  return slope;
+}
+
+/**************************************************************************
+  The function which determines the distribution of mountains and hills
+*************************************************************************/
+static void mountain_fn(int x, int y, int *function_map)
+{
+  int parameter; /* a percent determining relative importance of
+                  * absolute height and steepness */
+  int slopepart, heightpart, val;
+  static int slopemax = -1;
+  static int slopemin = -1;
+  
+  /* if its the first time through calculate slopemax and slopemin */
+  if (slopemax == -1) {
+    slopemin = tile_max_steepness(1, 1);
+    slopemax = slopemin;
+    whole_map_iterate(x, y){
+      val = tile_max_steepness(x, y);
+      if (slopemin > val)
+       slopemin = val;
+      if (slopemax < val)
+       slopemax = val;
+    } whole_map_iterate_end;
   }
-  for (y=map.ysize*9/10;y<map.ysize;y++) {
-    for (x=0;x<map.xsize;x++) {
-      if ((hmap(x, y)+(map.ysize/10-(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);
-      }
-    }
+  
+  /* set up parameter */
+  if (map.generator == 1){
+    parameter = 95;
   }
-
-  /* only arctic and tundra allowed at the poles (first and last two lines,
-     as defined in make_passable() ), to be consistent with generator>1. 
-     turn every land tile on the second lines that is not arctic into tundra,
-     since the first lines has already been set to all arctic above. */
-  for (x=0;x<map.xsize;x++) {
-    if (map_get_terrain(x, 1)!=T_ARCTIC &&
-       !is_ocean(map_get_terrain(x, 1)))
-      map_set_terrain(x, 1, T_TUNDRA);
-    if (map_get_terrain(x, map.ysize-2)!=T_ARCTIC && 
-       !is_ocean(map_get_terrain(x, map.ysize-2)))
-      map_set_terrain(x, map.ysize-2, T_TUNDRA);
+  else {
+    parameter = 30;
   }
+  
+  /* now computed the weighted function of slope and absolute height */
+  if (slopemax == slopemin)
+    slopepart = (100 - parameter) / 2;
+  else
+    slopepart = (tile_max_steepness(x, y) - slopemin) * (100 - parameter)
+      / (slopemax - slopemin);
+  if (maxval == 0)
+    heightpart = parameter / 2;
+  else
+    heightpart = hmap(x, y) * parameter / maxval; 
+  fmap(x, y) = 100 - slopepart - heightpart;
 }
 
 /**************************************************************************
-  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. 
+  make_mountains uses place_ter_type with a function of both height and 
+  steepness to place mountains and hills in accordance with map.mountains
 **************************************************************************/
-static void make_desert(int x, int y, int height, int diff) 
+static void make_mountains()
 {
-  if (abs(hmap(x, y)-height)<diff && map_get_terrain(x, y)==T_GRASSLAND) {
-    map_set_terrain(x, y, T_DESERT);
-    cartesian_adjacent_iterate(x, y, x1, y1) {
-      if (x != x1)
-       make_desert(x1, y1, height, diff-1);
-      else /* y != y1 */
-       make_desert(x1, y1, height, diff-3);
-    } cartesian_adjacent_iterate_end;
-  }
+  int mtotal = (map_num_tiles() * map.mountains * map.landpercent) / 20000;
+  int htotal = mtotal;
+  int *function_map;
+  
+  /* calculate the placement function at each point. */
+  function_map = fc_calloc (map.ysize * map.xsize, sizeof(int));
+  whole_map_iterate(x, y) {
+    mountain_fn(x, y, function_map);
+  } whole_map_iterate_end;
+
+  place_ter_type(T_MOUNTAINS, function_map, mtotal);
+  place_ter_type(T_HILLS, function_map, htotal);
 }
 
 /**************************************************************************
-  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
-**************************************************************************/
-static void make_forest(int x, int y, int height, int diff)
+  The placing function for arctic and tundra
+*************************************************************************/
+static void polar_fn(int x, int y, int *function_map)
 {
-  if (y==0 || y==map.ysize-1)
-    return;
+  fmap(x, y)
+    = (y > map.ysize - y - 1 ? map.ysize - y - 1 : y) * 200 / map.ysize;
+}
 
-  if (map_get_terrain(x, y)==T_GRASSLAND) {
-    if (y>map.ysize*42/100 && y<map.ysize*58/100 && myrand(100)>50)
-      map_set_terrain(x, y, T_JUNGLE);
-    else 
-      map_set_terrain(x, y, T_FOREST);
-      if (abs(hmap(x, y)-height)<diff) {
-       cartesian_adjacent_iterate(x, y, x1, y1) {
-         if (myrand(10)>5) make_forest(x1, y1, height, diff-5);
-       } cartesian_adjacent_iterate_end;
-      }
-    forests++;
+/**************************************************************************
+ Place arctic and tundra by first making a strip of arctic and then calling
+ place_ter_type for the rest with a function based on distance from poles,
+ there is no server option for amount of polar land so we have to guess
+**************************************************************************/
+static void make_polar(void)
+{
+  int x;
+  int atotal = (5 * map_num_tiles() * map.landpercent)/20000;
+  int ttotal = atotal;
+  int *function_map;
+  
+  /* first the polar strip must be placed */
+  /* note that atotal is keeping track of the glaciers over land, not
+     glaciers over water as well */
+  for (x=0;x<map.xsize;x++) {
+    map_set_terrain(x, 0, T_ARCTIC);
+    if (not_placed(x, 0)){
+      atotal --;
+    }
+    map_set_terrain(x, map.ysize-1, T_ARCTIC);
+    if (not_placed(x, map.ysize-1)){
+      atotal --;
+    }
   }
+  
+  /* calculate the placement function at each point. */
+  function_map = fc_calloc (map.ysize * map.xsize, sizeof(int));
+  whole_map_iterate(x, y) {
+    polar_fn(x, y, function_map);
+  } whole_map_iterate_end;
+
+  /* now use place_ter_type for the rest */
+  place_ter_type(T_ARCTIC, function_map, atotal);
+  place_ter_type(T_TUNDRA, function_map, ttotal);
 }
 
 /**************************************************************************
-  makeforest calls make_forest with random grassland locations until there
-  has been made enough forests. (the map.forestsize value controls this) 
+  forest placing function based on a random factor and proximity of forests
 **************************************************************************/
-static void make_forests(void)
+static void forest_fn(int x, int y, int *function_map)
 {
-  int x,y;
-  int forestsize = (map_num_tiles() * map.forestsize) / 1000;
+  int val = myrand(100);
+  int parameter = 20;
+  
+  fmap(x, y) += val * parameter / 100;
 
-  forests = 0;
+  /* divide the rest of val among near-by tiles with 1/32 for each tile
+     within radius 2 and an additional 1/32 for adjacent tiles.  Note that 
+     8 * 1/16 + 16 * 1/32 = 1, so we are right on budget. */ 
+  adjc_iterate(x, y, x1, y1) {
+    fmap(x1, y1) += val * (100 - parameter) / 3200;
+  } adjc_iterate_end;
 
-  do {
-    rand_map_pos(&x, &y);
-    if (map_get_terrain(x, y)==T_GRASSLAND) {
-      make_forest(x,y, hmap(x, y), 25);
+  square_iterate(x, y, 2, x1, y1) {
+    if ((x1 != x) || (y1 != y)) { 
+      fmap(x1, y1) += val * (100 - parameter) / 3200;
     }
-    if (myrand(100)>75) {
-      y=(myrand(map.ysize*2/10))+map.ysize*4/10;
-      x=myrand(map.xsize);
-      if (map_get_terrain(x, y)==T_GRASSLAND) {
-       make_forest(x,y, hmap(x, y), 25);
-      }
-    }
-  } while (forests<forestsize);
+  } square_iterate_end;
 }
 
 /**************************************************************************
-  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
+  jungle placing function, based on proximity to jungles and equator along
+  with a random factor
 **************************************************************************/
-static void make_swamps(void)
+static void jungle_fn(int x, int y, int *function_map)
 {
-  int x,y,swamps;
-  int forever=0;
-  for (swamps=0;swamps<map.swampsize;) {
-    forever++;
-    if (forever>1000) return;
-    rand_map_pos(&x, &y);
-    if (map_get_terrain(x, y)==T_GRASSLAND && hmap(x, y)<(maxval*60)/100) {
-      map_set_terrain(x, y, T_SWAMP);
-      cartesian_adjacent_iterate(x, y, x1, y1) {
-       if (myrand(10) > 5 && !is_ocean(map_get_terrain(x1, y1))) { 
-         map_set_terrain(x1, y1, T_SWAMP);
-       }
-       /* maybe this should increment i too? */
-      } cartesian_adjacent_iterate_end;
-      swamps++;
+  int val; 
+  int parameter1 = 30; /* percent not to be spread among near by tiles */
+  int parameter2 = 33; /* percent random in either case */
+  
+  val = myrand(parameter2) 
+    + abs(map.ysize / 2 - y) * (100 - parameter2) * 2 / map.ysize;
+
+  fmap(x, y) += val * parameter1 / 100;
+
+  /* see forest_fn comment for meaning of magic numbers */
+  adjc_iterate(x, y, x1, y1) {
+    fmap(x1, y1) += val * (100 - parameter1) / 3200;
+  } adjc_iterate_end;
+
+  square_iterate(x, y, 2, x1, y1) {
+    if ((x1 != x) || (y1 != y)) { 
+      fmap(x1, y1) += val * (100 - parameter1) / 3200;
     }
-  }
+  } square_iterate_end;
 }
 
+/**************************************************************************
+  make_forests uses place_ter_type to place forests and jungles
+**************************************************************************/
+static void make_forests(void)
+{
+  int jtotal = (map_num_tiles() * map.landpercent * map.forestsize) / 50000;
+  int ftotal = jtotal * 4;
+  int *jfunction_map;
+  int *ffunction_map;
+
+  /* calculate the placement functions at each point. */
+  jfunction_map = fc_calloc (map.ysize * map.xsize, sizeof(int));  
+  ffunction_map = fc_calloc (map.ysize * map.xsize, sizeof(int));
+  whole_map_iterate(x, y) {
+    jungle_fn(x, y, jfunction_map);
+    forest_fn(x, y, ffunction_map);
+  } whole_map_iterate_end;
+  
+  place_ter_type(T_JUNGLE, jfunction_map, jtotal);
+  place_ter_type(T_FOREST, ffunction_map, ftotal);
+}
+
+/**************************************************************************
+  swamps are placed on low lying locations using place_ter_type
+ **************************************************************************/
+static void make_swamps()
+{
+  int stotal=(map_num_tiles() * map.landpercent * map.swampsize) / 10000;
+  int *function_map;
+
+  /* calculate the placement function at each point.*/  
+  function_map = fc_calloc (map.ysize * map.xsize, sizeof(int));
+  whole_map_iterate(x, y) {
+    height_fn(x, y, function_map);
+  } whole_map_iterate_end;
+
+  place_ter_type(T_SWAMP, function_map, stotal);
+}
+
+/**************************************************************************
+  Placing function for deserts, based on distance from tropics of cancer or
+  capricorn, a random factor, and proximity of deserts
+ *************************************************************************/
+static void desert_fn(int x, int y, int *function_map)
+{
+  int parameter1 = 25; /* percent not to be spread among near by tiles */
+  int parameter2 = 20; /* percent random in either case */
+  int dpart, val;
+
+  /* calculate the distance from the poles weighted with a random factor */
+  dpart = (abs(map.ysize * 65 / 180 - y) > abs(map.ysize * 115 / 180 - y) ?
+           abs(map.ysize * 115 / 180 - y) : abs(map.ysize * 65 / 180 - y))
+    * (100 - parameter2) * 180 / (65 * map.ysize);
+  val = myrand(parameter2) + dpart;
+
+  fmap(x, y) += val * parameter1 / 100;
+
+  /* see forest_fn comment for meaning of magic numbers */
+  adjc_iterate(x, y, x1, y1) {
+    fmap(x1, y1) += val * (100 - parameter1) / 3200;
+  } adjc_iterate_end;
+
+  square_iterate(x, y, 2, x1, y1) {
+    fmap(x1, y1) += val * (100 - parameter1) / 3200;
+  } square_iterate_end;
+}
+
 /*************************************************************************
-  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 between 20 and 30 degrees north and south 
-  (deserts tend to be between 15 and 35, make_desert will expand them) and 
-  if it's a grassland square we call make_desert with this coordinate, we 
-  try this 500 times for each region: north and south.
+  Uses place_ter_type to place deserts
 **************************************************************************/
 static void make_deserts(void)
 {
-  int x,y,i,j;
-  i=map.deserts;
-  j=0;
-  while (i > 0 && j < 500) {
-    j++;
-
-    y=myrand(map.ysize*10/180)+map.ysize*110/180;
-    x=myrand(map.xsize);
-    if (map_get_terrain(x, y)==T_GRASSLAND) {
-      make_desert(x,y, hmap(x, y), 50);
-      i--;
-    }
-    y=myrand(map.ysize*10/180)+map.ysize*60/180;
-    x=myrand(map.xsize);
-    if (map_get_terrain(x, y)==T_GRASSLAND) {
-      make_desert(x,y, hmap(x, y), 50);
-      i--;
-    }
-  }
+  int dtotal = (map_num_tiles() * map.deserts * map.landpercent)/10000;
+  int *function_map;
+
+  /* calculate the placement function at each point.*/
+  function_map = fc_calloc (map.ysize * map.xsize, sizeof(int));
+  whole_map_iterate(x, y) {
+    desert_fn(x, y, function_map);
+  } whole_map_iterate_end;
+
+  place_ter_type(T_DESERT, function_map, dtotal);
 }
 
 /*********************************************************************
@@ -763,16 +899,35 @@
   river_map = NULL;
 }
 
+/**************************************************************************
+  distribution function for grasslands, based on height and a random factor
+**************************************************************************/
+static void grass_fn(int x, int y, int *function_map)
+{
+  int parameter = 50;
+
+  if ((map.generator >= 2) && (map.generator <= 4)){
+    fmap(x, y) = myrand(100);
+  }
+
+  fmap(x, y) = myrand(parameter) + hmap(x, y) * (100 - parameter) / maxval;
+}
+
 /**************************************************************************
-  make_plains converts 50% of the remaining grassland to plains, this should
-  maybe be lowered to 30% or done in batches, like the swamps?
+  make_grass makes grassland on the lowlands that remain using place_ter_type
 **************************************************************************/
-static void make_plains(void)
+static void make_grass(void)
 {
+  int gtotal = (map_num_tiles() * map.landpercent * map.grasssize) / 20000;
+  int *function_map;
+
+  /* calculate the placement function at each point.*/
+  function_map = fc_calloc (map.ysize * map.xsize, sizeof(int));
   whole_map_iterate(x, y) {
-    if (map_get_terrain(x, y) == T_GRASSLAND && myrand(100) > 50)
-      map_set_terrain(x, y, T_PLAINS);
+    grass_fn(x, y, function_map);
   } whole_map_iterate_end;
+
+  place_ter_type(T_GRASSLAND, function_map, gtotal);
 }
 
 /**************************************************************************
@@ -830,37 +985,33 @@
 **************************************************************************/
 static void make_land(void)
 {
-  int tres=(maxval*map.landpercent)/100;
-  int count=0;
-  int total = (map_num_tiles() * map.landpercent) / 100;
-  int forever=0;
-  do {
-    forever++;
-    if (forever>50) break; /* loop elimination */
-    count=0;
-    whole_map_iterate(x, y) {
-      if (hmap(x, y) < tres)
-       map_set_terrain(x, y, T_OCEAN);
-      else {
-       map_set_terrain(x, y, T_GRASSLAND);
-       count++;
-      }
-    } whole_map_iterate_end;
-    if (count>total)
-      tres*=11;
-    else
-      tres*=9;
-    tres/=10;
-  } while (abs(total-count)> maxval/40);
+  int ototal = (map_num_tiles() * (100 - map.landpercent)) / 100;
+  int *function_map;
+
+  /* set all tiles to plains */
+  whole_map_iterate(x, y){
+    map_set_terrain(x, y, T_PLAINS);
+  } whole_map_iterate_end;
+
+  /* now fill in the oceans and do call the functions which do the rest */
+
+  /* calculate the placement function at each point. */
+  function_map = fc_calloc (map.ysize * map.xsize, sizeof(int));
+  whole_map_iterate(x, y) {
+    height_fn(x, y, function_map);
+  } whole_map_iterate_end;
+
+  place_ter_type(T_OCEAN, function_map, ototal);
+
   if (map.separatepoles) {
     make_passable();
   }
-  make_mountains(maxval*8/10);
+  make_polar();
+  make_mountains();
+  make_deserts();
   make_forests();
   make_swamps();
-  make_deserts();
-  make_plains();
-  make_polar();
+  make_grass();
   make_fair();
   make_rivers();
 }
@@ -1431,7 +1582,7 @@
     get_random_map_position_from_state(&x, &y, pstate);
 
     if (map_get_continent(x, y) == pstate->isleindex &&
-       map_get_terrain(x, y) == T_GRASSLAND) {
+       not_placed(x, y)) {
 
       /* the first condition helps make terrain more contiguous,
         the second lets it avoid the coast: */
@@ -1457,7 +1608,7 @@
            map_set_terrain(x, y, T_RIVER);
        }
       }
-      if (map_get_terrain(x,y) != T_GRASSLAND) i--;
+      if (!not_placed(x,y)) i--;
     }
   }
 }
@@ -1484,7 +1635,7 @@
   while (i > 0 && (failsafe--) > 0) {
     get_random_map_position_from_state(&x, &y, pstate);
     if (map_get_continent(x, y) == pstate->isleindex &&
-       map_get_terrain(x, y) == T_GRASSLAND) {
+       not_placed(x, y)) {
 
       /* the first condition helps make terrain more contiguous,
         the second lets it avoid the coast: */
@@ -1556,7 +1707,7 @@
          return i != 0;
        }
 
-        map_set_terrain(map_x, map_y, T_GRASSLAND);
+        map_set_terrain(map_x, map_y, T_PLAINS);
        map_set_continent(map_x, map_y, pstate->isleindex);
         i++;
       }
@@ -1814,7 +1965,7 @@
   for (i = game.nplayers; i > 0; i--) {
     make_island(10 * pstate->totalmass / totalweight, 0, pstate);
   }
-  make_plains();  
+  make_grass();  
   free(height_map);
   height_map = NULL;
 
@@ -1899,7 +2050,7 @@
                  pstate);
   }
 
-  make_plains();  
+  make_grass();  
   free(height_map);
   height_map = NULL;
     
@@ -1975,7 +2126,7 @@
   for (i = game.nplayers; i > 0; i--) {
     make_island(10 * pstate->totalmass / totalweight, 0, pstate);
   }
-  make_plains();  
+  make_grass();  
   free(height_map);
   height_map = NULL;
 

[Prev in Thread] Current Thread [Next in Thread]
  • [Freeciv-Dev] Re: (PR#2749) mapgen patch 2 of 3 - land placement, kayeats@xxxxxxxxxxxxxxxxxxxxxxxxx <=