Complete.Org: Mailing Lists: Archives: freeciv-dev: April 2003:
[Freeciv-Dev] Re: Tidying savegames to pave the way for plug mapgens
Home

[Freeciv-Dev] Re: Tidying savegames to pave the way for plug mapgens

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
To: Cameron Morland <cameron@xxxxxxxxxx>
Cc: FreeCiv Developers <freeciv-dev@xxxxxxxxxxx>
Subject: [Freeciv-Dev] Re: Tidying savegames to pave the way for plug mapgens
From: Ross Wetmore <rwetmore@xxxxxxxxxxxx>
Date: Sat, 26 Apr 2003 11:13:51 -0400

I like the general philosophy of modularizing mapgen into a number of
sub elements with (boolean) or other control flags. Having a lot of
the make_<blort> elements runnable by any of the generators was also
one of the early changes in the corecleanup mapgen. Mix and match
ability is a godd thing (TM).

As stated below, and RFC describing the general system mechanics for
what you have done would be nice. This would allow a reasonable review
and widespread concensus on how to approach individual changes in a
systemmatic way and thus prevent a future rats' nest of cross purpose
hacks from being the outcome of what is otherwise a very worthwhile
direction to pursue.

Cheers,
RossW
=====

Cameron Morland wrote:
Hi,

This patch does a number of things, primarily to make plug-in map
generators much easier to write. For the most part I've been changing
what is required to successfully load a file as a savegame.

While I was at it I removed some features required to load extremely
old savegames. We already require that a savegame claim to be version
1.9.0 or later, so checking for features from 1.1 (!) isn't
productive.

Irrelevant Parameters
=====================
First of all, there are a very large number of parameters in a
savegame file, many of which do not need to be stored in the game,
especially for scenarios. It should be possible to set parameters,
such as gold, skill_level, nplayers, settlers, explorers, save_nturns,
etc and then load a scenario, without the scenario overriding the
specified values.

These parameters need to be present for scenarios, i.e. the ability to
specify a complete populated map is essential. They also need to be
present for savegames to be usable as a snapshot of a running game.

There is no reason for elements to be mandatory though. The issues
here are 1) are there already default values set, or 2) do certain
steps need to be run post load to fillin missing ones.

Most `x = secfile_lookup_<type>(...)` statements have been replaced
with `x = secfile_lookup_<type>_default(..., x)`, ie use the value
which the person running the server has already specified (or the
default, if the user hasn't specified anything).

You may need to be careful about just taking the current struct value
if it has not been initialized and it was assumed to be filled in by
the savegame data. This assumes 1) is always the case and it may not
be.

Most options just
need to be recorded in the file if the file wishes to change the pre-existing value. For more complex options, such as
fixed_start_positions and heightmaps (see below), I use the
has_capability thing, so the savegame must specify that it will use
those features.

This is case 2) where you may need to run a post-load routine to fill
in the values.

The only parameters that a scenario must specify are map.width,
map.height, game.version >= 10900, and game.server_state == 0.
If a scenario wants to keep its tinyisles, it has to specify
tinyisles=1; mapgen makes much more sense this way.

With appropriate caveats about all the uses of savegames, different
points at which they may be read in and flags settings that may or
may not require/cause pre/post processing.

I think there needs to be a fair amount of error checking to make
sure a partial map that is ok in one case does not cause faults if
used in another.

Loading Height Maps
===================
Currently, generators{1,5} construct a height map, then run make_land to
add the terrains. Generators{2,3,4} make tilemaps directly, including
rivers. I wanted external generators to be able to make use of the
terrain/river/specials placing algorithms (especially since Karen and
Ross have been working on making them better), so they needed to be
able to provide height maps.

With this patch, a map can provide a height map, which is a 16-bit
hexadecimal map. Once this is loaded the built-in terrain placing
algorithm runs (with specified landmass, etc), and a tilemap is
created.

This is not a good algorithm. It has strong dependencies on the
iteration order.

Break it up so the setting of the artifical values is done in one
pass, and the smoothing is done in a second in a way that does not
have order dependencies. This means collecting the smoothed data into a
separate array, so previous smoothing does not affect later tiles, or
smoothing the tiles in a random order, so that even while smoothing is
being folded back in during the process, there is no order dependency.
I actually like the latter as it does a more thorough and varied
smoothing process in some ways.

You may also need to do some more thorough checking to insure that
land/sea heights are kept distinct, i.e. the smoothing does not
submerge land tiles or cause land to arise out of the sea.

General mapgen layout
=====================
A few bits of mapgen make more sense now (IMHO). Instead of requiring
each generator to do everything, it can specify what it does by
setting booleans, then default functions can be applied later. This
should make the code tidier.

I'd like to see a rough RFC for how you have done this and what might be
a long term overall system. There are previous list messages on the
subject for a starter.

The idea is really good, but a hack approach may result in a particularly
bad tasste to the implementation that is not a long term benefit.

Since making rivers depends on having a height map, I wrote a simple
function to generate a heightmap given a tilemap.

You might look at the corecleanup example for this as well to see if it
offers any insights or possible enhancement options.

