Complete.Org: Mailing Lists: Archives: freeciv-dev: September 2004:
[Freeciv-Dev] Re: (PR#7584) [RFC]: generalizing terrain in mapgen
Home

[Freeciv-Dev] Re: (PR#7584) [RFC]: generalizing terrain in mapgen

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
Cc: mburda@xxxxxxxxx
Subject: [Freeciv-Dev] Re: (PR#7584) [RFC]: generalizing terrain in mapgen
From: "Jason Short" <jdorje@xxxxxxxxxxxxxxxxxxxxx>
Date: Thu, 9 Sep 2004 00:04:36 -0700
Reply-to: rt@xxxxxxxxxxx

<URL: http://rt.freeciv.org/Ticket/Display.html?id=7584 >

To demonstrate my proposal for this, I rewrote mapgen.  It now follows 
the algorithm I detailed before:

- Generate an hmap.
- Generate a tmap.
- Place rivers and generate an mmap.
- Place terrain, tile by tile, based on matching of the hmap,tmap,mmap 
to the values specified for the terrain in the ruleset.

Only gen1 works, but it works quite well.  There are about 70 different 
editable values in the ruleset, so there is a lot of possibility for 
tweaking the behavior.  In all I am quite happy with it.  Only a few 
hacks are included in the code: polar hmap adjustment and desert mmap 
adjustment.

There are not yet any server parameters controlling the behavior.  I 
would like to add a parameter each for height, temperature, and moisture 
that just changes the distribution of the values.

I'd also like to see what you could do with this and a civ3 terrain 
system.  The drawing code should be sufficient to support civ3 graphics 
in the client.  All that's missing from the code is the terrain 
restrictions civ3 imposes (e.g., tundra can't border desert).

jason


? server/diff
? server/foo.c
? server/output
? server/rivergen.h
Index: common/map.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/map.c,v
retrieving revision 1.192
diff -u -r1.192 map.c
--- common/map.c        3 Sep 2004 15:12:25 -0000       1.192
+++ common/map.c        9 Sep 2004 07:03:46 -0000
@@ -197,14 +197,8 @@
   map.riches                = MAP_DEFAULT_RICHES;
   map.huts                  = MAP_DEFAULT_HUTS;
   map.landpercent           = MAP_DEFAULT_LANDMASS;
-  map.grasssize             = MAP_DEFAULT_GRASS;
-  map.swampsize             = MAP_DEFAULT_SWAMPS;
-  map.deserts               = MAP_DEFAULT_DESERTS;
-  map.mountains             = MAP_DEFAULT_MOUNTAINS;
   map.riverlength           = MAP_DEFAULT_RIVERS;
-  map.forestsize            = MAP_DEFAULT_FORESTS;
   map.generator             = MAP_DEFAULT_GENERATOR;
-  map.tinyisles             = MAP_DEFAULT_TINYISLES;
   map.separatepoles         = MAP_DEFAULT_SEPARATE_POLES;
   map.alltemperate          = MAP_DEFAULT_ALLTEMPERATE;
   map.tiles                 = NULL;
Index: common/map.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/map.h,v
retrieving revision 1.213
diff -u -r1.213 map.h
--- common/map.h        3 Sep 2004 15:12:25 -0000       1.213
+++ common/map.h        9 Sep 2004 07:03:46 -0000
@@ -124,6 +124,12 @@
   Terrain_type_id warmer_wetter_result, warmer_drier_result;
   Terrain_type_id cooler_wetter_result, cooler_drier_result;
 
+  /* Mapgen data. */
+  int likelihood; /* >= 0 */
+  double height_avg, height_dev; /* [0,100] */
+  double temp_avg, temp_dev; /* [0,100] */
+  double moisture_avg, moisture_dev; /* [0,100] */
+
   bv_terrain_flags flags;
 
   char *helptext;
@@ -163,14 +169,8 @@
   int riches;
   int huts;
   int landpercent;
-  int grasssize;
-  int swampsize;
-  int deserts;
-  int mountains;
   int riverlength;
-  int forestsize;
   int generator;
-  bool tinyisles;
   bool separatepoles;
   bool alltemperate;
   int num_start_positions;
@@ -564,20 +564,28 @@
     }                                                                      \
 }
 
-/* Iterate over all positions on the globe. */
-#define whole_map_iterate(map_x, map_y)                                     \
-{                                                                           \
-  int WMI_index; /* We use index positions for cache efficiency. */         \
-  for (WMI_index = 0; WMI_index < MAX_MAP_INDEX; WMI_index++) {             \
+#define whole_map_iterate_index(index, map_x, map_y)                       \
+{                                                                          \
+  int index;                                                               \
+                                                                           \
+  for (index = 0; index < MAX_MAP_INDEX; index++) {                        \
     int map_x, map_y;                                                       \
-    index_to_map_pos(&map_x, &map_y, WMI_index);                            \
+    index_to_map_pos(&map_x, &map_y, index);                                \
     {
 
-#define whole_map_iterate_end                                               \
-    }                                                                       \
-  }                                                                         \
+
+#define whole_map_iterate_index_end                                        \
+    }                                                                      \
+  }                                                                        \
 }
 
+/* Iterate over all positions on the globe. */
+#define whole_map_iterate(map_x, map_y)                                     \
+  whole_map_iterate_index(_index, map_x, map_y)
+
+#define whole_map_iterate_end                                               \
+  whole_map_iterate_index_end
+
 BV_DEFINE(dir_vector, 8);
 
 struct unit_order {
@@ -635,30 +643,10 @@
 #define MAP_MIN_RICHES           0
 #define MAP_MAX_RICHES           1000
 
-#define MAP_DEFAULT_MOUNTAINS    30
-#define MAP_MIN_MOUNTAINS        10
-#define MAP_MAX_MOUNTAINS        100
-
-#define MAP_DEFAULT_GRASS       35
-#define MAP_MIN_GRASS           20
-#define MAP_MAX_GRASS           100
-
-#define MAP_DEFAULT_SWAMPS       5
-#define MAP_MIN_SWAMPS           0
-#define MAP_MAX_SWAMPS           100
-
-#define MAP_DEFAULT_DESERTS      5
-#define MAP_MIN_DESERTS          0
-#define MAP_MAX_DESERTS          100
-
 #define MAP_DEFAULT_RIVERS       5
 #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
Index: data/default/terrain.ruleset
===================================================================
RCS file: /home/freeciv/CVS/freeciv/data/default/terrain.ruleset,v
retrieving revision 1.31
diff -u -r1.31 terrain.ruleset
--- data/default/terrain.ruleset        4 Sep 2004 16:53:39 -0000       1.31
+++ data/default/terrain.ruleset        9 Sep 2004 07:03:48 -0000
@@ -216,6 +216,13 @@
 warmer_drier_result  = "no"
 cooler_wetter_result = "no"
 cooler_drier_result  = "no"
+likelihood           = 10000
+height_avg           = 40
+height_dev           = 40
+temp_avg             = 0
+temp_dev             = 1
+moisture_avg         = 20
+moisture_dev         = 100
 flags                = "NoBarbs", "CanHaveRiver", "UnsafeCoast", "Unsafe"
 helptext            = _("\
 Glaciers are found only in the most northerly or southerly\
@@ -264,6 +271,13 @@
 warmer_drier_result  = "no"
 cooler_wetter_result = "no"
 cooler_drier_result  = "no"
+likelihood           = 400
+height_avg           = 30
+height_dev           = 15
+temp_avg             = 100
+temp_dev             = 10
+moisture_avg         = 10
+moisture_dev         = 10
 flags                = "CanHaveRiver"
 helptext            = _("\
 Deserts are regions of extreme dryness, making agriculture and\
@@ -311,6 +325,13 @@
 warmer_drier_result  = "Desert"
 cooler_wetter_result = "no"
 cooler_drier_result  = "no"
+likelihood           = 1600
+height_avg           = 30
+height_dev           = 25
+temp_avg             = 40
+temp_dev             = 30
+moisture_avg         = 60
+moisture_dev         = 15
 flags                = "CanHaveRiver"
 helptext            = _("\
 Forests are densely wooded, making agriculture somewhat\
@@ -358,6 +379,13 @@
 warmer_drier_result  = "Desert"
 cooler_wetter_result = "Desert"
 cooler_drier_result  = "Tundra"
+likelihood           = 1500
+height_avg           = 20
+height_dev           = 25
+temp_avg             = 50
+temp_dev             = 25
+moisture_avg         = 60
+moisture_dev         = 20
 flags                = "Starter", "CanHaveRiver"
 helptext            = _("\
 Grasslands afford exceptional agricultural opportunities.\
@@ -404,6 +432,13 @@
 warmer_drier_result  = "no"
 cooler_wetter_result = "no"
 cooler_drier_result  = "no"
+likelihood           = 60
+height_avg           = 70
+height_dev           = 10
+temp_avg             = 60
+temp_dev             = 20
+moisture_avg         = 50
+moisture_dev         = 20
 flags                = "CanHaveRiver"
 helptext            = _("\
 In addition to being amenable to agriculture, Hills are frequently\
@@ -451,6 +486,13 @@
 warmer_drier_result  = "no"
 cooler_wetter_result = "Desert"
 cooler_drier_result  = "Tundra"
+likelihood           = 200
+height_avg           = 30
+height_dev           = 20
+temp_avg             = 100
+temp_dev             = 15
+moisture_avg         = 80
+moisture_dev         = 10
 flags                = "CanHaveRiver"
 helptext            = _("\
 Jungles are densely overgrown, making agriculture somewhat\
@@ -498,6 +540,13 @@
 warmer_drier_result  = "no"
 cooler_wetter_result = "no"
 cooler_drier_result  = "no"
+likelihood           = 10
+height_avg           = 95
+height_dev           = 3
+temp_avg             = 40
+temp_dev             = 20
+moisture_avg         = 20
+moisture_dev         = 20
 flags                = "CanHaveRiver"
 helptext            = _("\
 Mountains are regions of extreme altitude, making agriculture and\
@@ -545,6 +594,13 @@
 warmer_drier_result  = "no"
 cooler_wetter_result = "no"
 cooler_drier_result  = "no"
+likelihood           = 1
+height_avg           = -50
+height_dev           = 50
+temp_avg             = 50
+temp_dev             = 50
+moisture_avg         = 50
+moisture_dev         = 50
 flags                = "Oceanic", "NoPollution", "UnsafeCoast", "NoCities"
 helptext            = _("\
 Oceans cover much of the world, and only sea units (Triremes and\
@@ -595,6 +651,13 @@
 cooler_wetter_result = "Desert"
 cooler_drier_result  = "Tundra"
 flags                = "Starter", "CanHaveRiver"
+likelihood           = 500
+height_avg           = 30
+height_dev           = 20
+temp_avg             = 50
+temp_dev             = 20
+moisture_avg         = 40
+moisture_dev         = 15
 helptext            = _("\
 Plains are very broad, sparse regions, which makes trade slightly\
  inconvenient.\
@@ -642,6 +705,13 @@
 cooler_wetter_result = "Desert"
 cooler_drier_result  = "Tundra"
 flags                = "CanHaveRiver"
+likelihood           = 2000
+height_avg           = 0
+height_dev           = 20
+temp_avg             = 50
+temp_dev             = 25
+moisture_avg         = 80
+moisture_dev         = 25
 helptext            = _("\
 Swamps suffer from an over-abundance of water, making agriculture\
  somewhat problematic.\
@@ -689,6 +759,13 @@
 cooler_wetter_result = "Glacier"
 cooler_drier_result  = "Glacier"
 flags                = "NoBarbs", "CanHaveRiver"
+likelihood           = 100
+height_avg           = 20
+height_dev           = 20
+temp_avg             = 0
+temp_dev             = 5
+moisture_avg         = 20
+moisture_dev         = 20
 helptext            = _("\
 Tundra are broad, cold regions, fit for some agriculture and little\
  else.\
Index: server/mapgen.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/mapgen.h,v
retrieving revision 1.15
diff -u -r1.15 mapgen.h
--- server/mapgen.h     6 Sep 2004 16:58:39 -0000       1.15
+++ server/mapgen.h     9 Sep 2004 07:03:49 -0000
@@ -13,7 +13,7 @@
 #ifndef FC__MAPGEN_H
 #define FC__MAPGEN_H
 
-void map_fractal_generate(bool autosize);
+void map_generate(bool autosize);
 void create_start_positions(void);
 
 #endif  /* FC__MAPGEN_H */
Index: server/maphand.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/maphand.h,v
retrieving revision 1.46
diff -u -r1.46 maphand.h
--- server/maphand.h    3 Sep 2004 04:22:37 -0000       1.46
+++ server/maphand.h    9 Sep 2004 07:03:49 -0000
@@ -50,7 +50,7 @@
 };
 
 /* The maximum number of continents and oceans. */
-#define MAP_NCONT 300
+#define MAP_NCONT 15000
 
 void assign_continent_numbers(void);
 
Index: server/ruleset.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/ruleset.c,v
retrieving revision 1.193
diff -u -r1.193 ruleset.c
--- server/ruleset.c    6 Sep 2004 02:58:12 -0000       1.193
+++ server/ruleset.c    9 Sep 2004 07:03:49 -0000
@@ -1695,6 +1695,14 @@
        = lookup_terrain(secfile_lookup_str(file, "%s.cooler_drier_result",
                                            sec[i]), i);
 
+      t->likelihood = secfile_lookup_int(file, "%s.likelihood", sec[i]);
+      t->height_avg = secfile_lookup_int(file, "%s.height_avg", sec[i]);
+      t->height_dev = secfile_lookup_int(file, "%s.height_dev", sec[i]);
+      t->temp_avg = secfile_lookup_int(file, "%s.temp_avg", sec[i]);
+      t->temp_dev = secfile_lookup_int(file, "%s.temp_dev", sec[i]);
+      t->moisture_avg = secfile_lookup_int(file, "%s.moisture_avg", sec[i]);
+      t->moisture_dev = secfile_lookup_int(file, "%s.moisture_dev", sec[i]);
+
       slist = secfile_lookup_str_vec(file, &nval, "%s.flags", sec[i]);
       BV_CLR_ALL(t->flags);
       for (j = 0; j < nval; j++) {
Index: server/savegame.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/savegame.c,v
retrieving revision 1.184
diff -u -r1.184 savegame.c
--- server/savegame.c   4 Sep 2004 21:25:57 -0000       1.184
+++ server/savegame.c   9 Sep 2004 07:03:50 -0000
@@ -3095,21 +3095,11 @@
       map.generator = secfile_lookup_int(file, "map.generator");
       map.seed = secfile_lookup_int(file, "map.seed");
       map.landpercent = secfile_lookup_int(file, "map.landpercent");
-      map.grasssize =
-       secfile_lookup_int_default(file, MAP_DEFAULT_GRASS, "map.grasssize");
-      map.swampsize = secfile_lookup_int(file, "map.swampsize");
-      map.deserts = secfile_lookup_int(file, "map.deserts");
-      map.riverlength = secfile_lookup_int(file, "map.riverlength");
-      map.mountains = secfile_lookup_int(file, "map.mountains");
-      map.forestsize = secfile_lookup_int(file, "map.forestsize");
       map.have_huts = secfile_lookup_bool_default(file, TRUE,
                                                  "map.have_huts");
       map.alltemperate
        = secfile_lookup_bool_default(file, MAP_DEFAULT_ALLTEMPERATE,
                                      "map.alltemperate");
-      map.tinyisles
-       = secfile_lookup_bool_default(file, MAP_DEFAULT_TINYISLES,
-                                     "map.tinyisles");
       map.separatepoles
        = secfile_lookup_bool_default(file, MAP_DEFAULT_SEPARATE_POLES,
                                      "map.separatepoles");
@@ -3475,16 +3465,16 @@
     secfile_insert_int(file, map.seed, "map.seed");
     secfile_insert_int(file, map.landpercent, "map.landpercent");
     secfile_insert_int(file, map.riches, "map.riches");
-    secfile_insert_int(file, map.swampsize, "map.swampsize");
-    secfile_insert_int(file, map.deserts, "map.deserts");
+    secfile_insert_int(file, 5, "map.swampsize");
+    secfile_insert_int(file, 5, "map.deserts");
     secfile_insert_int(file, map.riverlength, "map.riverlength");
-    secfile_insert_int(file, map.mountains, "map.mountains");
-    secfile_insert_int(file, map.forestsize, "map.forestsize");
+    secfile_insert_int(file, 30, "map.mountains");
+    secfile_insert_int(file, 20, "map.forestsize");
     secfile_insert_int(file, map.huts, "map.huts");
     secfile_insert_int(file, map.generator, "map.generator");
     secfile_insert_bool(file, map.have_huts, "map.have_huts");
     secfile_insert_bool(file, map.alltemperate, "map.alltemperate");
-    secfile_insert_bool(file, map.tinyisles, "map.tinyisles");
+    secfile_insert_bool(file, TRUE, "map.tinyisles");
     secfile_insert_bool(file, map.separatepoles, "map.separatepoles");
   } 
 
Index: server/settings.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/settings.c,v
retrieving revision 1.2
diff -u -r1.2 settings.c
--- server/settings.c   4 Sep 2004 20:36:10 -0000       1.2
+++ server/settings.c   9 Sep 2004 07:03:50 -0000
@@ -274,12 +274,6 @@
             "(Zero indicates a scenario map.)"), NULL,
          MAP_MIN_GENERATOR, MAP_MAX_GENERATOR, MAP_DEFAULT_GENERATOR)
 
-  GEN_BOOL("tinyisles", map.tinyisles,
-          SSET_MAP_GEN, SSET_GEOLOGY, SSET_RARE, SSET_TO_CLIENT,
-          N_("Presence of 1x1 islands"),
-          N_("0 = no 1x1 islands; 1 = some 1x1 islands"), NULL,
-          MAP_DEFAULT_TINYISLES)
-
   GEN_BOOL("separatepoles", map.separatepoles,
           SSET_MAP_GEN, SSET_GEOLOGY, SSET_SITUATIONAL, SSET_TO_CLIENT,
           N_("Whether the poles are separate continents"),
@@ -298,38 +292,11 @@
          N_("Amount of land vs ocean"), "", NULL,
          MAP_MIN_LANDMASS, MAP_MAX_LANDMASS, MAP_DEFAULT_LANDMASS)
 
-  GEN_INT("mountains", map.mountains,
-         SSET_MAP_GEN, SSET_GEOLOGY, SSET_SITUATIONAL, SSET_TO_CLIENT,
-         N_("Amount of hills/mountains"),
-         N_("Small values give flat maps, higher values give more "
-            "hills and mountains."), NULL,
-         MAP_MIN_MOUNTAINS, MAP_MAX_MOUNTAINS, MAP_DEFAULT_MOUNTAINS)
-
   GEN_INT("rivers", map.riverlength,
          SSET_MAP_GEN, SSET_GEOLOGY, SSET_SITUATIONAL, SSET_TO_CLIENT,
          N_("Amount of river squares"), "", NULL,
          MAP_MIN_RIVERS, MAP_MAX_RIVERS, MAP_DEFAULT_RIVERS)
 
-  GEN_INT("grass", map.grasssize,
-         SSET_MAP_GEN, SSET_ECOLOGY, SSET_SITUATIONAL, SSET_TO_CLIENT,
-         N_("Amount of grass squares"), "", NULL,
-         MAP_MIN_GRASS, MAP_MAX_GRASS, MAP_DEFAULT_GRASS)
-
-  GEN_INT("forests", map.forestsize,
-         SSET_MAP_GEN, SSET_ECOLOGY, SSET_SITUATIONAL, SSET_TO_CLIENT,
-         N_("Amount of forest squares"), "", NULL, 
-         MAP_MIN_FORESTS, MAP_MAX_FORESTS, MAP_DEFAULT_FORESTS)
-
-  GEN_INT("swamps", map.swampsize,
-         SSET_MAP_GEN, SSET_ECOLOGY, SSET_SITUATIONAL, SSET_TO_CLIENT,
-         N_("Amount of swamp squares"), "", NULL, 
-         MAP_MIN_SWAMPS, MAP_MAX_SWAMPS, MAP_DEFAULT_SWAMPS)
-
-  GEN_INT("deserts", map.deserts,
-         SSET_MAP_GEN, SSET_ECOLOGY, SSET_SITUATIONAL, SSET_TO_CLIENT,
-         N_("Amount of desert squares"), "", NULL, 
-         MAP_MIN_DESERTS, MAP_MAX_DESERTS, MAP_DEFAULT_DESERTS)
-
   GEN_INT("seed", map.seed,
          SSET_MAP_GEN, SSET_INTERNAL, SSET_RARE, SSET_SERVER_ONLY,
          N_("Map generation random seed"),
Index: server/srv_main.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/srv_main.c,v
retrieving revision 1.187
diff -u -r1.187 srv_main.c
--- server/srv_main.c   6 Sep 2004 17:13:07 -0000       1.187
+++ server/srv_main.c   9 Sep 2004 07:03:51 -0000
@@ -1717,7 +1717,7 @@
   /* if we have a tile map, and map.generator==0, call map_fractal_generate
      anyway, to make the specials and huts */
   if (map_is_empty() || (map.generator == 0 && game.is_new_game)) {
-    map_fractal_generate(TRUE);
+    map_generate(TRUE);
   }
 
   /*
/********************************************************************** 
 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.
***********************************************************************/

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include "fcintl.h"
#include "game.h"
#include "log.h"
#include "map.h"
#include "maphand.h" /* assign_continent_numbers(), MAP_NCONT */
#include "mem.h"
#include "rand.h"
#include "shared.h"
#include "srv_main.h"
#include "support.h"

#include "mapgen.h"

/* Provide a block to convert from native to map coordinates.  For instance
 *   do_in_map_pos(mx, my, xn, yn) {
 *     map_set_terrain(mx, my, T_OCEAN);
 *   } do_in_map_pos_end;
 * Note that changing the value of the map coordinates won't change the native
 * coordinates.
 */
#define do_in_map_pos(map_x, map_y, nat_x, nat_y)                           \
{                                                                           \
  int map_x, map_y;                                                         \
  NATIVE_TO_MAP_POS(&map_x, &map_y, nat_x, nat_y);                          \
  {                                                                         \

#define do_in_map_pos_end                                                   \
  }                                                                         \
}

struct isledata {
  int goodies;
  int starters;
};
static struct isledata *islands;

static void generate_hmap_1(double *hmap);
static void generate_hmap_234(double *hmap);
static void generate_hmap_5(double *hmap);
static void generate_tmap(const double *hmap, double *tmap);
static void generate_mmap(const double *hmap, const double *tmap,
                          double *mmap);

struct {
  void (*generate_hmap)(double *hmap);
  void (*generate_tmap)(const double *hmap, double *tmap);
  void (*generate_mmap)(const double *hmap, const double *tmap,
                        double *mmap);
} generators[] = {
  {NULL, NULL, NULL},
  {generate_hmap_1, generate_tmap, generate_mmap},
  {generate_hmap_234, generate_tmap, generate_mmap},
  {generate_hmap_234, generate_tmap, generate_mmap},
  {generate_hmap_234, generate_tmap, generate_mmap},
  {generate_hmap_5, generate_tmap, generate_mmap},
};

static void print_xmap(const char *name, const double *xmap)
{
  int nat_x, nat_y;

  freelog(LOG_NORMAL, "%s map:", name);

  for (nat_y = 0; nat_y < map.ysize; nat_y++) {
    char line[4096] = "";

    for (nat_x = 0; nat_x < map.xsize; nat_x++) {
      int index = native_pos_to_index(nat_x, nat_y);

      if (xmap[index] < 0) {
        my_snprintf(line + strlen(line), sizeof(line) - strlen(line), "*");
      } else {
        int value = CLIP(0, xmap[index] * 10, 9);

        my_snprintf(line + strlen(line), sizeof(line) - strlen(line),
                    "%d", value);
      }
    }
    freelog(LOG_NORMAL, "  %s", line);
  }
}

/****************************************************************************
  Returns the lattitude of this map position.  This is a value in the
  range of [0,1].  0 is on the pole, 1 on the equator.
****************************************************************************/
static double map_lattitude(int map_x, int map_y)
{
  double x, y;
  
  if (map.alltemperate) {
    /* An all-temperate map has "average" temperature everywhere.
     *
     * TODO: perhaps there should be a random temperature variation. */
    return 0.5;
  }

  do_in_natural_pos(ntl_x, ntl_y, map_x, map_y) {
    if (!topo_has_flag(TF_WRAPX) && !topo_has_flag(TF_WRAPY)) {
      /* A FLAT (unwrapped) map 
       *
       * We assume this is a partial planetary map.  A polar zone is placed
       * at the top and the equator is at the bottom.  The user can specify
       * all-temperate to avoid this. */
      return (double)ntl_y / (double)(NATURAL_HEIGHT - 1);
    }

    /* Otherwise a wrapping map is assumed to be a global planetary map. */

    /* we fold the map to get the base symetries
     *
     * ...... 
     * :c__c:
     * :____:
     * :____:
     * :c__c:
     * ......
     *
     * C are the corners.  In all cases the 4 corners have equal temperature.
     * So we fold the map over in both directions and determine
     * x and y vars in the range [0.0, 1.0].
     *
     * ...>x 
     * :C_
     * :__
     * V
     * y
     *
     * And now this is what we have - just one-quarter of the map.
     */
    x = ((ntl_x > (NATURAL_WIDTH / 2 - 1)
          ? (double)NATURAL_WIDTH - 1.0 - (double)ntl_x
          : (double)ntl_x)
         / ((double)NATURAL_WIDTH / 2.0 - 1.0));
    y = ((ntl_y > (NATURAL_HEIGHT / 2 - 1)
          ? (double)NATURAL_HEIGHT - 1.0 - (double)ntl_y
          : (double)ntl_y)
         / ((double)NATURAL_HEIGHT / 2.0 - 1.0));
  } do_in_natural_pos_end;

  if (topo_has_flag(TF_WRAPX) && !topo_has_flag(TF_WRAPY)) {
    /* In an Earth-like topology the polar zones are at north and south.
     * This is equivalent to a Mercator projection. */
    return y;
  }
  
  if (!topo_has_flag(TF_WRAPX) && topo_has_flag(TF_WRAPY)) {
    /* In a Uranus-like topology the polar zones are at east and west.
     * This isn't really the way Uranus is; it's the way Earth would look
     * if you tilted your head sideways.  It's still a Mercator
     * projection. */
    return x;
  }

  /* Otherwise we have a torus topology.  We set it up as an approximation
   * of a sphere with two circular polar zones and a square equatorial
   * zone.  In this case north and south are not constant directions on the
   * map because we have to use a more complicated (custom) projection.
   *
   * Generators 2 and 5 work best if the center of the map is free.  So
   * we want to set up the map with the poles (N,S) along the sides and the
   * equator (/,\) in between.
   *
   * ........
   * :\ NN /:
   * : \  / :
   * :S \/ S:
   * :S /\ S:
   * : /  \ :
   * :/ NN \:
   * ''''''''
   */

  /* Remember that we've already folded the map into fourths:
   *
   * ....
   * :\ N
   * : \ 
   * :S \
   *
   * Now flip it along the X direction to get this:
   *
   * ....
   * :N /
   * : / 
   * :/ S
   */
  x = 1.0 - x;

  /* Since the north and south poles are equivalent, we can fold along the
   * diagonal.  This leaves us with 1/8 of the map
   *
   * .....
   * :P /
   * : / 
   * :/
   *
   * where P is the polar regions and / is the equator. */
  if (x + y > 1.0) {
    x = 1.0 - x;
    y = 1.0 - y;
  }

  /* This projection makes poles with a shape of a quarter-circle along
   * "P" and the equator as a straight line along "/".
   *
   * This is explained more fully at
   * http://rt.freeciv.org/Ticket/Display.html?id=8624. */
  return (1.5 * (x * x * y + x * y * y) 
          - 0.5 * (x * x * x + y * y * y) 
          + 1.5 * (x * x + y * y));
}

/****************************************************************************
  Generate the temperature map.  This is done after the height map is placed.
  The resulting temp is in [0,1].
****************************************************************************/
static void generate_tmap(const double *hmap, double *tmap)
{
  whole_map_iterate_index(index, x, y) {
    int nat_x, nat_y;

    MAP_TO_NATIVE_POS(&nat_x, &nat_y, x, y);
    tmap[index] = map_lattitude(x, y);
    if (hmap[index] > 0) {
      /* Adjust for height? */
      tmap[index] -= hmap[index] / 10;
    } else {
      /* Deeper ocean is more temperate. */
      double weight = -hmap[index] / 2;

      tmap[index] = tmap[index] * (1.0 - weight) + 0.5 * weight;
    }
    tmap[index] = CLIP(0, tmap[index], 1.0);
  } whole_map_iterate_index_end;
}

/****************************************************************************
  Generate the moisture map.  This is done after the hmap and tmap.
  The resulting temp is in [0,1].
****************************************************************************/
static void generate_mmap(const double *hmap, const double *tmap,
                          double *mmap)
{
  int i;
  double new_mmap[MAX_MAP_INDEX];

  whole_map_iterate_index(index, x, y) {
    mmap[index] = 0;
  } whole_map_iterate_index_end;

  for (i = 0; i < 100; i++) {
    whole_map_iterate_index(index, x, y) {
      double evaporation = 0;

      if (hmap[index] < 0) {
        /* Evaporation from ocean. */
        evaporation = 0.5 + tmap[index] / 2;
      }
      if (map_has_special(x, y, S_RIVER)) {
        evaporation = 0.25 + tmap[index] / 4;
      }
      new_mmap[index] = tmap[index] * 0.5 + evaporation * 0.5;
    } whole_map_iterate_index_end;

    whole_map_iterate_index(index, x, y) {
      mmap[index] = 0;
    } whole_map_iterate_index_end;

    whole_map_iterate_index(index, x, y) {
      double count = 2.0;

      adjc_iterate(x, y, x1, y1) {
        double this = 1.0 - MAX(hmap[map_pos_to_index(x1, y1)], 0);

        count += this;
      } adjc_iterate_end;

      mmap[index] = 2.0 * new_mmap[index] / count;
      adjc_iterate(x, y, x1, y1) {
        double this = 1.0 - MAX(hmap[map_pos_to_index(x1, y1)], 0);

        mmap[map_pos_to_index(x1, y1)] += new_mmap[index] * this / count;
      } adjc_iterate_end;
    } whole_map_iterate_index_end;

    whole_map_iterate_index(index, x, y) {
      double lattitude = map_lattitude(x, y);

      if (lattitude > 0.65 && lattitude < 0.85) {
        mmap[index] -= (0.1 - abs(0.75 - lattitude)) * 3.0;
        mmap[index] = CLIP(0, mmap[index], 1.0);
      }
    } whole_map_iterate_index_end;
  }
}

/****************************************************************************
  Return TRUE if the map in a city radius is SINGULAR.  This is used to
  avoid putting (non-polar) land near the edge of the map.
****************************************************************************/
static bool near_singularity(int map_x, int map_y)
{
  return is_singular_map_pos(map_x, map_y, CITY_MAP_RADIUS);
}

#define RIVERS_MAXTRIES 32767
enum river_map_flag {
  RS_BLOCKED, RS_RIVER, RS_COUNT
};
BV_DEFINE(river_map_type, RS_COUNT);

/*********************************************************************
 Help function used in make_river(). See the help there.
*********************************************************************/
static int river_test_blocked(int x, int y,
                              const double *hmap, river_map_type *rmap)
{
  if (BV_ISSET(rmap[map_pos_to_index(x, y)], RS_BLOCKED)) {
    return 1;
  }

  /* any un-blocked? */
  cardinal_adjc_iterate(x, y, x1, y1) {
    if (!BV_ISSET(rmap[map_pos_to_index(x1, y1)], RS_BLOCKED)) {
      return 0;
    }
  } cardinal_adjc_iterate_end;

  return 1; /* none non-blocked |- all blocked */
}

/*********************************************************************
 Help function used in make_river(). See the help there.
*********************************************************************/
static int river_test_rivergrid(int x, int y,
                                const double *hmap, river_map_type *rmap)
{
  return (count_special_near_tile(x, y, TRUE, S_RIVER) > 1) ? 1 : 0;
}

/*********************************************************************
 Help function used in make_river(). See the help there.
*********************************************************************/
static int river_test_height(int x, int y,
                             const double *hmap, river_map_type *rmap)
{
  return 1000 * hmap[map_pos_to_index(x, y)];
}

/*********************************************************************
 Help function used in make_river(). See the help there.
*********************************************************************/
static int river_test_adjacent_ocean(int x, int y, const double *hmap,
                                     river_map_type *rmap)
{
  int count;

  /* Terrains aren't placed yet; all we know is the hmap. */
  cardinal_adjc_iterate(x, y, x1, y1) {
    if (hmap[map_pos_to_index(x1, y1)] < 0) {
      count++;
    }
  } cardinal_adjc_iterate_end;

  return 6 - count;
}

/*********************************************************************
 Help function used in make_river(). See the help there.
*********************************************************************/
static int river_test_adjacent_river(int x, int y, const double *hmap,
                                     river_map_type *rmap)
{
  /* This number must always be >= 0.  6 is the maximum number of
   * cardinal directions. */
  return 6 - count_special_near_tile(x, y, TRUE, S_RIVER);
}

/*********************************************************************
 Help function used in make_river(). See the help there.
*********************************************************************/
static int river_test_adjacent_height(int x, int y, const double *hmap,
                                      river_map_type *rmap)
{
  int count = 0;
  double height = 0.0;

  cardinal_adjc_iterate(x, y, x2, y2) {
    height += hmap[map_pos_to_index(x2, y2)];
    count++;
  } cardinal_adjc_iterate_end;

  return 1000 * height / count;
}

/*********************************************************************
 Called from make_river. Marks all directions as blocked.  -Erik Sigra
*********************************************************************/
static void river_blockmark(int x, int y, river_map_type *rmap)
{
  freelog(LOG_DEBUG, "Blockmarking (%d, %d) and adjacent tiles.",
          x, y);

  BV_SET(rmap[map_pos_to_index(x, y)], RS_BLOCKED);

  cardinal_adjc_iterate(x, y, x1, y1) {
    BV_SET(rmap[map_pos_to_index(x1, y1)], RS_BLOCKED);
  } cardinal_adjc_iterate_end;
}

struct test_func {
  int (*func)(int x, int y, const double *hmap, river_map_type *rmap);
  bool fatal;
};


static struct test_func test_funcs[] = {
  {river_test_blocked,            TRUE},
  {river_test_rivergrid,          TRUE},
  {river_test_height,             FALSE},
  {river_test_adjacent_ocean,     FALSE},
  {river_test_adjacent_river,     FALSE},
  {river_test_adjacent_height,    FALSE}
};

static int count_underwater_near_tile(int x, int y, const double *hmap)
{
  int count = 0;

  cardinal_adjc_iterate(x, y, x1, y1) {
    if (hmap[map_pos_to_index(x1, y1)] < 0) {
      count++;
    }
  } cardinal_adjc_iterate_end;

  return count;
}

/********************************************************************
 Makes a river starting at (x, y). Returns 1 if it succeeds.
 Return 0 if it fails. The river is stored in river_map.
 
 How to make a river path look natural
 =====================================
 Rivers always flow down. Thus rivers are best implemented on maps
 where every tile has an explicit height value. However, Freeciv has a
 flat map. But there are certain things that help the user imagine
 differences in height between tiles. The selection of direction for
 rivers should confirm and even amplify the user's image of the map's
 topology.
 
 To decide which direction the river takes, the possible directions
 are tested in a series of test until there is only 1 direction
 left. Some tests are fatal. This means that they can sort away all
 remaining directions. If they do so, the river is aborted. Here
 follows a description of the test series.
 
 * Falling into itself: fatal
     (river_test_blocked)
     This is tested by looking up in the river_map array if a tile or
     every tile surrounding the tile is marked as blocked. A tile is
     marked as blocked if it belongs to the current river or has been
     evaluated in a previous iteration in the creation of the current
     river.
     
     Possible values:
     0: Is not falling into itself.
     1: Is falling into itself.
     
 * Forming a 4-river-grid: optionally fatal
     (river_test_rivergrid)
     A minimal 4-river-grid is formed when an intersection in the map
     grid is surrounded by 4 river tiles. There can be larger river
     grids consisting of several overlapping minimal 4-river-grids.
     
     Possible values:
     0: Is not forming a 4-river-grid.
     1: Is forming a 4-river-grid.

 * Highlands:
     (river_test_highlands)
     Rivers must not flow up in mountains or hills if there are
     alternatives.
     
     Possible values:
     0: Is not hills and not mountains.
     1: Is hills.
     2: Is mountains.

 * Adjacent ocean:
     (river_test_adjacent_ocean)
     Rivers must flow down to coastal areas when possible:

     Possible values:
     n: 4 - adjacent_terrain_tiles4(...)

 * Adjacent river:
     (river_test_adjacent_river)
     Rivers must flow down to areas near other rivers when possible:

     Possible values:
     n: 6 - count_river_near_tile(...) (should be small after the
                                        river-grid test)
                                        
 * Adjacent highlands:
     (river_test_adjacent_highlands)
     Rivers must not flow towards highlands if there are alternatives. 
     
 * Swamps:
     (river_test_swamp)
     Rivers must flow down in swamps when possible.
     
     Possible values:
     0: Is swamps.
     1: Is not swamps.
     
 * Adjacent swamps:
     (river_test_adjacent_swamp)
     Rivers must flow towards swamps when possible.

 * height_map:
     (river_test_height_map)
     Rivers must flow in the direction which takes it to the tile with
     the lowest value on the height_map.
     
     Possible values:
     n: height_map[...]
     
 If these rules haven't decided the direction, the random number
 generator gets the desicion.                              -Erik Sigra
*********************************************************************/
static bool make_river(int x, int y, const double *hmap, const double *tmap,
                       river_map_type *rmap)
{
  /* Comparison value for each tile surrounding the current tile.  It is
   * the suitability to continue a river to the tile in that direction;
   * lower is better.  However rivers may only run in cardinal directions;
   * the other directions are ignored entirely. */
  int rd_comparison_val[8];

  int index = map_pos_to_index(x, y);
  bool rd_direction_is_valid[8];
  int num_valid_directions, func_num, direction;

  while (TRUE) {
    /* Mark the current tile as river. */
    BV_SET(rmap[index], RS_RIVER);
    freelog(LOG_DEBUG,
            "The tile at (%d, %d) has been marked as river in river_map.\n",
            x, y);

    /* Test if the river is done. */
    /* We arbitrarily make rivers end at the poles. */
    if (count_special_near_tile(x, y, TRUE, S_RIVER) != 0
        || count_underwater_near_tile(x, y, hmap) != 0
        || tmap[index] < 0.1) { 

      freelog(LOG_DEBUG,
              "The river ended at (%d, %d).\n", x, y);
      return TRUE;
    }

    /* Else choose a direction to continue the river. */
    freelog(LOG_DEBUG,
            "The river did not end at (%d, %d). Evaluating directions...\n",
            x, y);

    /* Mark all available cardinal directions as available. */
    memset(rd_direction_is_valid, 0, sizeof(rd_direction_is_valid));
    cardinal_adjc_dir_iterate(x, y, x1, y1, dir) {
      rd_direction_is_valid[dir] = TRUE;
    } cardinal_adjc_dir_iterate_end;

    /* Test series that selects a direction for the river. */
    for (func_num = 0; func_num < ARRAY_SIZE(test_funcs); func_num++) {
      int best_val = -1;

      /* first get the tile values for the function */
      cardinal_adjc_dir_iterate(x, y, x1, y1, dir) {
        if (rd_direction_is_valid[dir]) {
          rd_comparison_val[dir]
            = (test_funcs[func_num].func) (x1, y1, tmap, rmap);
          if (best_val == -1) {
            best_val = rd_comparison_val[dir];
          } else {
            best_val = MIN(rd_comparison_val[dir], best_val);
          }
        }
      } cardinal_adjc_dir_iterate_end;

      if (best_val < 0) {
        /* This can happen if most directions are invalidated by a
         * previous filter. */
        return FALSE;
      }

      /* should we abort? */
      if (best_val > 0 && test_funcs[func_num].fatal) {
        return FALSE;
      }

      /* mark the less attractive directions as invalid */
      cardinal_adjc_dir_iterate(x, y, x1, y1, dir) {
        if (rd_direction_is_valid[dir]) {
          if (rd_comparison_val[dir] != best_val) {
            rd_direction_is_valid[dir] = FALSE;
          }
        }
      } cardinal_adjc_dir_iterate_end;
    }

    /* Directions evaluated with all functions. Now choose the best
       direction before going to the next iteration of the while loop */
    num_valid_directions = 0;
    cardinal_adjc_dir_iterate(x, y, x1, y1, dir) {
      if (rd_direction_is_valid[dir]) {
        num_valid_directions++;
      }
    } cardinal_adjc_dir_iterate_end;

    if (num_valid_directions == 0) {
      return FALSE; /* river aborted */
    }

    /* One or more valid directions: choose randomly. */
    freelog(LOG_DEBUG, "mapgen.c: Had to let the random number"
            " generator select a direction for a river.");
    direction = myrand(num_valid_directions);
    freelog(LOG_DEBUG, "mapgen.c: direction: %d", direction);

    /* Find the direction that the random number generator selected. */
    cardinal_adjc_dir_iterate(x, y, x1, y1, dir) {
      if (rd_direction_is_valid[dir]) {
        if (direction > 0) {
          direction--;
        } else {
          river_blockmark(x, y, rmap);
          x = x1;
          y = y1;
          break;
        }
      }
    } cardinal_adjc_dir_iterate_end;
    assert(direction == 0);

  } /* end while; (Make a river.) */
}

static double find_lowest_adjacent_height(int x, int y, const double *hmap)
{
  double height = 1.0;

  cardinal_adjc_iterate(x, y, x1, y1) {
    height = MIN(height, hmap[map_pos_to_index(x1, y1)]);
  } cardinal_adjc_iterate_end;

  return height;
}

/**************************************************************************
  Calls make_river until there are enough river tiles on the map. It stops
  when it has tried to create RIVERS_MAXTRIES rivers.           -Erik Sigra
**************************************************************************/
static void make_rivers(const double *hmap, const double *tmap)
{
  int x, y, index; /* The coordinates. */
  river_map_type rmap[MAX_MAP_INDEX];

  /* Formula to make the river density similar om different sized maps. Avoids
     too few rivers on large maps and too many rivers on small maps. */
  int desirable_riverlength =
    map.riverlength *
    /* This 10 is a conversion factor to take into account the fact that this
     * river code was written when map.riverlength had a maximum value of 
     * 1000 rather than the current 100 */
    10 *
    /* The size of the map (poles don't count). */
    map_num_tiles() * (map.alltemperate ? 1.0 : 0.90) *
    /* Rivers need to be on land only. */
    map.landpercent /
    /* Adjustment value. Tested by me. Gives no rivers with 'set
       rivers 0', gives a reasonable amount of rivers with default
       settings and as many rivers as possible with 'set rivers 100'. */
    0xD000; /* (= 53248 in decimal) */

  /* The number of river tiles that have been set. */
  int current_riverlength = 0;

  int i; /* Loop variable. */

  /* Counts the number of iterations (should increase with 1 during
     every iteration of the main loop in this function).
     Is needed to stop a potentially infinite loop. */
  int iteration_counter = 0;

  /* The main loop in this function. */
  while (current_riverlength < desirable_riverlength
         && iteration_counter < RIVERS_MAXTRIES) {

    rand_map_pos(&x, &y);
    index = map_pos_to_index(x, y);

    /* Check if it is suitable to start a river on the current tile.
     */
    if (
        /* Don't start a river on ocean. */
        hmap[index] > 0

        /* Don't start a river on river. */
        && !map_has_special(x, y, S_RIVER)

        /* Don't start a river on a tile is surrounded by > 1 river +
           ocean tile. */
        && (count_special_near_tile(x, y, TRUE, S_RIVER)
            + count_underwater_near_tile(x, y, hmap) <= 1)

        /* Don't start a river on a tile that is surrounded by hills or
           mountains unless it is hard to find somewhere else to start
           it. */
        && (hmap[index] > find_lowest_adjacent_height(x, y, hmap)
            || iteration_counter >= RIVERS_MAXTRIES / 2)

        /* Don't start a river on hills unless it is hard to find
           somewhere else to start it. */
        && (hmap[index] < 0.6
            || iteration_counter >= RIVERS_MAXTRIES / 10 * 6)

        /* Don't start a river on mountains unless it is hard to find
           somewhere else to start it. */
        && (hmap[index] < 0.8
            || iteration_counter >= RIVERS_MAXTRIES / 10 * 7)

        /* Don't start a river on arctic unless it is hard to find
           somewhere else to start it. */
        && (tmap[index] > 0.2
            || iteration_counter >= RIVERS_MAXTRIES / 10 * 8)) {

      /* Reset river_map before making a new river. */
      for (i = 0; i < MAX_MAP_INDEX; i++) {
        BV_CLR_ALL(rmap[i]);
      }

      freelog(LOG_DEBUG,
              "Found a suitable starting tile for a river at (%d, %d)."
              " Starting to make it.",
              x, y);

      /* Try to make a river. If it is OK, apply it to the map. */
      if (make_river(x, y, hmap, tmap, rmap)) {
        whole_map_iterate(x1, y1) {
          if (BV_ISSET(rmap[map_pos_to_index(x1, y1)], RS_RIVER)) {
            map_set_special(x1, y1, S_RIVER);
            current_riverlength++;
            freelog(LOG_DEBUG, "Applied a river to (%d, %d).", x1, y1);
          }
        } whole_map_iterate_end;
      }
      else {
        freelog(LOG_DEBUG,
                "mapgen.c: A river failed. It might have gotten stuck in a 
helix.");
      }
    } /* end if; */
    iteration_counter++;
    freelog(LOG_DEBUG,
            "current_riverlength: %d; desirable_riverlength: %d; 
iteration_counter: %d",
            current_riverlength, desirable_riverlength, iteration_counter);
  } /* end while; */
}

/**************************************************************************
  Place terrain based on a generated heightmap, temperaturemap, and
  moisturemap.
**************************************************************************/
static void place_terrain(const double *hmap, const double *tmap,
                          const double *mmap)
{
  int totals[T_COUNT];

  memset(totals, 0, sizeof(totals));

#define RANDOM_PLACEMENT
  whole_map_iterate_index(index, x, y) {
    double likelihoods[T_COUNT], sum = 0;
    Terrain_type_id best = T_NONE;
    double best_likelihood;
#ifdef RANDOM_PLACEMENT
    int ilikelihoods[T_COUNT], isum = 0; /* Same thing, but integer. */
#endif

    freelog(LOG_DEBUG, "Placing terrain at %d,%d.", x, y);
    freelog(LOG_DEBUG, "  Values are %f, %f, %f.",
            hmap[index], tmap[index], mmap[index]);

    terrain_type_iterate(t) {
      struct tile_type *ptype = get_tile_type(t);
      double likelihood = ptype->likelihood;
      double height_exp = ((100.0 * hmap[index] - ptype->height_avg)
                           / ptype->height_dev);
      double temp_exp = ((100.0 * tmap[index] - ptype->temp_avg)
                         / ptype->temp_dev);
      double moisture_exp = ((100.0 * mmap[index] - ptype->moisture_avg)
                             / ptype->moisture_dev);

      likelihoods[t] = 0;
      if (is_ocean(t) && hmap[index] > 0) {
        continue;
      }
      if (!is_ocean(t) && hmap[index] < 0) {
        continue;
      }

      likelihood /= exp(height_exp * height_exp);
      likelihood /= exp(temp_exp * temp_exp);
      likelihood /= exp(moisture_exp * moisture_exp);

      likelihoods[t] = likelihood;
      assert(likelihoods[t] >= 0);
      sum += likelihoods[t];

      if (best == T_NONE || likelihood > best_likelihood) {
        best = t;
        best_likelihood = likelihood;
      }

      freelog(LOG_DEBUG, "  Goodness of %s is %f (%f/%f/%f).",
              get_terrain_name(t), likelihood,
              height_exp, temp_exp, moisture_exp);
    } terrain_type_iterate_end;

#ifdef RANDOM_PLACEMENT
    assert(sum > 0);

    /* Scale to 1000 so we can use myrand with integers. */
    terrain_type_iterate(t) {
      ilikelihoods[t] = likelihoods[t] * 1000.0 / sum;
      isum += ilikelihoods[t];
    } terrain_type_iterate_end;

    isum = myrand(isum);

    terrain_type_iterate(t) {
      isum -= ilikelihoods[t];
      if (isum < 0) {
        map_set_terrain(x, y, t);
        totals[t]++;
        freelog(LOG_DEBUG, "  Placing %s.", get_terrain_name(t));
        break;
      }
    } terrain_type_iterate_end;
    assert(isum < 0);
#else
    assert(best != T_NONE);
    map_set_terrain(x, y, best);
    totals[best]++;
    freelog(LOG_DEBUG, "  Placing %s.", get_terrain_name(best));
#endif
  } whole_map_iterate_end;

  terrain_type_iterate(t) {
    freelog(LOG_NORMAL, "Placed %5d %s (%d%%).",
            totals[t], get_terrain_name(t),
            totals[t] * 100 / MAX_MAP_INDEX);
  } terrain_type_iterate_end;

  assign_continent_numbers();
}

/****************************************************************************
  Return an approximation of the goodness of a tile to a civilization.
****************************************************************************/
static int get_tile_value(int x, int y)
{
  struct tile *ptile = map_get_tile(x, y);
  Terrain_type_id old_terrain;
  enum tile_special_type old_special;
  int value, irrig_bonus, mine_bonus;

  /* Give one point for each food / shield / trade produced. */
  value = (get_food_tile(x, y)
           + get_shields_tile(x, y)
           + get_trade_tile(x, y));

  old_terrain = ptile->terrain;
  old_special = ptile->special;

  map_set_special(x, y, S_ROAD);
  map_irrigate_tile(x, y);
  irrig_bonus = (get_food_tile(x, y)
                 + get_shields_tile(x, y)
                 + get_trade_tile(x, y)) - value;

  ptile->terrain = old_terrain;
  ptile->special = old_special;
  map_set_special(x, y, S_ROAD);
  map_mine_tile(x, y);
  mine_bonus = (get_food_tile(x, y)
                 + get_shields_tile(x, y)
                 + get_trade_tile(x, y)) - value;

  ptile->terrain = old_terrain;
  ptile->special = old_special;

  value += MAX(0, MAX(mine_bonus, irrig_bonus)) / 2;

  return value;
}

/**************************************************************************
 Allocate islands array and fill in values.
 Note this is only used for map.generator <= 1 or >= 5, since others
 setups islands and starters explicitly.
**************************************************************************/
static void setup_isledata(void)
{
  int starters = 0;
  int min,  i;
  
  assert(map.num_continents > 0);
  
  /* allocate + 1 so can use continent number as index */
  islands = fc_calloc((map.num_continents + 1), sizeof(struct isledata));

  /* add up all the resources of the map */
  whole_map_iterate(x, y) {
    /* number of different continents seen from (x,y) */
    int seen_conts = 0;
    /* list of seen continents */
    Continent_id conts[CITY_TILES]; 
    int j;
    
    /* add tile's value to each continent that is within city 
     * radius distance */
    map_city_radius_iterate(x, y, x1, y1) {
      Terrain_type_id t = map_get_terrain(x1, y1);

      /* (x1,y1) is possible location of a future city which will
       * be able to get benefit of the tile (x,y) */
      if (is_ocean(t) || !terrain_has_flag(t, TER_STARTER)) {
        /* Not land, or too cold. */
        continue;
      }
      for (j = 0; j < seen_conts; j++) {
        if (map_get_continent(x1, y1) == conts[j]) {
          /* Continent of (x1,y1) is already in the list */
          break;
        }
      }
      if (j >= seen_conts) { 
        /* we have not seen this continent yet */
        assert(seen_conts < CITY_TILES);
        conts[seen_conts] = map_get_continent(x1, y1);
        seen_conts++;
      }
    } map_city_radius_iterate_end;
    
    /* Now actually add the tile's value to all these continents */
    for (j = 0; j < seen_conts; j++) {
      islands[conts[j]].goodies += get_tile_value(x, y);
    }
  } whole_map_iterate_end;
  
  /* now divide the number of desired starting positions among
   * the continents so that the minimum number of resources per starting 
   * position is as large as possible */
  
  /* set minimum number of resources per starting position to be value of
   * the best continent */
  min = 0;
  for (i = 1; i <= map.num_continents; i++) {
    if (min < islands[i].goodies) {
      min = islands[i].goodies;
    }
  }
  
  /* place as many starting positions as possible with the current minumum
   * number of resources, if not enough are placed, decrease the minimum */
  while ((starters < game.nplayers) && (min > 0)) {
    int nextmin = 0;
    
    starters = 0;
    for (i = 1; i <= map.num_continents; i++) {
      int value = islands[i].goodies;
      
      starters += value / min;
      if (nextmin < (value / (value / min + 1))) {
        nextmin = value / (value / min + 1);
      }
    }
    
    freelog(LOG_VERBOSE,
            "%d starting positions allocated with\n"
            "at least %d resouces per starting position; \n",
            starters, min);

    assert(nextmin < min);
    /* This choice of next min guarantees that there will be at least 
     * one more starter on one of the continents */
    min = nextmin;
  }
  
  if (min == 0) {
    freelog(LOG_VERBOSE,
            "If we continue some starting positions will have to have "
            "access to zero resources (as defined in get_tile_value). \n");
    freelog(LOG_FATAL,
            "Cannot create enough starting position and will abort.\n"
            "Please report this bug at " WEBSITE_URL);
    abort();
  } else {
    for (i = 1; i <= map.num_continents; i++) {
      islands[i].starters = islands[i].goodies / min;
    }
  }
}

struct start_filter_data {
  int count; /* Number of existing start positions. */
  int dist; /* Minimum distance between starting positions. */
};

/**************************************************************************
  Return TRUE if (x,y) is a good starting position.

  Bad places:
  - Islands with no room.
  - Non-suitable terrain;
  - On a hut;
  - Too close to another starter on the same continent:
    'dist' is too close (real_map_distance)
    'nr' is the number of other start positions in
    map.start_positions to check for too closeness.
**************************************************************************/
static bool is_valid_start_pos(int x, int y, void *dataptr)
{
  struct start_filter_data *data = dataptr;
  int i;
  Terrain_type_id t = map_get_terrain(x, y);

  if (is_ocean(map_get_terrain(x, y))) {
    return FALSE;
  }

  if (islands[(int)map_get_continent(x, y)].starters == 0) {
    return FALSE;
  }

  /* Only start on certain terrain types. */
  if (!terrain_has_flag(t, TER_STARTER)) {
    return FALSE;
  }
  
  /* Don't start on a hut. */
  if (map_has_special(x, y, S_HUT)) {
    return FALSE;
  }
  
  /* Nobody will start on the poles since they aren't valid terrain. */

  /* Don't start too close to someone else. */
  for (i = 0; i < data->count; i++) {
    int x1 = map.start_positions[i].x;
    int y1 = map.start_positions[i].y;

    if (map_get_continent(x, y) == map_get_continent(x1, y1)
        && real_map_distance(x, y, x1, y1) < data->dist) {
      return FALSE;
    }
  }

  return TRUE;
}

/**************************************************************************
  where do the different races start on the map? well this function tries
  to spread them out on the different islands.
**************************************************************************/
void create_start_positions(void)
{
  int x, y, k, sum;
  struct start_filter_data data;
  
  if (!islands) {
    /* Isle data is already setup for generators 2, 3, and 4. */
    setup_isledata();
  }

  data.dist = MIN(40, MIN(map.xsize / 2, map.ysize / 2));

  data.count = 0;
  sum = 0;
  for (k = 1; k <= map.num_continents; k++) {
    sum += islands[k].starters;
    if (islands[k].starters != 0) {
      freelog(LOG_VERBOSE, "starters on isle %i", k);
    }
  }
  assert(game.nplayers <= data.count + sum);

  map.start_positions = fc_realloc(map.start_positions,
                                   game.nplayers
                                   * sizeof(*map.start_positions));
  while (data.count < game.nplayers) {
    if (rand_map_pos_filtered(&x, &y, &data, is_valid_start_pos)) {
      islands[(int)map_get_continent(x, y)].starters--;
      map.start_positions[data.count].x = x;
      map.start_positions[data.count].y = y;
      map.start_positions[data.count].nation = NO_NATION_SELECTED;
      freelog(LOG_DEBUG, "Adding %d,%d as starting position %d.",
              x, y, data.count);
      data.count++;

    } else {
      
      data.dist--;
      if (data.dist == 0) {
        die(_("The server appears to have gotten into an infinite loop "
              "in the allocation of starting positions, and will abort.\n"
              "Maybe the numbers of players/ia is too much for this map.\n"
              "Please report this bug at %s."), WEBSITE_URL);
      }
    }
  }
  map.num_start_positions = game.nplayers;

  free(islands);
  islands = NULL;
}

/****************************************************************************
  Set the map xsize and ysize based on a base size and ratio (in natural
  coordinates).
****************************************************************************/
static void set_sizes(double size, int Xratio, int Yratio)
{
  /* Some code in generator assumes even dimension, so this is set to 2.
   * Future topologies may also require even dimensions. */
  const int even = 2;

  /* In iso-maps we need to double the map.ysize factor, since xsize is
   * in native coordinates which are compressed 2x in the X direction. */ 
  const int iso = (topo_has_flag(TF_ISO) || topo_has_flag(TF_HEX)) ? 2 : 1;

  /* We have:
   *
   *   1000 * size = xsize * ysize
   *
   * And to satisfy the ratios and other constraints we set
   *
   *   xsize = i_size * xratio * even
   *   ysize = i_size * yratio * even * iso
   *
   * For any value of "i_size".  So with some substitution
   *
   *   1000 * size = i_size * i_size * xratio * yratio * even * even * iso
   *   i_size = sqrt(1000 * size / (xratio * yratio * even * even * iso))
   * 
   * Make sure to round off i_size to preserve exact wanted ratios,
   * that may be importante for some topologies.
   */
  const int i_size
    = sqrt((double)(1000 * size)
           / (double)(Xratio * Yratio * iso * even * even)) + 0.49;

  /* Now build xsize and ysize value as described above. */
  map.xsize = Xratio * i_size * even;
  map.ysize = Yratio * i_size * even * iso;

  /* Now make sure the size isn't too large for this ratio.  If it is
   * then decrease the size and try again. */
  if (MAX(MAP_WIDTH, MAP_HEIGHT) > MAP_MAX_LINEAR_SIZE ) {
    assert(size > 0.1);
    set_sizes(size - 0.1, Xratio, Yratio);
    return;
  }

  /* If the ratio is too big for some topology the simplest way to avoid
   * this error is to set the maximum size smaller for all topologies! */
  if (map.size > size + 0.9) {
    /* Warning when size is set uselessly big */ 
    freelog(LOG_ERROR,
            "Requested size of %d is too big for this topology.",
            map.size);
  }
  freelog(LOG_VERBOSE,
          "Creating a map of size %d x %d = %d tiles (%d requested).",
          map.xsize, map.ysize, map.xsize * map.ysize, map.size * 1000);
}

/*
 * The default ratios for known topologies
 *
 * The factor Xratio * Yratio determines the accuracy of the size.
 * Small ratios work better than large ones; 3:2 is not the same as 6:4
 */
#define AUTO_RATIO_FLAT           {1, 1}
#define AUTO_RATIO_CLASSIC        {3, 2} 
#define AUTO_RATIO_URANUS         {2, 3} 
#define AUTO_RATIO_TORUS          {1, 1}

/*************************************************************************** 
  This function sets sizes in a topology-specific way then calls
  map_init_topology.
***************************************************************************/
static void generator_init_topology(bool autosize)
{
  /* The default server behavior is to generate xsize/ysize from the
   * "size" server option.  Others may want to set xsize/ysize directly. */
  if (autosize) {
    /* Changing or reordering the topo_flag enum will break this code. */
    const int default_ratios[4][2] =
      {AUTO_RATIO_FLAT, AUTO_RATIO_CLASSIC,
       AUTO_RATIO_URANUS, AUTO_RATIO_TORUS};
    const int id = 0x3 & map.topology_id;

    assert(TF_WRAPX == 0x1 && TF_WRAPY == 0x2);

    /* Set map.xsize and map.ysize based on map.size. */
    set_sizes(map.size, default_ratios[id][0], default_ratios[id][1]);
  }

  /* Then initialise all topoloicals parameters */
  map_init_topology(TRUE);
}

struct xmap_tile {
  int index;
  double value;
};

static int compare_xmap_tiles(const void *void_a, const void *void_b)
{
  const struct xmap_tile *a = void_a, *b = void_b;

  if (a->value > b->value) {
    return 1;
  } else if (a->value < b->value) {
    return -1;
  } else {
    /* Use an arbitrary but consistent ordering. */
    return a->index - b->index;
  }
}

/**************************************************************************
  Change the values of the map, so that they contain ranking of each 
  tile scaled to [0,1].  The distribution will be linear, so the lowest 20%
  of tiles will have values lower than 0.2.
**************************************************************************/
static void adjust_xmap(double *xmap)
{
  struct xmap_tile tiles[MAX_MAP_INDEX];
  int i, last_count = -1;
  double last;

  /* Fill out array. */
  whole_map_iterate_index(index, x, y) {
    tiles[index].index = index;
    tiles[index].value = xmap[index];
  } whole_map_iterate_end;

  /* Sort array. */
  qsort(tiles, MAX_MAP_INDEX, sizeof(*tiles), compare_xmap_tiles);

  /* Now rebuild the map, with a linear distribution. */
  for (i = 0; i < MAX_MAP_INDEX; i++) {
    if (last_count == -1 || tiles[i].value > last) {
      last_count = i;
      last = tiles[i].value;
    }
    xmap[tiles[i].index] = (double)last_count / (double)(MAX_MAP_INDEX - 1);
  }
}

static void create_oceans(double *hmap)
{
  double oceans = (double)(100 - map.landpercent) / 100.0;
  double landmass = 1.0 - oceans;

  whole_map_iterate_index(index, x, y) {
    if (hmap[index] > oceans) {
      /* Land.  Put on scale of [0,1]. */
      hmap[index] = (hmap[index] - oceans) / landmass;
    } else {
      /* Ocean.  Put on scale of [-1,0]. */
      hmap[index] = hmap[index] / oceans - 1.0;
    }
  } whole_map_iterate_end;
}

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

  Currently this is only used in the gen1 hmap, but it could be used in
  any of the maps.
**************************************************************************/
static void smooth_xmap(double *hmap)
{
  /* We make a new height map and then copy it back over the old one.
   * Care must be taken so that the new height map uses the exact same
   * storage structure as the real one - it must be the same size and
   * use the same indexing. The advantage of the new array is there's
   * no feedback from overwriting in-use values.
   */
  double new_hmap[MAX_MAP_INDEX];

  whole_map_iterate_index(index, x, y) {
    /* double-count this tile */
    double height_sum = hmap[index] * 2;

    /* weight of counted tiles */
    int counter = 2;

    adjc_iterate(x, y, x2, y2) {
      /* count adjacent tile once */
      height_sum += hmap[map_pos_to_index(x2, y2)];
      counter++;
    } adjc_iterate_end;

    height_sum = MAX(height_sum, 0);
    new_hmap[index] = height_sum / counter;
  } whole_map_iterate_end;

  memcpy(hmap, new_hmap, sizeof(new_hmap));
}

static void elevate_poles(double *hmap)
{
  double distance, polar_lattitude, ocean_lattitude;
  double oceans = 1.0 - (double)map.landpercent / 100.0;

  adjust_xmap(hmap);

  if (topo_has_flag(TF_WRAPX) && topo_has_flag(TF_WRAPY)) {
    distance = 30.0;
  } else if (topo_has_flag(TF_WRAPX)) {
    distance = NATURAL_HEIGHT / 2;
  } else if (topo_has_flag(TF_WRAPY)) {
    distance = NATURAL_WIDTH / 2;
  } else {
    distance = NATURAL_HEIGHT;
  }

  polar_lattitude = 0.5 / distance;
  ocean_lattitude = 2.5 / distance;

  whole_map_iterate_index(index, x, y) {
    if (map_lattitude(x, y) < polar_lattitude) {
      hmap[index] = (oceans + 1.0) / 2.0;
    } else if (map_lattitude(x, y) < ocean_lattitude) {
      hmap[index] = oceans / 2.0;
    }
  } whole_map_iterate_index_end;
}

/**************************************************************************
  Generator1 method to generate hmap.  Basically random.
**************************************************************************/
static void generate_hmap_1(double *hmap)
{
  int i;

  whole_map_iterate_index(index, x, y) {
    hmap[index] = myrand(40);
  } whole_map_iterate_end;

  for (i = 0; i < 1500; i++) {
    int x, y, index;

    rand_map_pos(&x, &y);
    index = map_pos_to_index(x, y);

    if (near_singularity(x, y) || map_lattitude(x, y) <= 0.1) {
      /* Avoid land near singularities or at the poles. */
      hmap[index] -= myrand(5000);
    } else { 
      hmap[index] += myrand(5000);
    }
    if ((i % 100) == 0) {
      smooth_xmap(hmap); 

      whole_map_iterate_index(index, x, y) {
        /* random factor: -30..30 */
        hmap[index] += myrand(61) - 30;
      } whole_map_iterate_index_end;
    }
  }

  for (i = 0; i < 3; i++) {
    whole_map_iterate_index(index, x, y) {
      /* random factor: -30..30 */
      hmap[index] += myrand(61) - 30;
    } whole_map_iterate_index_end;
    smooth_xmap(hmap);
  }

  elevate_poles(hmap);
}

static void generate_hmap_234(double *hmap)
{
  /* Gen2 hmap generator */
  generate_hmap_1(hmap); /* TODO */
}

/**************************************************************************
  Recursive function which does the work for generator 5.

  All (x0,y0) and (x1,y1) are in native coordinates.
**************************************************************************/
static void gen5rec(double *hmap, int step, int x0, int y0, int x1, int y1)
{
  int val[2][2];
  int x1wrap = x1; /* to wrap correctly */ 
  int y1wrap = y1; 

  /* All x and y values are native. */

  if (((y1 - y0 <= 0) || (x1 - x0 <= 0)) 
      || ((y1 - y0 == 1) && (x1 - x0 == 1))) {
    return;
  }

  if (x1 == map.xsize)
    x1wrap = 0;
  if (y1 == map.ysize)
    y1wrap = 0;

  val[0][0] = hmap[native_pos_to_index(x0, y0)];
  val[0][1] = hmap[native_pos_to_index(x0, y1wrap)];
  val[1][0] = hmap[native_pos_to_index(x1wrap, y0)];
  val[1][1] = hmap[native_pos_to_index(x1wrap, y1wrap)];

  /* set midpoints of sides to avg of side's vertices plus a random factor */
  /* unset points are zero, don't reset if set */
#define set_midpoints(X, Y, V)                                          \
  do_in_map_pos(map_x, map_y, (X), (Y)) {                               \
    if (!near_singularity(map_x, map_y)                                 \
        && map_lattitude(map_x, map_y) > 0.2                            \
        && hmap[native_pos_to_index((X), (Y))] == 0) {                  \
      hmap[native_pos_to_index((X), (Y))] = (V);                        \
    }                                                                   \
  } do_in_map_pos_end;

  set_midpoints((x0 + x1) / 2, y0,
                (val[0][0] + val[1][0]) / 2 + myrand(step) - step / 2);
  set_midpoints((x0 + x1) / 2,  y1wrap,
                (val[0][1] + val[1][1]) / 2 + myrand(step) - step / 2);
  set_midpoints(x0, (y0 + y1)/2,
                (val[0][0] + val[0][1]) / 2 + myrand(step) - step / 2);  
  set_midpoints(x1wrap,  (y0 + y1) / 2,
                (val[1][0] + val[1][1]) / 2 + myrand(step) - step / 2);  

  /* set middle to average of midpoints plus a random factor, if not set */
  set_midpoints((x0 + x1) / 2, (y0 + y1) / 2,
                ((val[0][0] + val[0][1] + val[1][0] + val[1][1]) / 4
                 + myrand(step) - step / 2));

#undef set_midpoints

  /* now call recursively on the four subrectangles */
  gen5rec(hmap, 2 * step / 3, x0, y0, (x1 + x0) / 2, (y1 + y0) / 2);
  gen5rec(hmap, 2 * step / 3, x0, (y1 + y0) / 2, (x1 + x0) / 2, y1);
  gen5rec(hmap, 2 * step / 3, (x1 + x0) / 2, y0, x1, (y1 + y0) / 2);
  gen5rec(hmap, 2 * step / 3, (x1 + x0) / 2, (y1 + y0) / 2, x1, y1);
}

/**************************************************************************
Generator 5 makes earthlike worlds with one or more large continents and
a scattering of smaller islands. It does so by dividing the world into
blocks and on each block raising or lowering the corners, then the 
midpoints and middle and so on recursively.  Fiddling with 'xdiv' and 
'ydiv' will change the size of the initial blocks and, if the map does not 
wrap in at least one direction, fiddling with 'avoidedge' will change the 
liklihood of continents butting up to non-wrapped edges.

  All X and Y values used in this function are in native coordinates.
**************************************************************************/
static void generate_hmap_5(double *hmap)
{
  const bool xnowrap = !topo_has_flag(TF_WRAPX);
  const bool ynowrap = !topo_has_flag(TF_WRAPY);

  /* 
   * How many blocks should the x and y directions be divided into
   * initially. 
   */
  const int xdiv = 6;           
  const int ydiv = 5;

  int xdiv2 = xdiv + (xnowrap ? 1 : 0);
  int ydiv2 = ydiv + (ynowrap ? 1 : 0);

  int xmax = map.xsize - (xnowrap ? 1 : 0);
  int ymax = map.ysize - (ynowrap ? 1 : 0);
  int xn, yn;
  /* just need something > log(max(xsize, ysize)) for the recursion */
  int step = map.xsize + map.ysize; 
  /* edges are avoided more strongly as this increases */
  int avoidedge = (50 - map.landpercent) * step / 100 + step / 3; 

  /* set initial points */
  for (xn = 0; xn < xdiv2; xn++) {
    for (yn = 0; yn < ydiv2; yn++) {
      do_in_map_pos(x, y, (xn * xmax / xdiv), (yn * ymax / ydiv)) {
        int index = map_pos_to_index(x, y);

        /* set initial points */
        hmap[index] = myrand(2 * step) - (2 * step) / 2;

        if (near_singularity(x, y)) {
          /* avoid edges (topological singularities) */
          hmap[index] -= avoidedge;
        }

        if (map_lattitude(x, y) <= 0.1) {
          /* separate poles and avoid too much land at poles */
          hmap[index] -= myrand(avoidedge);
        }
      } do_in_map_pos_end;
    }
  }

  /* calculate recursively on each block */
  for (xn = 0; xn < xdiv; xn++) {
    for (yn = 0; yn < ydiv; yn++) {
      gen5rec(hmap, step, xn * xmax / xdiv, yn * ymax / ydiv, 
              (xn + 1) * xmax / xdiv, (yn + 1) * ymax / ydiv);
    }
  }

  /* put in some random fuzz */
  whole_map_iterate_index(index, x, y) {
    hmap[index] = 8 * hmap[index] + myrand(4) - 2;
  } whole_map_iterate_end;

  elevate_poles(hmap);
}

/****************************************************************************
  Set all nearby tiles as placed on pmap. This helps avoiding huts being 
  too close
****************************************************************************/
static void set_placed_close_hut(int map_x, int map_y, bool *pmap)
{
  square_iterate(map_x, map_y, 3, x1, y1) {
    pmap[map_pos_to_index(x1, y1)] = TRUE;
  } square_iterate_end;
}

/****************************************************************************
  Return TRUE if a safe tile is in a radius of 1.  This function is used to
  test where to place specials on the sea.
****************************************************************************/
static bool near_safe_tiles(int x_ct, int y_ct)
{
  square_iterate(x_ct, y_ct, 1, map_x, map_y) {
    if (!terrain_has_flag(map_get_terrain(map_x, map_y), TER_UNSAFE_COAST)) {
      return TRUE;
    }   
  } square_iterate_end;

  return FALSE;
}

/**************************************************************************
  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.
**************************************************************************/
static void make_huts(int number)
{
  int x, y, count = 0;
  bool pmap[MAX_MAP_INDEX];

  memset(pmap, 0, sizeof(pmap));

  while (number * map_num_tiles() >= 2000 && count++ < map_num_tiles() * 2) {

    /* Add a hut.  But not on a polar area, on an ocean, or too close to
     * another hut. */
    rand_map_pos(&x, &y);
    if (!is_ocean(map_get_terrain(x, y))) {
      number--;
      map_set_special(x, y, S_HUT);
      set_placed_close_hut(x, y, pmap);
      /* Don't add to islands[].goodies because islands[] not
         setup at this point, except for generator>1, but they
         have pre-set starters anyway. */
    }
  }
}

/****************************************************************************
  Return TRUE iff there's a special (i.e., SPECIAL_1 or SPECIAL_2) within
  1 tile of the given map position.
****************************************************************************/
static bool is_special_close(int map_x, int map_y)
{
  square_iterate(map_x, map_y, 1, x1, y1) {
    if (map_has_special(x1, y1, S_SPECIAL_1)
        || map_has_special(x1, y1, S_SPECIAL_2)) {
      return TRUE;
    }
  } square_iterate_end;

  return FALSE;
}

/****************************************************************************
  Add specials to the map with given probability (out of 1000).
****************************************************************************/
static void add_specials(int prob)
{
  Terrain_type_id ttype;

  whole_map_iterate(x, y)  {
    ttype = map_get_terrain(x, y);
    if (!is_ocean(ttype)
        && !is_special_close(x, y) 
        && myrand(1000) < prob) {
      if (tile_types[ttype].special_1_name[0] != '\0'
          && (tile_types[ttype].special_2_name[0] == '\0'
              || (myrand(100) < 50))) {
        map_set_special(x, y, S_SPECIAL_1);
      } else if (tile_types[ttype].special_2_name[0] != '\0') {
        map_set_special(x, y, S_SPECIAL_2);
      }
    } else if (is_ocean(ttype) && near_safe_tiles(x,y) 
               && myrand(1000) < prob && !is_special_close(x, y)) {
      if (tile_types[ttype].special_1_name[0] != '\0'
          && (tile_types[ttype].special_2_name[0] == '\0'
              || (myrand(100) < 50))) {
        map_set_special(x, y, S_SPECIAL_1);
      } else if (tile_types[ttype].special_2_name[0] != '\0') {
        map_set_special(x, y, S_SPECIAL_2);
      }
    }
  } whole_map_iterate_end;
  
  map.have_specials = TRUE;
}

/**************************************************************************
  See stdinhand.c for information on map generation methods.

FIXME: Some continent numbers are unused at the end of this function, fx
       removed completely by remove_tiny_islands.
       When this function is finished various data is written to "islands",
       indexed by continent numbers, so a simple renumbering would not
       work...

  If "autosize" is specified then mapgen will automatically size the map
  based on the map.size server parameter and the specified topology.  If
  not map.xsize and map.ysize will be used.
**************************************************************************/
void map_generate(bool autosize)
{
  /* save the current random state: */
  RANDOM_STATE rstate = get_myrand_state();
 
  if (map.seed==0)
    map.seed = (myrand(MAX_UINT32) ^ time(NULL)) & (MAX_UINT32 >> 1);

  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) {
    generator_init_topology(autosize);
    map_allocate();
  }
  if (map.generator != 0) {
    double hmap[MAX_MAP_INDEX], tmap[MAX_MAP_INDEX], mmap[MAX_MAP_INDEX];

    memset(hmap, 0, sizeof(hmap));
    memset(tmap, 0, sizeof(tmap));
    memset(mmap, 0, sizeof(mmap));

    generators[map.generator].generate_hmap(hmap);
    adjust_xmap(hmap);
    create_oceans(hmap);
    print_xmap("hmap", hmap);

    generators[map.generator].generate_tmap(hmap, tmap);
    adjust_xmap(tmap);
    //print_xmap("tmap", tmap);
    make_rivers(hmap, tmap);

    generators[map.generator].generate_mmap(hmap, tmap, mmap);
    adjust_xmap(mmap);
    //print_xmap("mmap", mmap);

    place_terrain(hmap, tmap, mmap);
  } else {
      assign_continent_numbers();
  }

  if(!map.have_specials) /* some scenarios already provide specials */
    add_specials(map.riches); /* hvor mange promiller specials oensker vi*/

  if (!map.have_huts)
    make_huts(map.huts); /* Vi vil have store promiller; man kan aldrig faa
                            for meget oel! */

  /* restore previous random state: */
  set_myrand_state(rstate);
}

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