Load Command
============
If you ask for help on the 'load' command, it will say that "Any
current data including players, rulesets and server options are lost."
This is rather annoying, and is mostly fixed by the "irrelevant
parameters" section above. It still screws up the players, but pretty
well everything else should be left alone (assuming, of course, that
the file you load doesn't specify these things). It's a start.

As you say, this is one of the places where you need to deal with
conflicts. Some sanity checks, and rules for which parameters have
precedence should be implemented and documented. Otherwise, there will
be a lot of segfaults.

External Generators
===================
I haven't written the glue code to run external map generators, I've
just been running them from the command line, redirecting to a file,
then loading the file. But now at least the plug-in generators don't
have to be so complicated; if they want to use a heightmap technique,
they can just generate the heightmap and let mapgen do the rest.

If you are just doing the equivalent of reading scenarios from the
commandline, then things are simpler. When you run plug-in generators
at internal stages in the process, then you need to be able to read
information from the savegame to parameterize the generator, and check
the subsequent values on read in to insure they are still compatible
with the previous/post code flow.

Attached are the diff, and a pretty terrible map generator written in
Perl (it's not a great generator, it's just something I hacked
together as an example). Comments? I'll be away for a few days, but
I'd like to see what people think.

The perl generator is a neat example of how simple external geenrators
might be. Having a template with code to modify/fill in values is an
interesting way to get around the registry load/save code and still
have something that is relatively easy to update to keep in sync with
an evolving codebase or savegame format.

But some way to parse the existing savegame to recover parameters for
a more ambitious generator may require a bit more effort.

I think this does show how powerful the savegame interface is as the
communication channel for passing arguments and returning results from
an external map generator. Adding the code to the core to output and
reread savegames at key points to make use of this is the critical step
to making this a powerful integrated feature.

One of the areas that might need to be worked on as an adjunct to this
is the sequence of initialization steps. At present, things are very
much hardwired and integfrated so that it is difficult to unwind or
free some components to update them. A more modular organization of
things like the game and player structures or gen_impr initialization
so one could take full advantage of a savegame reload and update step
would be nice.

------------------------------------------------------------------------

? generatesillymap.pl
? punchmap.pl
? client/tilespec.c.shelf
? data/trident/.xvpics
? data/trident/tiles.xcf
Index: client/civclient.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/civclient.c,v
retrieving revision 1.168
diff -u -3 -p -r1.168 civclient.c
--- client/civclient.c  2003/04/17 20:06:35     1.168
+++ client/civclient.c  2003/04/24 01:14:43
@@ -616,7 +616,7 @@ void client_game_init()
   conn_list_init(&game.est_connections);
   conn_list_init(&game.game_connections);
- game_init();
+  full_game_init();
   attribute_init();
   agents_init();
   cm_init();
Index: common/game.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/game.c,v
retrieving revision 1.161
diff -u -3 -p -r1.161 game.c
--- common/game.c       2003/04/17 20:06:36     1.161
+++ common/game.c       2003/04/24 01:14:44
@@ -632,12 +632,14 @@ void game_remove_city(struct city *pcity
 }
/***************************************************************
-...
+Do a complete initialisation of game structures. This should run
+only at the beginning, because many of these we'll want to leave
+untouched later, for example if loading a scenario; we should +leave them at values selected by the user. nb possibly more of +game_init should be moved here. --CJM
 ***************************************************************/
-void game_init(void)
+void full_game_init(void)
 {
-  int i;
-  game.is_new_game   = TRUE;
   game.globalwarming = 0;
   game.warminglevel  = 8;
   game.nuclearwinter = 0;
@@ -651,11 +653,6 @@ void game_init(void)
   game.timeoutinc    = GAME_DEFAULT_TIMEOUTINC;
   game.timeoutincmult= GAME_DEFAULT_TIMEOUTINCMULT;
   game.timeoutcounter= 1;
-  game.tcptimeout    = GAME_DEFAULT_TCPTIMEOUT;
-  game.netwait       = GAME_DEFAULT_NETWAIT;
-  game.last_ping     = 0;
-  game.pingtimeout   = GAME_DEFAULT_PINGTIMEOUT;
-  game.pingtime      = GAME_DEFAULT_PINGTIME;
   game.end_year      = GAME_DEFAULT_END_YEAR;
   game.year          = GAME_START_YEAR;
   game.turn          = 0;
@@ -698,6 +695,27 @@ void game_init(void)
   game.nbarbarians = 0;
   game.occupychance= GAME_DEFAULT_OCCUPYCHANCE;
+ game.randseed=GAME_DEFAULT_RANDSEED;
+  game.watchtower_vision=GAME_DEFAULT_WATCHTOWER_VISION;
+  game.watchtower_extra_vision=GAME_DEFAULT_WATCHTOWER_EXTRA_VISION,
+
+  game_init();
+  map_init();
+}
+
+/***************************************************************
+...
+***************************************************************/
+void game_init(void)
+{
+  int i;
+  game.is_new_game   = TRUE;
+  game.tcptimeout    = GAME_DEFAULT_TCPTIMEOUT;
+  game.netwait       = GAME_DEFAULT_NETWAIT;
+  game.last_ping     = 0;
+  game.pingtimeout   = GAME_DEFAULT_PINGTIMEOUT;
+  game.pingtime      = GAME_DEFAULT_PINGTIME;
+
   game.heating     = 0;
   game.cooling     = 0;
   sz_strlcpy(game.save_name, GAME_DEFAULT_SAVE_NAME);
@@ -707,9 +725,6 @@ void game_init(void)
 #else
   game.save_compress_level = GAME_NO_COMPRESS_LEVEL;
 #endif
-  game.randseed=GAME_DEFAULT_RANDSEED;
-  game.watchtower_vision=GAME_DEFAULT_WATCHTOWER_VISION;
-  game.watchtower_extra_vision=GAME_DEFAULT_WATCHTOWER_EXTRA_VISION,
   game.allowed_city_names = GAME_DEFAULT_ALLOWED_CITY_NAMES;
sz_strlcpy(game.rulesetdir, GAME_DEFAULT_RULESETDIR);
@@ -740,7 +755,6 @@ void game_init(void)
   game.load_options.load_settings = TRUE;
init_our_capability(); - map_init();
   idex_init();
for(i=0; i<MAX_NUM_PLAYERS+MAX_NUM_BARBARIANS; i++)
Index: common/game.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/game.h,v
retrieving revision 1.121
diff -u -3 -p -r1.121 game.h
--- common/game.h       2003/04/17 20:06:36     1.121
+++ common/game.h       2003/04/24 01:14:44
@@ -227,6 +227,7 @@ struct lvldat {
   int advspeed;
 };
+void full_game_init(void);
 void game_init(void);
 void game_free(void);
 void ruleset_data_free(void);
Index: common/map.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/map.c,v
retrieving revision 1.138
diff -u -3 -p -r1.138 map.c
--- common/map.c        2003/04/22 20:50:56     1.138
+++ common/map.c        2003/04/24 01:14:45
@@ -195,6 +195,8 @@ void map_init(void)
   map.num_continents        = 0;
   map.num_start_positions   = 0;
   map.fixed_start_positions = FALSE;
+  map.have_heightmap        = FALSE;
+  map.have_tilemap          = FALSE;
   map.have_specials         = FALSE;
   map.have_rivers_overlay   = FALSE;
   map.have_huts             = FALSE;
Index: common/map.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/map.h,v
retrieving revision 1.145
diff -u -3 -p -r1.145 map.h
--- common/map.h        2003/04/22 20:50:56     1.145
+++ common/map.h        2003/04/24 01:14:45
@@ -171,7 +171,9 @@ struct civ_map { bool fixed_start_positions;
   bool have_specials;
   bool have_huts;
-  bool have_rivers_overlay;    /* only applies if !have_specials */
+  bool have_heightmap;
+  bool have_tilemap;
+  bool have_rivers_overlay;
   int num_continents;
   struct tile *tiles;
Index: server/mapgen.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/mapgen.c,v
retrieving revision 1.110
diff -u -3 -p -r1.110 mapgen.c
--- server/mapgen.c     2003/02/20 09:45:22     1.110
+++ server/mapgen.c     2003/04/24 01:14:47
@@ -31,10 +31,7 @@
#include "mapgen.h" -/* 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)])
-
+static void tiles_to_heightmap(void);
 static void make_huts(int number);
 static void add_specials(int prob);
 static void mapgenerator1(void);
@@ -43,7 +40,6 @@ static void mapgenerator3(void);
 static void mapgenerator4(void);
 static void mapgenerator5(void);
 static void smooth_map(void);
-static void adjust_map(int minval);
#define RIVERS_MAXTRIES 32767
 enum river_map_type {RS_BLOCKED = 0, RS_RIVER = 1};
@@ -54,7 +50,7 @@ enum river_map_type {RS_BLOCKED = 0, RS_
    A value of 2 means river.                            -Erik Sigra */
 static int *river_map;
-static int *height_map;
+int *height_map;
 static int maxval=0;
 static int forests=0;
@@ -671,6 +667,8 @@ static void make_rivers(void)
      Is needed to stop a potentially infinite loop. */
   int iteration_counter = 0;
+ map.have_rivers_overlay = TRUE;
+
   river_map = fc_malloc(sizeof(int)*map.xsize*map.ysize);
/* The main loop in this function. */
@@ -862,7 +860,6 @@ static void make_land(void)
   make_plains();
   make_polar();
   make_fair();
-  make_rivers();
 }
/**************************************************************************
@@ -1133,6 +1130,44 @@ void create_start_positions(void)
 }
/**************************************************************************
+Given a set of tiles, generate a fairly plausible height map.
+Data for this function should be extracted from the ruleset.
+**************************************************************************/
+static void tiles_to_heightmap(void)
+{
+  int heights[T_LAST];
+  int adjc_spread = 3;
+  int square_spread = 4;
+  int radius = 3;
+
+  heights[T_ARCTIC] = 100;
+  heights[T_DESERT] = 180;
+  heights[T_FOREST] = 150;
+  heights[T_GRASSLAND] = 80;
+  heights[T_HILLS] = 300;
+  heights[T_JUNGLE] = 140;
+  heights[T_MOUNTAINS] = 350;
+  heights[T_OCEAN] = 0;
+  heights[T_PLAINS] = 200;
+  heights[T_RIVER] = 90;
+  heights[T_SWAMP] = 50;
+  heights[T_TUNDRA] = 110;
+
+  maxval = 0;
+  whole_map_iterate(x, y) {
+    hmap(x, y) += heights[map_get_terrain(x, y)];
+
+    adjc_iterate(x, y, x1, y1) {
+      hmap(x1, y1) += heights[map_get_terrain(x, y)] / adjc_spread;
+    } adjc_iterate_end;
+
+    square_iterate(x, y, radius, x1, y1) {
+      hmap(x1, y1) += heights[map_get_terrain(x, y)] / square_spread;
+    } square_iterate_end;
+  } whole_map_iterate_end;
+}
+
+/**************************************************************************
   See stdinhand.c for information on map generation methods.
FIXME: Some continent numbers are unused at the end of this function, fx
@@ -1145,16 +1180,18 @@ void map_fractal_generate(void)
 {
   /* save the current random state: */
   RANDOM_STATE rstate = get_myrand_state();
- +
+  if(!map.have_heightmap) {
+    height_map = fc_malloc(sizeof(int) * map.xsize * map.ysize);
+  }
+
   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) {
-    map_allocate();
     /* if one mapgenerator fails, it will choose another mapgenerator */
     /* with a lower number to try again */
     if (map.generator == 5 )
@@ -1167,18 +1204,49 @@ void map_fractal_generate(void)
       mapgenerator2();
     if( map.generator == 1 )
       mapgenerator1();
-    if (!map.tinyisles) {
-      remove_tiny_islands();
-    }
   }
- if(!map.have_specials) /* some scenarios already provide specials */
+  if(!map.have_heightmap && !map.have_tilemap) {
+    /* The mapgenerator failed, or we loaded an empty scenario. */
+    freelog(LOG_FATAL, "mapgen.c: This map contains neither a heightmap not a 
tilemap.");
+    abort();
+  }
+
+  if(!map.have_tilemap) {
+    /* we don't have a tilemap, so we must have a heightmap. */
+    assert(map.have_heightmap);
+    map_allocate();
+    make_land();
+  }
+
+  if(!map.have_heightmap) {
+    /* we don't have a heightmap, so we must have a tilemap. */
+    assert(map.have_tilemap);
+    tiles_to_heightmap();
+  }
+
+  if (!map.tinyisles) {
+    remove_tiny_islands();
+  }
+ + if(!map.have_rivers_overlay) {
+    make_rivers();
+  }
+
+  if(!map.have_specials) { /* some scenarios already provide specials */
     add_specials(map.riches); /* hvor mange promiller specials oensker vi*/
+  }
- if (!map.have_huts)
+  if (!map.have_huts) {
     make_huts(map.huts); /* Vi vil have store promiller; man kan aldrig faa
-                           for meget oel! */
+                         * for meget oel! */
+  }
+ /* we no longer need the height map.
+   * Note that we might want to keep it eventually. */
+  free(height_map);
+  height_map = NULL;
+
   /* restore previous random state: */
   set_myrand_state(rstate);
 }
@@ -1220,8 +1288,18 @@ void adjust_terrain_param(void)
   i reduce the height so the lowest height is zero, this makes calculations
   easier
 **************************************************************************/
-static void adjust_map(int minval)
+void adjust_map(void)
 {
+  unsigned int minval = MAX_UINT32;
+  whole_map_iterate(x, y) {
+    if (hmap(x, y) > maxval)
+      maxval = hmap(x, y);
+    if (hmap(x, y) < minval)
+      minval = hmap(x, y);
+  } whole_map_iterate_end;
+
+  maxval-=minval;
+
   whole_map_iterate(x, y) {
     hmap(x, y) -= minval;
   } whole_map_iterate_end;
@@ -1233,8 +1311,6 @@ static void adjust_map(int minval)
 static void mapgenerator1(void)
 {
   int i;
-  int minval=5000000;
-  height_map=fc_malloc (sizeof(int)*map.xsize*map.ysize);
adjust_terrain_param(); @@ -1256,19 +1332,12 @@ static void mapgenerator1(void) smooth_map(); smooth_map(); - whole_map_iterate(x, y) {
-    if (hmap(x, y) > maxval)
-      maxval = hmap(x, y);
-    if (hmap(x, y) < minval)
-      minval = hmap(x, y);
-  } whole_map_iterate_end;
+  adjust_map();
- maxval-=minval;
-  adjust_map(minval);
-
-  make_land();
-  free(height_map);
-  height_map = NULL;
+  /* this generator provides a heightmap, but no tilemap. */
+  map.have_tilemap = FALSE;
+  map.have_heightmap = TRUE;
+  map.have_rivers_overlay = FALSE;
 }
/**************************************************************************
@@ -1743,7 +1812,7 @@ static void initworld(struct gen234_stat
 {
   int x, y;
- height_map = fc_malloc(sizeof(int) * map.ysize * map.xsize);
+  //height_map = fc_malloc(sizeof(int) * map.ysize * map.xsize);
   islands = fc_malloc((MAP_NCONT+1)*sizeof(struct isledata));
for (y = 0 ; y < map.ysize ; y++) @@ -1788,6 +1857,8 @@ static void mapgenerator2(void)
     return;
   }
+ map_allocate(); /* we're making a tilemap, not a heightmap. */
+
   adjust_terrain_param();
   pstate->totalmass =
       ((map.ysize - 6 - spares) * map.landpercent * (map.xsize - spares)) /
@@ -1815,12 +1886,15 @@ static void mapgenerator2(void)
     make_island(10 * pstate->totalmass / totalweight, 0, pstate);
   }
make_plains(); - free(height_map);
-  height_map = NULL;
if(checkmass>map.xsize+map.ysize+totalweight) {
     freelog(LOG_VERBOSE, "%ld mass left unplaced", checkmass);
   }
+
+  /* this generator provides a tilemap and rivers, but no heightmap. */
+  map.have_tilemap = TRUE;
+  map.have_heightmap = FALSE;
+  map.have_rivers_overlay = TRUE;
 }
/**************************************************************************
@@ -1842,6 +1916,8 @@ static void mapgenerator3(void)
     return;
   }
+ map_allocate(); /* we're making a tilemap, not a heightmap. */
+
   adjust_terrain_param();
   pstate->totalmass =
       ((map.ysize - 6 - spares) * map.landpercent * (map.xsize - spares)) /
@@ -1900,8 +1976,6 @@ static void mapgenerator3(void)
   }
make_plains(); - free(height_map);
-  height_map = NULL;
if(j==1500) {
     freelog(LOG_NORMAL, _("Generator 3 left %li landmass unplaced."), 
checkmass);
@@ -1909,6 +1983,10 @@ static void mapgenerator3(void)
     freelog(LOG_VERBOSE, "%ld mass left unplaced", checkmass);
   }
+ /* this generator provides a tilemap and rivers, but no heightmap. */
+  map.have_tilemap = TRUE;
+  map.have_heightmap = FALSE;
+  map.have_rivers_overlay = TRUE;
 }
/**************************************************************************
@@ -1922,7 +2000,6 @@ static void mapgenerator4(void)
   struct gen234_state state;
   struct gen234_state *pstate = &state;
-
   /* no islands with mass >> sqr(min(xsize,ysize)) */
i = game.nplayers / 2;
@@ -1931,6 +2008,8 @@ static void mapgenerator4(void)
     return;
   }
+ map_allocate(); /* we're making a tilemap, not a heightmap. */
+
   if(map.landpercent>60)
     bigweight=30;
   else if(map.landpercent>40)
@@ -1976,12 +2055,15 @@ static void mapgenerator4(void)
     make_island(10 * pstate->totalmass / totalweight, 0, pstate);
   }
make_plains(); - free(height_map);
-  height_map = NULL;
if(checkmass>map.xsize+map.ysize+totalweight) {
     freelog(LOG_VERBOSE, "%ld mass left unplaced", checkmass);
   }
+
+  /* this generator provides a tilemap and rivers, but no heightmap. */
+  map.have_tilemap = TRUE;
+  map.have_heightmap = FALSE;
+  map.have_rivers_overlay = TRUE;
 }
/**************************************************************************
@@ -2045,7 +2127,7 @@ blocks and on each block raising or lowe
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.
+likelihood of continents butting up to non-wrapped edges.
 **************************************************************************/
 static void mapgenerator5(void)
 {
@@ -2070,8 +2152,6 @@ static void mapgenerator5(void)
   /* edges are avoided more strongly as this increases */
int avoidedge = (50 - map.landpercent) * step / 100 + step / 3; - height_map = fc_malloc(sizeof(int) * map.xsize * map.ysize);
-
   adjust_terrain_param();
/* initialize map */
@@ -2127,16 +2207,10 @@ static void mapgenerator5(void)
   whole_map_iterate(x, y) {
     /* put in some random fuzz */
     hmap(x, y) = 8 * hmap(x, y) + myrand(4) - 2;
-    /* and calibrate maxval and minval */
-    if (hmap(x, y) > maxval)
-      maxval = hmap(x, y);
-    if (hmap(x, y) < minval)
-      minval = hmap(x, y);
   } whole_map_iterate_end;
-  maxval -= minval;
-  adjust_map(minval);
+  adjust_map();
- make_land();
-  free(height_map);
-  height_map = NULL;
+    /* this generator provides a heightmap, but not tilemap. */
+  map.have_tilemap = FALSE;
+  map.have_heightmap = TRUE;
 }
Index: server/mapgen.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/mapgen.h,v
retrieving revision 1.10
diff -u -3 -p -r1.10 mapgen.h
--- server/mapgen.h     2002/02/05 19:05:51     1.10
+++ server/mapgen.h     2003/04/24 01:14:47
@@ -13,9 +13,14 @@
 #ifndef FC__MAPGEN_H
 #define FC__MAPGEN_H
+/* 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)])
+
 void assign_continent_numbers(void);
 void map_fractal_generate(void);
 void create_start_positions(void);
 void adjust_terrain_param(void);
+void adjust_map(void);
#endif /* FC__MAPGEN_H */
Index: server/savegame.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/savegame.c,v
retrieving revision 1.116
diff -u -3 -p -r1.116 savegame.c
--- server/savegame.c   2003/04/22 20:50:56     1.116
+++ server/savegame.c   2003/04/24 01:14:49
@@ -52,6 +52,9 @@
#include "savegame.h" +extern int *height_map;
+#include "mapgen.h"
+
/* * This loops over the entire map to save data. It collects all the
  * data of a line using get_xy_char and then executes the
@@ -114,14 +117,14 @@
  * we let any map data type to be empty, and just print an
  * informative warning message about it. */
-#define LOAD_MAP_DATA(secfile_lookup_line, set_xy_char) \
+#define LOAD_MAP_DATA(secfile_lookup_line, set_xy_char, nibbles)              \
 {                                                                             \
   int y;                                                                      \
   bool warning_printed = FALSE;                                               \
   for (y = 0; y < map.ysize; y++) {                                           \
     char *line = secfile_lookup_line;                                         \
     int x;                                                                    \
-    if (!line || strlen(line) != map.xsize) {                                 \
+    if (!line || strlen(line) != map.xsize*nibbles) {                         \
       if(!warning_printed) {                                                  \
         freelog(LOG_ERROR, _("The save file contains incomplete "             \
                 "map data.  This can happen with old saved "                  \
@@ -130,8 +133,8 @@
         if(!line) {                                                           \
           freelog(LOG_ERROR, _("Reason: line not found"));                    \
         } else {                                                              \
-          freelog(LOG_ERROR, _("Reason: line too short "                      \
-                  "(expected %d got %lu"), map.xsize,                         \
+          freelog(LOG_ERROR, _("Reason: line wrong length "                   \
+                  "(expected %d got %lu)"), map.xsize*nibbles,                \
                   (unsigned long) strlen(line));                              \
         }                                                                     \
         freelog(LOG_ERROR, "secfile_lookup_line='%s'",                        \
@@ -140,21 +143,26 @@
       }                                                                       \
       continue;                                                               \
     }                                                                         \
-    for(x = 0; x < map.xsize; x++) {                                          \
-      char ch = line[x];                                                      \
-      if (regular_map_pos_is_normal(x, y)) {                                  \
+    for(x = 0; x < map.xsize * nibbles; x += nibbles) {                       \
+      char ch[nibbles];                                                       \
+      int nib;                                                                \
+      for(nib = 0; nib < nibbles; nib++) {                                    \
+        ch[nib] = line[x+nib];                                                \
+      }                                                                       \
+      if (regular_map_pos_is_normal(x/nibbles, y)) {                          \
         set_xy_char;                                                          \
       } else {                                                                \
-        assert(ch == '#');                                                    \
+        assert(ch[0] == '#');  /* ??? CJM */                                  \
       }                                                                       \
     }                                                                         \
   }                                                                           \
 }
/* The following should be removed when compatibility with
-   pre-1.13.0 savegames is broken: startoptions, spacerace2
-   and rulesets */
-#define SAVEFILE_OPTIONS "startoptions spacerace2 rulesets" \
+ * pre-1.13.0 savegames is broken: spacerace2
+ * and rulesets. Note that startoptions is no longer needed, even without
+ * breaking compatibility with 1.9.0. */
+#define SAVEFILE_OPTIONS "spacerace2 rulesets" \
 " diplchance_percent worklists2 map_editor known32fix turn " \
 "attributes watchtower rulesetdir client_worklists"
@@ -264,6 +272,46 @@ static int unquote_block(const char *con
 }
/***************************************************************
+Load specials only, ignoring rivers (which are stored in the same word).
+***************************************************************/
+static void map_specials_load(struct section_file *file)
+{
+  LOAD_MAP_DATA(secfile_lookup_str(file, "map.l%03d", y),
+               map_get_tile(x, y)->special =
+               (map_get_tile(x, y)->special & S_RIVER) |
+               (ascii_hex2bin(ch[0], 0) & ~S_RIVER),1);
+  LOAD_MAP_DATA(secfile_lookup_str(file, "map.u%03d", y),
+ map_get_tile(x, y)->special |= + (ascii_hex2bin(ch[0], 1) & ~S_RIVER),1);
+  LOAD_MAP_DATA(secfile_lookup_str_default(file, NULL, "map.n%03d", y),
+ map_get_tile(x, y)->special |= + (ascii_hex2bin(ch[0], 2) & ~S_RIVER),1);
+  LOAD_MAP_DATA(secfile_lookup_str_default(file, NULL, "map.f%03d", y),
+ map_get_tile(x, y)->special |= + (ascii_hex2bin(ch[0], 3) & ~S_RIVER),1);
+}
+
+/***************************************************************
+Load height map. Note that this is stored as one set of long lines,
+using 4 nibbles (16 bits) each. Note that the nibbles are big-endian,
+ie they read they way you might expect.
+***************************************************************/
+static void map_height_load(struct section_file *file)
+{
+  const int size = 4; /* how many nibbles per data value? */
+
+  LOAD_MAP_DATA(secfile_lookup_str(file, "map.H%03d", y),
+               hmap(x/size,y) = (ascii_hex2bin(ch[0], 3) |
+                            ascii_hex2bin(ch[1], 2) |
+                            ascii_hex2bin(ch[2], 1) |
+                            ascii_hex2bin(ch[3], 0)), size);
+
+  adjust_map(); /* fix maxval and minval for mapgen */
+
+  map.have_heightmap = TRUE;
+}
+
+/***************************************************************
 load starting positions for the players from a savegame file
 Now we don't know how many start positions there are nor how many
 should be because rulesets are loaded later. So try to load as
@@ -298,10 +346,11 @@ load the tile map from a savegame file
 ***************************************************************/
 static void map_tiles_load(struct section_file *file)
 {
-  map.is_earth=secfile_lookup_bool(file, "map.is_earth");
+  map.is_earth = secfile_lookup_bool_default(file, FALSE, "map.is_earth");
+  map.have_tilemap = TRUE;
/* In some cases we read these before, but not always, and
-   * its safe to read them again:
+   * it's safe to read them again:
    */
   map.xsize=secfile_lookup_int(file, "map.width");
   map.ysize=secfile_lookup_int(file, "map.height");
@@ -310,27 +359,22 @@ static void map_tiles_load(struct sectio
/* get the terrain type */
   LOAD_MAP_DATA(secfile_lookup_str(file, "map.t%03d", y),
-               map_get_tile(x, y)->terrain = char2terrain(ch));
+               map_get_tile(x, y)->terrain = char2terrain(ch[0]), 1);
assign_continent_numbers();
 }
/***************************************************************
-load the rivers overlay map from a savegame file
-
-(This does not need to be called from map_load(), because
- map_load() loads the rivers overlay along with the rest of
- the specials.  Call this only if you've already called
- map_tiles_load(), and want to overlay rivers defined as
- specials, rather than as terrain types.)
+load the rivers overlay map from a savegame file.
 ***************************************************************/
 static void map_rivers_overlay_load(struct section_file *file)
 {
   /* Get the bits of the special flags which contain the river special
      and extract the rivers overlay from them. */
   LOAD_MAP_DATA(secfile_lookup_str_default(file, NULL, "map.n%03d", y),
-               map_get_tile(x, y)->special |=
-               (ascii_hex2bin(ch, 2) & S_RIVER));
+               map_get_tile(x, y)->special =
+               (map_get_tile(x, y)->special & ~S_RIVER) |
+               (ascii_hex2bin(ch[0], 2) & S_RIVER), 1);
   map.have_rivers_overlay = TRUE;
 }
@@ -341,11 +385,6 @@ static void map_load(struct section_file
 {
   char *savefile_options = secfile_lookup_str(file, "savefile.options");
- /* map_init();
-   * This is already called in game_init(), and calling it
-   * here stomps on map.huts etc.  --dwp
-   */
-
   map_tiles_load(file);
   if (secfile_lookup_bool_default(file, TRUE, "game.save_starts")
       && game.load_options.load_starts) {
@@ -355,39 +394,35 @@ static void map_load(struct section_file
     map.fixed_start_positions = FALSE;
   }
- /* get 4-bit segments of 16-bit "special" field. */
-  LOAD_MAP_DATA(secfile_lookup_str(file, "map.l%03d", y),
-               map_get_tile(x, y)->special = ascii_hex2bin(ch, 0));
-  LOAD_MAP_DATA(secfile_lookup_str(file, "map.u%03d", y),
-               map_get_tile(x, y)->special |= ascii_hex2bin(ch, 1));
-  LOAD_MAP_DATA(secfile_lookup_str_default(file, NULL, "map.n%03d", y),
-               map_get_tile(x, y)->special |= ascii_hex2bin(ch, 2));
-  LOAD_MAP_DATA(secfile_lookup_str_default(file, NULL, "map.f%03d", y),
-               map_get_tile(x, y)->special |= ascii_hex2bin(ch, 3));
+  /* load specials and rivers; we actually waste a bit of speed here,
+   * since the rivers and specials are stored together. But it makes
+   * the code more maintainable. */
+  map_specials_load(file);
+  map_rivers_overlay_load(file);
if (secfile_lookup_bool_default(file, TRUE, "game.save_known")
       && game.load_options.load_known) {
/* get 4-bit segments of the first half of the 32-bit "known" field */
     LOAD_MAP_DATA(secfile_lookup_str(file, "map.a%03d", y),
-                 map_get_tile(x, y)->known = ascii_hex2bin(ch, 0));
+                 map_get_tile(x, y)->known = ascii_hex2bin(ch[0], 0), 1);
     LOAD_MAP_DATA(secfile_lookup_str(file, "map.b%03d", y),
-                 map_get_tile(x, y)->known |= ascii_hex2bin(ch, 1));
+                 map_get_tile(x, y)->known |= ascii_hex2bin(ch[0], 1), 1);
     LOAD_MAP_DATA(secfile_lookup_str(file, "map.c%03d", y),
-                 map_get_tile(x, y)->known |= ascii_hex2bin(ch, 2));
+                 map_get_tile(x, y)->known |= ascii_hex2bin(ch[0], 2), 1);
     LOAD_MAP_DATA(secfile_lookup_str(file, "map.d%03d", y),
-                 map_get_tile(x, y)->known |= ascii_hex2bin(ch, 3));
+                 map_get_tile(x, y)->known |= ascii_hex2bin(ch[0], 3), 1);
if (has_capability("known32fix", savefile_options)) {
       /* get 4-bit segments of the second half of the 32-bit "known" field */
       LOAD_MAP_DATA(secfile_lookup_str(file, "map.e%03d", y),
-                   map_get_tile(x, y)->known |= ascii_hex2bin(ch, 4));
+                   map_get_tile(x, y)->known |= ascii_hex2bin(ch[0], 4), 1);
       LOAD_MAP_DATA(secfile_lookup_str(file, "map.g%03d", y),
-                   map_get_tile(x, y)->known |= ascii_hex2bin(ch, 5));
+                   map_get_tile(x, y)->known |= ascii_hex2bin(ch[0], 5), 1);
       LOAD_MAP_DATA(secfile_lookup_str(file, "map.h%03d", y),
-                   map_get_tile(x, y)->known |= ascii_hex2bin(ch, 6));
+                   map_get_tile(x, y)->known |= ascii_hex2bin(ch[0], 6), 1);
       LOAD_MAP_DATA(secfile_lookup_str(file, "map.i%03d", y),
-                   map_get_tile(x, y)->known |= ascii_hex2bin(ch, 7));
+                   map_get_tile(x, y)->known |= ascii_hex2bin(ch[0], 7), 1);
     }
   }
@@ -1167,37 +1202,37 @@ static void player_map_load(struct playe
       && game.load_options.load_private_map) {
     LOAD_MAP_DATA(secfile_lookup_str(file, "player%d.map_t%03d", plrno, y),
                  map_get_player_tile(x, y, plr)->terrain =
-                 char2terrain(ch));
+                 char2terrain(ch[0]), 1);
/* get 4-bit segments of 12-bit "special" field. */
     LOAD_MAP_DATA(secfile_lookup_str(file, "player%d.map_l%03d", plrno, y),
                  map_get_player_tile(x, y, plr)->special =
-                 ascii_hex2bin(ch, 0));
+                 ascii_hex2bin(ch[0], 0), 1);
     LOAD_MAP_DATA(secfile_lookup_str(file, "player%d.map_u%03d", plrno, y),
                  map_get_player_tile(x, y, plr)->special |=
-                 ascii_hex2bin(ch, 1));
+                 ascii_hex2bin(ch[0], 1), 1);
     LOAD_MAP_DATA(secfile_lookup_str_default
                  (file, NULL, "player%d.map_n%03d", plrno, y),
                  map_get_player_tile(x, y, plr)->special |=
-                 ascii_hex2bin(ch, 2));
+                 ascii_hex2bin(ch[0], 2), 1);
/* get 4-bit segments of 16-bit "updated" field */
     LOAD_MAP_DATA(secfile_lookup_str
                  (file, "player%d.map_ua%03d", plrno, y),
                  map_get_player_tile(x, y, plr)->last_updated =
-                 ascii_hex2bin(ch, 0));
+                 ascii_hex2bin(ch[0], 0),1);
     LOAD_MAP_DATA(secfile_lookup_str
                  (file, "player%d.map_ub%03d", plrno, y),
                  map_get_player_tile(x, y, plr)->last_updated |=
-                 ascii_hex2bin(ch, 1));
+                 ascii_hex2bin(ch[0], 1),1);
     LOAD_MAP_DATA(secfile_lookup_str
                  (file, "player%d.map_uc%03d", plrno, y),
                  map_get_player_tile(x, y, plr)->last_updated |=
-                 ascii_hex2bin(ch, 2));
+                 ascii_hex2bin(ch[0], 2),1);
     LOAD_MAP_DATA(secfile_lookup_str
                  (file, "player%d.map_ud%03d", plrno, y),
                  map_get_player_tile(x, y, plr)->last_updated |=
-                 ascii_hex2bin(ch, 3));
+                 ascii_hex2bin(ch[0], 3),1);
{
       int j;
@@ -1778,34 +1813,49 @@ void game_load(struct section_file *file
                                          "game.metaserver"));
     meta_addr_split();
- game.gold = secfile_lookup_int(file, "game.gold");
-    game.tech          = secfile_lookup_int(file, "game.tech");
-    game.skill_level   = secfile_lookup_int(file, "game.skill_level");
+ game.gold = + secfile_lookup_int_default(file, game.gold, "game.gold"); + game.tech = + secfile_lookup_int_default(file, game.tech, "game.tech");
+    game.skill_level =
+      secfile_lookup_int_default(file, game.skill_level, "game.skill_level");
     if (game.skill_level==0)
       game.skill_level = GAME_OLD_DEFAULT_SKILL_LEVEL;
- game.timeout = secfile_lookup_int(file, "game.timeout");
-    game.timeoutint = secfile_lookup_int_default(file,
-                                                GAME_DEFAULT_TIMEOUTINT,
-                                                "game.timeoutint");
+ game.timeout = + secfile_lookup_int_default(file, game.timeout, "game.timeout"); + game.timeoutint = + secfile_lookup_int_default(file, game.timeoutint, "game.timeoutint");
     game.timeoutintinc =
-       secfile_lookup_int_default(file, GAME_DEFAULT_TIMEOUTINTINC,
-                                  "game.timeoutintinc");
+ secfile_lookup_int_default(file, game.timeoutintinc, + "game.timeoutintinc");
     game.timeoutinc =
-       secfile_lookup_int_default(file, GAME_DEFAULT_TIMEOUTINC,
-                                  "game.timeoutinc");
+      secfile_lookup_int_default(file, game.timeoutinc, "game.timeoutinc");
     game.timeoutincmult =
-       secfile_lookup_int_default(file, GAME_DEFAULT_TIMEOUTINCMULT,
-                                  "game.timeoutincmult");
+ secfile_lookup_int_default(file, game.timeoutincmult, + "game.timeoutincmult");
     game.timeoutcounter =
-       secfile_lookup_int_default(file, 1, "game.timeoutcounter");
+ secfile_lookup_int_default(file, game.timeoutcounter, + "game.timeoutcounter"); - game.end_year = secfile_lookup_int(file, "game.end_year");
-    game.researchcost  = secfile_lookup_int_default(file, 0, 
"game.researchcost");
-    if (game.researchcost == 0)
-      game.researchcost = secfile_lookup_int(file, "game.techlevel");
+    game.end_year =
+      secfile_lookup_int_default(file, game.end_year, "game.end_year");
+ + { + /* Savefiles as recent as 1.9.0 called researchcost "techlevel. + * So save the current researchcost, and try to load researchcost + * from the file; it we fail, try to load techlevel, but fall back + * to what was there before. */
+      int rcost = game.researchcost;
+      game.researchcost =
+       secfile_lookup_int_default(file, 0, "game.researchcost");
+      if (game.researchcost == 0) {
+ game.researchcost = + secfile_lookup_int_default(file, rcost, "game.techlevel");
+      }
+    }
- game.year = secfile_lookup_int(file, "game.year");
+    game.year = secfile_lookup_int_default(file, game.year, "game.year");
if (has_capability("turn", savefile_options)) {
       game.turn = secfile_lookup_int(file, "game.turn");
@@ -1813,72 +1863,87 @@ void game_load(struct section_file *file
       game.turn = -2;
     }
- game.min_players = secfile_lookup_int(file, "game.min_players");
-    game.max_players   = secfile_lookup_int(file, "game.max_players");
-    game.nplayers      = secfile_lookup_int(file, "game.nplayers");
-    game.globalwarming = secfile_lookup_int(file, "game.globalwarming");
-    game.warminglevel  = secfile_lookup_int(file, "game.warminglevel");
-    game.nuclearwinter = secfile_lookup_int_default(file, 0, 
"game.nuclearwinter");
-    game.coolinglevel  = secfile_lookup_int_default(file, 8, 
"game.coolinglevel");
-    game.notradesize   = secfile_lookup_int_default(file, 0, 
"game.notradesize");
-    game.fulltradesize = secfile_lookup_int_default(file, 1, 
"game.fulltradesize");
-    game.unhappysize   = secfile_lookup_int(file, "game.unhappysize");
-    game.angrycitizen  = secfile_lookup_bool_default(file, FALSE, 
"game.angrycitizen");
-
-    if (game.version >= 10100) {
-      game.cityfactor  = secfile_lookup_int(file, "game.cityfactor");
-      game.diplcost    = secfile_lookup_int(file, "game.diplcost");
-      game.freecost    = secfile_lookup_int(file, "game.freecost");
-      game.conquercost = secfile_lookup_int(file, "game.conquercost");
-      game.foodbox     = secfile_lookup_int(file, "game.foodbox");
-      game.techpenalty = secfile_lookup_int(file, "game.techpenalty");
-      game.razechance  = secfile_lookup_int(file, "game.razechance");
-
-      /* suppress warnings about unused entries in old savegames: */
-      (void) section_file_lookup(file, "game.rail_food");
-      (void) section_file_lookup(file, "game.rail_prod");
-      (void) section_file_lookup(file, "game.rail_trade");
-      (void) section_file_lookup(file, "game.farmfood");
-    }
-    if (game.version >= 10300) {
-      game.civstyle = secfile_lookup_int_default(file, 0, "game.civstyle");
-      game.save_nturns = secfile_lookup_int(file, "game.save_nturns");
-    }
+ game.min_players = + secfile_lookup_int_default(file, game.min_players, "game.min_players");
+    game.max_players =
+      secfile_lookup_int_default(file, game.max_players, "game.max_players");
+ game.nplayers = + secfile_lookup_int_default(file, game.nplayers, "game.nplayers");
+    game.globalwarming =
+ secfile_lookup_int_default(file, game.globalwarming, + "game.globalwarming"); + game.warminglevel = + secfile_lookup_int_default(file, game.warminglevel, "game.warminglevel");
+    game.nuclearwinter =
+      secfile_lookup_int_default(file, 0, "game.nuclearwinter");
+    game.coolinglevel =
+      secfile_lookup_int_default(file, 8, "game.coolinglevel");
+    game.notradesize =
+      secfile_lookup_int_default(file, 0, "game.notradesize");
+ game.fulltradesize = + secfile_lookup_int_default(file, 1, "game.fulltradesize");
+    game.unhappysize =
+      secfile_lookup_int_default(file, game.unhappysize, "game.unhappysize");
+    game.angrycitizen =
+      secfile_lookup_bool_default(file, FALSE, "game.angrycitizen");
+
+ /* These options are valid for game.version >= 1.1.0, + * but we already require 1.9.0. */ + game.cityfactor = + secfile_lookup_int_default(file, game.cityfactor, "game.cityfactor");
+    game.diplcost    =
+      secfile_lookup_int_default(file, game.diplcost, "game.diplcost");
+    game.freecost    =
+      secfile_lookup_int_default(file, game.freecost, "game.freecost");
+    game.conquercost =
+      secfile_lookup_int_default(file, game.conquercost, "game.conquercost");
+ game.foodbox = + secfile_lookup_int_default(file, game.foodbox, "game.foodbox"); + game.techpenalty = + secfile_lookup_int_default(file, game.techpenalty, "game.techpenalty"); + game.razechance = + secfile_lookup_int_default(file, game.razechance, "game.razechance"); + + /* These options are valid for game.version >= 1.3.0,
+     * but we already require 1.9.0. */
+    game.civstyle =
+      secfile_lookup_int_default(file, game.civstyle, "game.civstyle");
+    game.save_nturns =
+      secfile_lookup_int_default(file, game.save_nturns, "game.save_nturns");
game.citymindist = secfile_lookup_int_default(file,
-      GAME_DEFAULT_CITYMINDIST, "game.citymindist");
+      game.citymindist, "game.citymindist");
game.rapturedelay = secfile_lookup_int_default(file,
-      GAME_DEFAULT_RAPTUREDELAY, "game.rapturedelay");
+      game.rapturedelay, "game.rapturedelay");
- if (has_capability("watchtower", savefile_options)) {
+    /* If these aren't defined in the file, use the current values. */
       game.watchtower_extra_vision =
-         secfile_lookup_int(file, "game.watchtower_extra_vision");
+ secfile_lookup_int_default(file, game.watchtower_extra_vision, + "game.watchtower_extra_vision");
       game.watchtower_vision =
-         secfile_lookup_int(file, "game.watchtower_vision");
-    } else {
-      game.watchtower_extra_vision = 0;
-      game.watchtower_vision = 1;
-    }
+ secfile_lookup_int_default(file, game.watchtower_vision, + "game.watchtower_vision"); sz_strlcpy(game.save_name,
-              secfile_lookup_str_default(file, GAME_DEFAULT_SAVE_NAME,
+ secfile_lookup_str_default(file, game.save_name, "game.save_name")); - game.aifill = secfile_lookup_int_default(file, 0, "game.aifill"); + game.aifill = + secfile_lookup_int_default(file, game.aifill, "game.aifill"); - game.scorelog = secfile_lookup_bool_default(file, FALSE, "game.scorelog"); + game.scorelog = + secfile_lookup_bool_default(file, game.scorelog, "game.scorelog");
     sz_strlcpy(game.id, secfile_lookup_str_default(file, "", "game.id"));
- game.fogofwar = secfile_lookup_bool_default(file, FALSE, "game.fogofwar"); + game.fogofwar = secfile_lookup_bool_default(file, game.fogofwar, + "game.fogofwar");
     game.fogofwar_old = game.fogofwar;
- game.civilwarsize =
-      secfile_lookup_int_default(file, GAME_DEFAULT_CIVILWARSIZE,
-                                "game.civilwarsize");
+ game.civilwarsize = secfile_lookup_int_default(file, game.civilwarsize, + "game.civilwarsize");
     game.contactturns =
-      secfile_lookup_int_default(file, GAME_DEFAULT_CONTACTTURNS,
-                                "game.contactturns");
+      secfile_lookup_int_default(file, game.contactturns, "game.contactturns");
if(has_capability("diplchance_percent", savefile_options)) {
       game.diplchance = secfile_lookup_int_default(file, game.diplchance,
@@ -1980,64 +2045,120 @@ void game_load(struct section_file *file
   }
{
-    if (game.version >= 10300) {
-      if (game.load_options.load_settings) {
-       game.settlers = secfile_lookup_int(file, "game.settlers");
-       game.explorer = secfile_lookup_int(file, "game.explorer");
-       game.dispersion =
-         secfile_lookup_int_default(file, GAME_DEFAULT_DISPERSION, 
"game.dispersion");
-      }
-
-      map.riches = secfile_lookup_int(file, "map.riches");
-      map.huts = secfile_lookup_int(file, "map.huts");
-      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");
-
-      if (has_capability("startoptions", savefile_options)) {
-       map.xsize = secfile_lookup_int(file, "map.width");
-       map.ysize = secfile_lookup_int(file, "map.height");
-      } else {
-       /* old versions saved with these names in PRE_GAME_STATE: */
-       map.xsize = secfile_lookup_int(file, "map.xsize");
-       map.ysize = secfile_lookup_int(file, "map.ysize");
-      }
-
-      if (tmp_server_state==PRE_GAME_STATE
-         && map.generator == 0
-         && !has_capability("map_editor",savefile_options)) {
-       /* generator 0 = map done with map editor */
-       /* aka a "scenario" */
-        if (has_capability("specials",savefile_options)) {
-          map_load(file);
-         map.fixed_start_positions = TRUE;
-          return;
-        }
-        map_tiles_load(file);
-        if (has_capability("riversoverlay",savefile_options)) {
+    if (game.load_options.load_settings) {
+ game.settlers = + secfile_lookup_int_default(file, game.settlers, "game.settlers"); + game.explorer = + secfile_lookup_int_default(file, game.explorer, "game.explorer");
+      game.dispersion =
+       secfile_lookup_int_default(file, game.dispersion, "game.dispersion");
+    }
+ + map.riches = + secfile_lookup_int_default(file, map.riches, "map.riches"); + map.huts = + secfile_lookup_int_default(file, map.huts, "map.huts");
+    map.generator = /* Only broken maps (ie those made by external generators)
+                    * are likely to forget to set the generator. */
+      secfile_lookup_int_default(file, 0, "map.generator");
+    map.seed =
+      secfile_lookup_int_default(file, map.seed, "map.seed");
+ map.landpercent = + secfile_lookup_int_default(file, map.landpercent, "map.landpercent");
+    map.grasssize =
+      secfile_lookup_int_default(file, map.grasssize, "map.grasssize");
+ map.swampsize = + secfile_lookup_int_default(file, map.swampsize, "map.swampsize");
+    map.deserts =
+      secfile_lookup_int_default(file, map.deserts, "map.deserts");
+    map.riverlength =
+      secfile_lookup_int_default(file, map.riverlength, "map.riverlength");
+ map.mountains = + secfile_lookup_int_default(file, map.mountains, "map.mountains"); + map.forestsize = + secfile_lookup_int_default(file, map.forestsize, "map.forestsize"); + map.have_huts = + secfile_lookup_bool_default(file, TRUE, "map.have_huts"); + + /* extremely old savegames used xsize and ysize instead of
+     * width and height. But that was old even in in 1.9.0, which
+     * is the oldest savegame we support. */
+    map.xsize = secfile_lookup_int(file, "map.width");
+    map.ysize = secfile_lookup_int(file, "map.height");
+ + map.tinyisles = + secfile_lookup_bool_default(file, map.tinyisles, "map.tinyisles");
+    map.separatepoles =
+      secfile_lookup_bool_default(file, map.separatepoles,"map.separatepoles");
+
+    if (tmp_server_state==PRE_GAME_STATE) {
+      if(map.generator == 0) {
+       freelog(LOG_DEBUG, "Anticipating possibly-incomplete scenario map");
+
+       /* It's a map not generated by us, and may be incomplete.
+        * 1. Try to load height map.
+        * 2. Load tile map, or make a tile map from the height map
+        *    using the standard land placing algorithm, or
+        *    exit with an error.
+        * 3. Load specials, or make them up; load rivers, or make them up;
+        *    load starting positions, or make them up.
+        */
+       
+       /* try to load height map ... */
+       if(has_capability("heightmap", savefile_options)) {
+         freelog(LOG_DEBUG, "Reading heightmap");
+         height_map=fc_calloc (map.xsize*map.ysize, sizeof(int));
+
+         /* actually read it. */
+         map_height_load(file);
+ + /* now even if we have a height map, we may still have tiles,
+          * but not, for example, rivers. So if we have a height map,
+          * we have to say if we also have a tile map. */
+         if(has_capability("tilemap", savefile_options)) {
+           freelog(LOG_DEBUG, "Tilemap also exists; reading it");
+           map_tiles_load(file);
+         }
+       } else {
+         /* no height map; we must have a tile map. Load it. */
+         map_tiles_load(file);
+       }
+
+       /* load specials. If we don't have them here, we'll make them later. */
+       if(has_capability("specials", savefile_options)) {
+         map_specials_load(file);
+         map.have_specials = TRUE;
+       } else {
+         /* in mapgen we'll add specials */
+         map.have_specials = FALSE;
+       }
+
+       /* load rivers */
+       if(has_capability("riversoverlay", savefile_options)) {
          map_rivers_overlay_load(file);
+         map.have_rivers_overlay = TRUE;
+       } else {
+         /* in mapgen we'll make up some rivers */
+         map.have_rivers_overlay = FALSE;
        }
-        if (has_capability("startpos",savefile_options)) {
-          map_startpos_load(file);
-         map.fixed_start_positions = TRUE;
-          return;
-        }
+
+       if(has_capability("startpos", savefile_options)) {
+         /* load starting positions, and record that we have them.
+          * we'll make them up later if there aren't any. */
+         map_startpos_load(file);
+       }
+       
        return;
       }
-    }
-    if(tmp_server_state==PRE_GAME_STATE) {
+
+      /* server state is not PRE_GAME_STATE, but generator is non-zero. */
       return;
     }
   }
+ /* we can only get here if the server is not in PRE_GAME_STATE, which means
+   * it's a real savegame, not a scenario or partial map. */
+
   /* We check
      1) if the block exists at all.
      2) if it is saved. */
@@ -2181,7 +2302,7 @@ void game_save(struct section_file *file
     if (map.have_specials) {
       sz_strlcat(options, " specials");
     }
-    if (map.have_rivers_overlay && !map.have_specials) {
+    if (map.have_rivers_overlay) {
       sz_strlcat(options, " riversoverlay");
     }
   }
Index: server/srv_main.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/srv_main.c,v
retrieving revision 1.123
diff -u -3 -p -r1.123 srv_main.c
--- server/srv_main.c   2003/04/17 20:06:37     1.123
+++ server/srv_main.c   2003/04/24 01:14:50
@@ -1545,7 +1545,7 @@ void srv_main(void)
con_flush(); - game_init();
+  full_game_init();
/* init network */ init_connections();



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