diff --git a/client/mapview_common.c b/client/mapview_common.c
index d317764f8c..3cb1c353e2 100644
--- a/client/mapview_common.c
+++ b/client/mapview_common.c
@@ -765,10 +765,10 @@ static void normalize_gui_pos(const struct tileset *t,
    * we wrap even if the map position is unreal, which normalize_map_pos
    * doesn't necessarily do. */
   MAP_TO_NATIVE_POS(&nat_x, &nat_y, map_x, map_y);
-  if (current_topo_has_flag(TF_WRAPX)) {
+  if (current_wrap_has_flag(WRAP_X)) {
     nat_x = FC_WRAP(nat_x, wld.map.xsize);
   }
-  if (current_topo_has_flag(TF_WRAPY)) {
+  if (current_wrap_has_flag(WRAP_Y)) {
     nat_y = FC_WRAP(nat_y, wld.map.ysize);
   }
   NATIVE_TO_MAP_POS(&map_x, &map_y, nat_x, nat_y);
@@ -940,11 +940,11 @@ static bool calc_mapview_origin(float *gui_x0, float *gui_y0)
    * while clipping is done in scroll (native) positions. */
   get_mapview_scroll_window(&xmin, &ymin, &xmax, &ymax, &xsize, &ysize);
 
-  if (!current_topo_has_flag(TF_WRAPX)) {
+  if (!current_wrap_has_flag(WRAP_X)) {
     *gui_x0 = CLIP(xmin, *gui_x0, xmax - xsize);
   }
 
-  if (!current_topo_has_flag(TF_WRAPY)) {
+  if (!current_wrap_has_flag(WRAP_Y)) {
     *gui_y0 = CLIP(ymin, *gui_y0, ymax - ysize);
   }
 
@@ -1083,13 +1083,13 @@ void get_mapview_scroll_window(float *xmin, float *ymin,
      * iso-view or a full tile in non-iso view.  The above math already has
      * taken care of some of this so all that's left is to fix the corner
      * cases. */
-    if (current_topo_has_flag(TF_WRAPX)) {
+    if (current_wrap_has_flag(WRAP_X)) {
       *xmax += *xsize;
 
       /* We need to be able to scroll a little further to the left. */
       *xmin -= tileset_tile_width(tileset) * map_zoom;
     }
-    if (current_topo_has_flag(TF_WRAPY)) {
+    if (current_wrap_has_flag(WRAP_Y)) {
       *ymax += *ysize;
 
       /* We need to be able to scroll a little further up. */
@@ -1255,19 +1255,19 @@ bool tile_visible_and_not_on_border_mapcanvas(struct tile *ptile)
    * border, then it's a border tile.  We can only really check the
    * scrolling when the mapview window lines up with the map. */
   if (canvas_x < border_x
-      && (!same || scroll_x > xmin || current_topo_has_flag(TF_WRAPX))) {
+      && (!same || scroll_x > xmin || current_wrap_has_flag(WRAP_X))) {
     return FALSE;
   }
   if (canvas_y < border_y
-      && (!same || scroll_y > ymin || current_topo_has_flag(TF_WRAPY))) {
+      && (!same || scroll_y > ymin || current_wrap_has_flag(WRAP_Y))) {
     return FALSE;
   }
   if (canvas_x + tileset_tile_width(tileset) * map_zoom > mapview.width - border_x
-      && (!same || scroll_x + xsize < xmax || current_topo_has_flag(TF_WRAPX))) {
+      && (!same || scroll_x + xsize < xmax || current_wrap_has_flag(WRAP_X))) {
     return FALSE;
   }
   if (canvas_y + tileset_tile_height(tileset) * map_zoom > mapview.height - border_y
-      && (!same || scroll_y + ysize < ymax || current_topo_has_flag(TF_WRAPY))) {
+      && (!same || scroll_y + ysize < ymax || current_wrap_has_flag(WRAP_Y))) {
     return FALSE;
   }
 
@@ -3209,7 +3209,7 @@ static bool can_do_cached_drawing(void)
    *
    * The logic below is complicated and determined in part by
    * trial-and-error. */
-  if (!current_topo_has_flag(TF_WRAPX) && !current_topo_has_flag(TF_WRAPY)) {
+  if (!current_wrap_has_flag(WRAP_X) && !current_wrap_has_flag(WRAP_Y)) {
     /* An unwrapping map: no limitation.  On an unwrapping map no tile can
      * be visible twice so there's no problem. */
     return TRUE;
@@ -3235,11 +3235,11 @@ static bool can_do_cached_drawing(void)
 
     /* Now we can use the full width and height, with the exception of a small
      * area on each side. */
-    if (current_topo_has_flag(TF_WRAPX)
+    if (current_wrap_has_flag(WRAP_X)
 	&& w > (NATURAL_WIDTH - isodiff) * W / isofactor) {
       return FALSE;
     }
-    if (current_topo_has_flag(TF_WRAPY)
+    if (current_wrap_has_flag(WRAP_Y)
 	&& h > (NATURAL_HEIGHT - isodiff) * H / isofactor) {
       return FALSE;
     }
diff --git a/client/overview_common.c b/client/overview_common.c
index f3f78143f6..f872a6d3e0 100644
--- a/client/overview_common.c
+++ b/client/overview_common.c
@@ -83,8 +83,8 @@ static void gui_to_natural_pos(const struct tileset *t,
   Translate from gui to overview coordinate systems.
 ****************************************************************************/
 static void gui_to_overview_pos(const struct tileset *t,
-				int *ovr_x, int *ovr_y,
-				int gui_x, int gui_y)
+                                int *ovr_x, int *ovr_y,
+                                int gui_x, int gui_y)
 {
   double ntl_x, ntl_y;
 
@@ -95,7 +95,7 @@ static void gui_to_overview_pos(const struct tileset *t,
   *ovr_y = floor((ntl_y - (double)gui_options.overview.map_y0) * OVERVIEW_TILE_SIZE);
 
   /* Now do additional adjustments.  See map_to_overview_pos(). */
-  if (current_topo_has_flag(TF_WRAPX)) {
+  if (current_wrap_has_flag(WRAP_X)) {
     *ovr_x = FC_WRAP(*ovr_x, NATURAL_WIDTH * OVERVIEW_TILE_SIZE);
   } else {
     if (MAP_IS_ISOMETRIC) {
@@ -107,7 +107,8 @@ static void gui_to_overview_pos(const struct tileset *t,
       *ovr_x -= OVERVIEW_TILE_SIZE;
     }
   }
-  if (current_topo_has_flag(TF_WRAPY)) {
+
+  if (current_wrap_has_flag(WRAP_Y)) {
     *ovr_y = FC_WRAP(*ovr_y, NATURAL_HEIGHT * OVERVIEW_TILE_SIZE);
   }
 }
@@ -290,19 +291,19 @@ void center_tile_overviewcanvas(void)
   int ox, oy;
 
   gui_to_natural_pos(tileset, &ntl_x, &ntl_y,
-		     mapview.gui_x0 + mapview.width / 2,
-		     mapview.gui_y0 + mapview.height / 2);
+                     mapview.gui_x0 + mapview.width / 2,
+                     mapview.gui_y0 + mapview.height / 2);
 
   /* NOTE: this embeds the map wrapping in the overview code.  This is
    * basically necessary for the overview to be efficiently
    * updated. */
-  if (current_topo_has_flag(TF_WRAPX)) {
+  if (current_wrap_has_flag(WRAP_X)) {
     gui_options.overview.map_x0 = wrap_double(ntl_x - (double)NATURAL_WIDTH / 2.0,
                                               NATURAL_WIDTH);
   } else {
     gui_options.overview.map_x0 = 0;
   }
-  if (current_topo_has_flag(TF_WRAPY)) {
+  if (current_wrap_has_flag(WRAP_Y)) {
     gui_options.overview.map_y0 = wrap_double(ntl_y - (double)NATURAL_HEIGHT / 2.0,
                                               NATURAL_HEIGHT);
   } else {
@@ -330,7 +331,7 @@ void map_to_overview_pos(int *overview_x, int *overview_y,
     int ovr_x = ntl_x - gui_options.overview.map_x0;
     int ovr_y = ntl_y - gui_options.overview.map_y0;
 
-    if (current_topo_has_flag(TF_WRAPX)) {
+    if (current_wrap_has_flag(WRAP_X)) {
       ovr_x = FC_WRAP(ovr_x, NATURAL_WIDTH);
     } else {
       if (MAP_IS_ISOMETRIC) {
@@ -342,7 +343,7 @@ void map_to_overview_pos(int *overview_x, int *overview_y,
         ovr_x--;
       }
     }
-    if (current_topo_has_flag(TF_WRAPY)) {
+    if (current_wrap_has_flag(WRAP_Y)) {
       ovr_y = FC_WRAP(ovr_y, NATURAL_HEIGHT);
     }
     *overview_x = OVERVIEW_TILE_SIZE * ovr_x;
@@ -359,7 +360,7 @@ void overview_to_map_pos(int *map_x, int *map_y,
   int ntl_x = overview_x / OVERVIEW_TILE_SIZE + gui_options.overview.map_x0;
   int ntl_y = overview_y / OVERVIEW_TILE_SIZE + gui_options.overview.map_y0;
 
-  if (MAP_IS_ISOMETRIC && !current_topo_has_flag(TF_WRAPX)) {
+  if (MAP_IS_ISOMETRIC && !current_wrap_has_flag(WRAP_X)) {
     /* Clip half tile left and right.  See comment in map_to_overview_pos. */
     ntl_x++;
   }
@@ -418,7 +419,7 @@ void overview_update_tile(struct tile *ptile)
     int overview_x = ntl_x * OVERVIEW_TILE_SIZE;
 
     if (MAP_IS_ISOMETRIC) {
-      if (current_topo_has_flag(TF_WRAPX)) {
+      if (current_wrap_has_flag(WRAP_X)) {
         if (overview_x > gui_options.overview.width - OVERVIEW_TILE_WIDTH) {
           /* This tile is shown half on the left and half on the right
            * side of the overview.  So we have to draw it in two parts. */
@@ -453,7 +454,7 @@ void calculate_overview_dimensions(void)
   static int recursion = 0; /* Just to be safe. */
 
   /* Clip half tile left and right.  See comment in map_to_overview_pos. */
-  int shift = (MAP_IS_ISOMETRIC && !current_topo_has_flag(TF_WRAPX)) ? -1 : 0;
+  int shift = (MAP_IS_ISOMETRIC && !current_wrap_has_flag(WRAP_X)) ? -1 : 0;
 
   if (recursion > 0 || wld.map.xsize <= 0 || wld.map.ysize <= 0) {
     return;
diff --git a/client/packhand.c b/client/packhand.c
index 37b2df54a4..cf3f421ebb 100644
--- a/client/packhand.c
+++ b/client/packhand.c
@@ -2152,9 +2152,10 @@ void handle_unit_short_info(const struct packet_unit_short_info *packet)
 /************************************************************************//**
   Server requested topology change.
 ****************************************************************************/
-void handle_set_topology(int topology_id)
+void handle_set_topology(int topology_id, int wrap_id)
 {
   wld.map.topology_id = topology_id;
+  wld.map.wrap_id = wrap_id;
 
   if (forced_tileset_name[0] == '\0'
       && (tileset_map_topo_compatible(topology_id, tileset, NULL)
@@ -2174,7 +2175,7 @@ void handle_set_topology(int topology_id)
   Receive information about the map size and topology from the server.  We
   initialize some global variables at the same time.
 ****************************************************************************/
-void handle_map_info(int xsize, int ysize, int topology_id)
+void handle_map_info(int xsize, int ysize, int topology_id, int wrap_id)
 {
   int ts_topo;
 
@@ -2192,6 +2193,7 @@ void handle_map_info(int xsize, int ysize, int topology_id)
   }
 
   wld.map.topology_id = topology_id;
+  wld.map.wrap_id = wrap_id;
 
   map_init_topology();
   main_map_allocate();
diff --git a/common/fc_types.h b/common/fc_types.h
index 8f3285a256..0e333d4303 100644
--- a/common/fc_types.h
+++ b/common/fc_types.h
@@ -562,9 +562,9 @@ typedef int Unit_Class_id;
  * Changing the names will break file format compatibility. */
 #define SPECENUM_NAME topo_flag
 #define SPECENUM_BITWISE
-#define SPECENUM_VALUE0 TF_WRAPX
+#define SPECENUM_VALUE0 TF_OLD_WRAPX
 #define SPECENUM_VALUE0NAME N_("WrapX")
-#define SPECENUM_VALUE1 TF_WRAPY
+#define SPECENUM_VALUE1 TF_OLD_WRAPY
 #define SPECENUM_VALUE1NAME N_("WrapY")
 #define SPECENUM_VALUE2 TF_ISO
 #define SPECENUM_VALUE2NAME N_("ISO")
@@ -573,6 +573,14 @@ typedef int Unit_Class_id;
 #define TOPO_FLAG_BITS  4
 #include "specenum_gen.h"
 
+#define SPECENUM_NAME wrap_flag
+#define SPECENUM_BITWISE
+#define SPECENUM_VALUE0 WRAP_X
+#define SPECENUM_VALUE0NAME N_("WrapX")
+#define SPECENUM_VALUE1 WRAP_Y
+#define SPECENUM_VALUE1NAME N_("WrapY")
+#include "specenum_gen.h"
+
 /* Used in the network protocol. */
 #define SPECENUM_NAME impr_genus_id
 #define SPECENUM_VALUE0 IG_GREAT_WONDER
diff --git a/common/map.c b/common/map.c
index ad0ff45331..c4ab2b91d9 100644
--- a/common/map.c
+++ b/common/map.c
@@ -156,6 +156,7 @@ bool map_is_empty(void)
 void map_init(struct civ_map *imap, bool server_side)
 {
   imap->topology_id = MAP_DEFAULT_TOPO;
+  imap->wrap_id = MAP_DEFAULT_WRAP;
   imap->num_continents = 0;
   imap->num_oceans = 0;
   imap->tiles = NULL;
@@ -238,13 +239,13 @@ static void generate_map_indices(void)
    * case we're not concerned with going too far and wrapping around, so we
    * just have to make sure we go far enough if we're at one edge of the
    * map. */
-  nat_min_x = (current_topo_has_flag(TF_WRAPX) ? 0 : (nat_center_x - wld.map.xsize + 1));
-  nat_min_y = (current_topo_has_flag(TF_WRAPY) ? 0 : (nat_center_y - wld.map.ysize + 1));
+  nat_min_x = (current_wrap_has_flag(WRAP_X) ? 0 : (nat_center_x - wld.map.xsize + 1));
+  nat_min_y = (current_wrap_has_flag(WRAP_Y) ? 0 : (nat_center_y - wld.map.ysize + 1));
 
-  nat_max_x = (current_topo_has_flag(TF_WRAPX)
+  nat_max_x = (current_wrap_has_flag(WRAP_X)
 	       ? (wld.map.xsize - 1)
 	       : (nat_center_x + wld.map.xsize - 1));
-  nat_max_y = (current_topo_has_flag(TF_WRAPY)
+  nat_max_y = (current_wrap_has_flag(WRAP_Y)
 	       ? (wld.map.ysize - 1)
 	       : (nat_center_y + wld.map.ysize - 1));
   tiles = (nat_max_x - nat_min_x + 1) * (nat_max_y - nat_min_y + 1);
@@ -395,12 +396,12 @@ static inline struct tile *base_native_pos_to_tile(const struct civ_map *nmap,
   /* Wrap in X and Y directions, as needed. */
   /* If the position is out of range in a non-wrapping direction, it is
    * unreal. */
-  if (current_topo_has_flag(TF_WRAPX)) {
+  if (current_wrap_has_flag(WRAP_X)) {
     nat_x = FC_WRAP(nat_x, wld.map.xsize);
   } else if (nat_x < 0 || nat_x >= wld.map.xsize) {
     return NULL;
   }
-  if (current_topo_has_flag(TF_WRAPY)) {
+  if (current_wrap_has_flag(WRAP_Y)) {
     nat_y = FC_WRAP(nat_y, wld.map.ysize);
   } else if (nat_y < 0 || nat_y >= wld.map.ysize) {
     return NULL;
@@ -987,10 +988,10 @@ struct tile *nearest_real_tile(const struct civ_map *nmap, int x, int y)
   int nat_x, nat_y;
 
   MAP_TO_NATIVE_POS(&nat_x, &nat_y, x, y);
-  if (!current_topo_has_flag(TF_WRAPX)) {
+  if (!current_wrap_has_flag(WRAP_X)) {
     nat_x = CLIP(0, nat_x, wld.map.xsize - 1);
   }
-  if (!current_topo_has_flag(TF_WRAPY)) {
+  if (!current_wrap_has_flag(WRAP_Y)) {
     nat_y = CLIP(0, nat_y, wld.map.ysize - 1);
   }
   NATIVE_TO_MAP_POS(&x, &y, nat_x, nat_y);
@@ -1012,9 +1013,9 @@ int map_num_tiles(void)
   instead.
 ***********************************************************************/
 void base_map_distance_vector(int *dx, int *dy,
-			      int x0dv, int y0dv, int x1dv, int y1dv)
+                              int x0dv, int y0dv, int x1dv, int y1dv)
 {
-  if (current_topo_has_flag(TF_WRAPX) || current_topo_has_flag(TF_WRAPY)) {
+  if (current_wrap_has_flag(WRAP_X) || current_wrap_has_flag(WRAP_Y)) {
     /* Wrapping is done in native coordinates. */
     MAP_TO_NATIVE_POS(&x0dv, &y0dv, x0dv, y0dv);
     MAP_TO_NATIVE_POS(&x1dv, &y1dv, x1dv, y1dv);
@@ -1023,11 +1024,11 @@ void base_map_distance_vector(int *dx, int *dy,
      * map distance vector but is easier to wrap. */
     *dx = x1dv - x0dv;
     *dy = y1dv - y0dv;
-    if (current_topo_has_flag(TF_WRAPX)) {
+    if (current_wrap_has_flag(WRAP_X)) {
       /* Wrap dx to be in [-map.xsize/2, map.xsize/2). */
       *dx = FC_WRAP(*dx + wld.map.xsize / 2, wld.map.xsize) - wld.map.xsize / 2;
     }
-    if (current_topo_has_flag(TF_WRAPY)) {
+    if (current_wrap_has_flag(WRAP_Y)) {
       /* Wrap dy to be in [-map.ysize/2, map.ysize/2). */
       *dy = FC_WRAP(*dy + wld.map.ysize / 2, wld.map.ysize) - wld.map.ysize / 2;
     }
@@ -1417,10 +1418,10 @@ bool is_singular_tile(const struct tile *ptile, int dist)
     /* Iso-natural coordinates are doubled in scale. */
     dist *= MAP_IS_ISOMETRIC ? 2 : 1;
 
-    return ((!current_topo_has_flag(TF_WRAPX) 
-	     && (ntl_x < dist || ntl_x >= NATURAL_WIDTH - dist))
-	    || (!current_topo_has_flag(TF_WRAPY)
-		&& (ntl_y < dist || ntl_y >= NATURAL_HEIGHT - dist)));
+    return ((!current_wrap_has_flag(WRAP_X)
+             && (ntl_x < dist || ntl_x >= NATURAL_WIDTH - dist))
+            || (!current_wrap_has_flag(WRAP_Y)
+                && (ntl_y < dist || ntl_y >= NATURAL_HEIGHT - dist)));
   } do_in_natural_pos_end;
 }
 
diff --git a/common/map.h b/common/map.h
index 8f96883f94..3d6fa672bc 100644
--- a/common/map.h
+++ b/common/map.h
@@ -40,10 +40,14 @@ static const bool C_PERCENT = TRUE;
 #define MAP_IS_ISOMETRIC (CURRENT_TOPOLOGY & (TF_ISO + TF_HEX))
 
 #define CURRENT_TOPOLOGY (wld.map.topology_id)
+#define CURRENT_WRAP (wld.map.wrap_id)
 
 #define topo_has_flag(topo, flag) (((topo) & (flag)) != 0)
 #define current_topo_has_flag(flag) topo_has_flag((CURRENT_TOPOLOGY), (flag))
 
+#define wrap_has_flag(wrap, flag) (((wrap) & (flag)) != 0)
+#define current_wrap_has_flag(flag) wrap_has_flag((CURRENT_WRAP), (flag))
+
 #define ALL_DIRECTIONS_CARDINAL() topo_has_flag((CURRENT_TOPOLOGY), TF_HEX)
 
 bool map_is_empty(void);
@@ -612,9 +616,11 @@ moves. Includes MAP_MAX_LINEAR_SIZE because a map can be non wrapping. */
 #define MAP_ORIGINAL_TOPO        TF_WRAPX
 #ifdef FREECIV_WEB
 /* Freeciv-web doesn't support isometric maps yet. */
-#define MAP_DEFAULT_TOPO         TF_WRAPX
+#define MAP_DEFAULT_TOPO         0
+#define MAP_DEFAULT_WRAP         WRAP_X
 #else /* FREECIV_WEB */
-#define MAP_DEFAULT_TOPO         (TF_WRAPX|TF_ISO|TF_HEX)
+#define MAP_DEFAULT_TOPO         (TF_ISO|TF_HEX)
+#define MAP_DEFAULT_WRAP         (WRAP_X)
 #endif /* FREECIV_WEB */
 
 #define MAP_DEFAULT_SEED         0
diff --git a/common/map_types.h b/common/map_types.h
index ce81c63ee9..dbaf10c705 100644
--- a/common/map_types.h
+++ b/common/map_types.h
@@ -70,6 +70,7 @@ enum map_startpos {
 
 struct civ_map {
   int topology_id;
+  int wrap_id;
   enum direction8 valid_dirs[8], cardinal_dirs[8];
   int num_valid_dirs, num_cardinal_dirs;
   struct iter_index *iterate_outwards_indices;
diff --git a/common/mapimg.c b/common/mapimg.c
index 2740cfba36..31abc1ba72 100644
--- a/common/mapimg.c
+++ b/common/mapimg.c
@@ -389,7 +389,8 @@ struct img {
   const struct rgbcolor **map;
 };
 
-static struct img *img_new(struct mapdef *mapdef, int topo, int xsize, int ysize);
+static struct img *img_new(struct mapdef *mapdef, int topo, int wrap,
+                           int xsize, int ysize);
 static void img_destroy(struct img *pimg);
 static inline void img_set_pixel(struct img *pimg, const int mindex,
                                  const struct rgbcolor *pcolor);
@@ -1374,7 +1375,8 @@ bool mapimg_create(struct mapdef *pmapdef, bool force, const char *savename,
     generate_save_name(savename, mapimgfile, sizeof(mapimgfile),
                        mapimg_generate_name(pmapdef));
 
-    pimg = img_new(pmapdef, CURRENT_TOPOLOGY, wld.map.xsize, wld.map.ysize);
+    pimg = img_new(pmapdef, CURRENT_TOPOLOGY, CURRENT_WRAP,
+                   wld.map.xsize, wld.map.ysize);
     img_createmap(pimg);
     if (!img_save(pimg, mapimgfile, path)) {
       ret = FALSE;
@@ -1397,7 +1399,8 @@ bool mapimg_create(struct mapdef *pmapdef, bool force, const char *savename,
       generate_save_name(savename, mapimgfile, sizeof(mapimgfile),
                          mapimg_generate_name(pmapdef));
 
-      pimg = img_new(pmapdef, CURRENT_TOPOLOGY, wld.map.xsize, wld.map.ysize);
+      pimg = img_new(pmapdef, CURRENT_TOPOLOGY, CURRENT_WRAP,
+                     wld.map.xsize, wld.map.ysize);
       img_createmap(pimg);
       if (!img_save(pimg, mapimgfile, path)) {
         ret = FALSE;
@@ -1444,7 +1447,7 @@ bool mapimg_colortest(const char *savename, const char *path)
 #define SIZE_X 16
 #define SIZE_Y 5
 
-  pimg = img_new(pmapdef, 0, SIZE_X + 2,
+  pimg = img_new(pmapdef, 0, 0, SIZE_X + 2,
                  SIZE_Y * (max_playercolor / SIZE_X) + 2);
 
   pixel = pimg->pixel_tile(NULL, NULL, FALSE);
@@ -1863,7 +1866,8 @@ static const struct toolkit *img_toolkit_get(enum imagetool tool)
 /************************************************************************//**
   Create a new image.
 ****************************************************************************/
-static struct img *img_new(struct mapdef *mapdef, int topo, int xsize, int ysize)
+static struct img *img_new(struct mapdef *mapdef, int topo, int wrap,
+                           int xsize, int ysize)
 {
   struct img *pimg;
 
@@ -1894,8 +1898,8 @@ static struct img *img_new(struct mapdef *mapdef, int topo, int xsize, int ysize
                          * TILE_SIZE;
 
       /* magic for isohexa: change size if wrapping in only one direction */
-      if ((topo_has_flag(topo, TF_WRAPX) && !topo_has_flag(topo, TF_WRAPY))
-          || (!topo_has_flag(topo, TF_WRAPX) && topo_has_flag(topo, TF_WRAPY))) {
+      if ((wrap_has_flag(wrap, WRAP_X) && !wrap_has_flag(wrap, WRAP_Y))
+          || (!wrap_has_flag(wrap, WRAP_X) && wrap_has_flag(wrap, WRAP_Y))) {
         pimg->imgsize.y += (pimg->mapsize.x - pimg->mapsize.y / 2) / 2
                            * TILE_SIZE;
       }
diff --git a/common/networking/packets.def b/common/networking/packets.def
index 49569f6e2e..2c2edd1580 100644
--- a/common/networking/packets.def
+++ b/common/networking/packets.def
@@ -626,6 +626,7 @@ PACKET_MAP_INFO = 17; sc, lsend
   XYSIZE xsize;
   XYSIZE ysize;
   UINT8 topology_id;
+  UINT8 wrap_id;
 end
 
 PACKET_NUKE_TILE_INFO = 18; sc, dsend,lsend
@@ -2044,6 +2045,7 @@ end
 
 PACKET_SET_TOPOLOGY = 253; sc
   UINT8 topology_id;
+  UINT8 wrap_id;
 end
 
 /************** Effects hash packets **********************/
diff --git a/fc_version b/fc_version
index 1f123af4b9..0e07578362 100755
--- a/fc_version
+++ b/fc_version
@@ -56,7 +56,7 @@ DEFAULT_FOLLOW_TAG=S3_2
 #   - No new mandatory capabilities can be added to the release branch; doing
 #     so would break network capability of supposedly "compatible" releases.
 #
-NETWORK_CAPSTRING="+Freeciv.Devel-3.2-2022.Feb.03"
+NETWORK_CAPSTRING="+Freeciv.Devel-3.2-2022.Feb.05"
 
 FREECIV_DISTRIBUTOR=""
 
diff --git a/server/generator/height_map.c b/server/generator/height_map.c
index 5624bd09fa..f13567ace9 100644
--- a/server/generator/height_map.c
+++ b/server/generator/height_map.c
@@ -196,14 +196,14 @@ liklihood of continents butting up to non-wrapped edges.
 **************************************************************************/
 void make_pseudofractal1_hmap(int extra_div)
 {
-  const bool xnowrap = !current_topo_has_flag(TF_WRAPX);
-  const bool ynowrap = !current_topo_has_flag(TF_WRAPY);
+  const bool xnowrap = !current_wrap_has_flag(WRAP_X);
+  const bool ynowrap = !current_wrap_has_flag(WRAP_Y);
 
-  /* 
+  /*
    * How many blocks should the x and y directions be divided into
-   * initially. 
+   * initially.
    */
-  const int xdiv = 5 + extra_div;		
+  const int xdiv = 5 + extra_div;
   const int ydiv = 5 + extra_div;
 
   int xdiv2 = xdiv + (xnowrap ? 1 : 0);
@@ -213,9 +213,9 @@ void make_pseudofractal1_hmap(int extra_div)
   int ymax = wld.map.ysize - (ynowrap ? 1 : 0);
   int x_current, y_current;
   /* just need something > log(max(xsize, ysize)) for the recursion */
-  int step = wld.map.xsize + wld.map.ysize; 
+  int step = wld.map.xsize + wld.map.ysize;
   /* edges are avoided more strongly as this increases */
-  int avoidedge = (100 - wld.map.server.landpercent) * step / 100 + step / 3; 
+  int avoidedge = (100 - wld.map.server.landpercent) * step / 100 + step / 3;
 
   height_map = fc_malloc(sizeof(*height_map) * MAP_INDEX_SIZE);
 
diff --git a/server/generator/mapgen.c b/server/generator/mapgen.c
index c9df51262e..6dff89c59b 100644
--- a/server/generator/mapgen.c
+++ b/server/generator/mapgen.c
@@ -2553,14 +2553,14 @@ fair_map_pos_tile(struct fair_tile *pmap, int x, int y)
 
   /* Wrap in X and Y directions, as needed. */
   if (nat_x < 0 || nat_x >= wld.map.xsize) {
-    if (current_topo_has_flag(TF_WRAPX)) {
+    if (current_wrap_has_flag(WRAP_X)) {
       nat_x = FC_WRAP(nat_x, wld.map.xsize);
     } else {
       return NULL;
     }
   }
   if (nat_y < 0 || nat_y >= wld.map.ysize) {
-    if (current_topo_has_flag(TF_WRAPY)) {
+    if (current_wrap_has_flag(WRAP_Y)) {
       nat_y = FC_WRAP(nat_y, wld.map.ysize);
     } else {
       return NULL;
@@ -2596,7 +2596,7 @@ fair_map_tile_border(struct fair_tile *pmap, struct fair_tile *ptile,
 
   index_to_native_pos(&nat_x, &nat_y, ptile - pmap);
 
-  if (!current_topo_has_flag(TF_WRAPX)
+  if (!current_wrap_has_flag(WRAP_X)
       && (nat_x < dist || nat_x >= wld.map.xsize - dist)) {
     return TRUE;
   }
@@ -2605,7 +2605,7 @@ fair_map_tile_border(struct fair_tile *pmap, struct fair_tile *ptile,
     dist *= 2;
   }
 
-  if (!current_topo_has_flag(TF_WRAPY)
+  if (!current_wrap_has_flag(WRAP_Y)
       && (nat_y < dist || nat_y >= wld.map.ysize - dist)) {
     return TRUE;
   }
@@ -3550,19 +3550,19 @@ static bool map_generate_fair_islands(void)
       }
 
       /* Make start point for teams. */
-      if (current_topo_has_flag(TF_WRAPX)) {
+      if (current_wrap_has_flag(WRAP_X)) {
         dx = fc_rand(wld.map.xsize);
       }
-      if (current_topo_has_flag(TF_WRAPY)) {
+      if (current_wrap_has_flag(WRAP_Y)) {
         dy = fc_rand(wld.map.ysize);
       }
       for (j = 0; j < teams_num; j++) {
         start_x[j] = (wld.map.xsize * (2 * j + 1)) / (2 * teams_num) + dx;
         start_y[j] = (wld.map.ysize * (2 * j + 1)) / (2 * teams_num) + dy;
-        if (current_topo_has_flag(TF_WRAPX)) {
+        if (current_wrap_has_flag(WRAP_X)) {
           start_x[j] = FC_WRAP(start_x[j], wld.map.xsize);
         }
-        if (current_topo_has_flag(TF_WRAPY)) {
+        if (current_wrap_has_flag(WRAP_Y)) {
           start_y[j] = FC_WRAP(start_y[j], wld.map.ysize);
         }
       }
diff --git a/server/generator/mapgen_topology.c b/server/generator/mapgen_topology.c
index a87c1dda95..89d297864c 100644
--- a/server/generator/mapgen_topology.c
+++ b/server/generator/mapgen_topology.c
@@ -53,12 +53,12 @@ int map_colatitude(const struct tile *ptile)
   index_to_map_pos(&tile_x, &tile_y, tile_index(ptile));
   do_in_natural_pos(ntl_x, ntl_y, tile_x, tile_y) {
     if (wld.map.server.single_pole) {
-      if (!current_topo_has_flag(TF_WRAPY)) {
+      if (!current_wrap_has_flag(WRAP_Y)) {
         /* Partial planetary map.  A polar zone is placed
          * at the top and the equator is at the bottom. */
         return MAX_COLATITUDE * ntl_y / (NATURAL_HEIGHT - 1);
       }
-      if (!current_topo_has_flag(TF_WRAPX)) {
+      if (!current_wrap_has_flag(WRAP_X)) {
         return MAX_COLATITUDE * ntl_x / (NATURAL_WIDTH -1);
       }
     }
@@ -96,13 +96,13 @@ int map_colatitude(const struct tile *ptile)
 	 / (NATURAL_HEIGHT / 2 - 1));
   } do_in_natural_pos_end;
 
-  if (!current_topo_has_flag(TF_WRAPY)) {
+  if (!current_wrap_has_flag(WRAP_Y)) {
     /* In an Earth-like topology the polar zones are at north and south.
      * This is equivalent to a Mercator projection. */
     return MAX_COLATITUDE * y;
   }
 
-  if (!current_topo_has_flag(TF_WRAPX) && current_topo_has_flag(TF_WRAPY)) {
+  if (!current_wrap_has_flag(WRAP_X) && current_wrap_has_flag(WRAP_Y)) {
     /* 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
@@ -286,8 +286,8 @@ static void set_sizes(double size, int Xratio, int Yratio)
 ****************************************************************************/
 static void get_ratios(int *x_ratio, int *y_ratio)
 {
-  if (current_topo_has_flag(TF_WRAPX)) {
-    if (current_topo_has_flag(TF_WRAPY)) {
+  if (current_wrap_has_flag(WRAP_X)) {
+    if (current_wrap_has_flag(WRAP_Y)) {
       /* Ratios for torus map. */
       *x_ratio = 1;
       *y_ratio = 1;
@@ -297,7 +297,7 @@ static void get_ratios(int *x_ratio, int *y_ratio)
       *y_ratio = 2;
     }
   } else {
-    if (current_topo_has_flag(TF_WRAPY)) {
+    if (current_wrap_has_flag(WRAP_Y)) {
       /* Ratios for uranus map. */
       *x_ratio = 2;
       *y_ratio = 3;
@@ -399,7 +399,7 @@ void generator_init_topology(bool autosize)
 
   /* correction for single pole (Flat Earth) */
   if (wld.map.server.single_pole) {
-    if (!current_topo_has_flag(TF_WRAPY) || !current_topo_has_flag(TF_WRAPX)) {
+    if (!current_wrap_has_flag(WRAP_Y) || !current_wrap_has_flag(WRAP_X)) {
       ice_base_colatitude /= 2;
     }
   }
diff --git a/server/maphand.c b/server/maphand.c
index d883838276..579e6710ad 100644
--- a/server/maphand.c
+++ b/server/maphand.c
@@ -633,6 +633,7 @@ void send_map_info(struct conn_list *dest)
   minfo.xsize = wld.map.xsize;
   minfo.ysize = wld.map.ysize;
   minfo.topology_id = wld.map.topology_id;
+  minfo.wrap_id = wld.map.wrap_id;
 
   lsend_packet_map_info(dest, &minfo);
 }
diff --git a/server/settings.c b/server/settings.c
index b0a0f290a6..316e0080e8 100644
--- a/server/settings.c
+++ b/server/settings.c
@@ -251,11 +251,23 @@ static const struct sset_val_name *mapsize_name(int mapsize)
 static const struct sset_val_name *topology_name(int topology_bit)
 {
   switch (1 << topology_bit) {
-  NAME_CASE(TF_WRAPX, "WRAPX", N_("Wrap East-West"));
-  NAME_CASE(TF_WRAPY, "WRAPY", N_("Wrap North-South"));
   NAME_CASE(TF_ISO, "ISO", N_("Isometric"));
   NAME_CASE(TF_HEX, "HEX", N_("Hexagonal"));
   }
+
+  return NULL;
+}
+
+/************************************************************************//**
+  Map wrap setting names accessor.
+****************************************************************************/
+static const struct sset_val_name *wrap_name(int wrap_bit)
+{
+  switch (1 << wrap_bit) {
+  NAME_CASE(WRAP_X, "WRAPX", N_("Wrap East-West"));
+  NAME_CASE(WRAP_Y, "WRAPY", N_("Wrap North-South"));
+  }
+
   return NULL;
 }
 
@@ -780,6 +792,22 @@ static void topology_action(const struct setting *pset)
   struct packet_set_topology packet;
 
   packet.topology_id = *pset->integer.pvalue;
+  packet.wrap_id = wld.map.wrap_id;
+
+  conn_list_iterate(game.est_connections, pconn) {
+    send_packet_set_topology(pconn, &packet);
+  } conn_list_iterate_end;
+}
+
+/************************************************************************//**
+  Map wrap setting changed.
+****************************************************************************/
+static void wrap_action(const struct setting *pset)
+{
+  struct packet_set_topology packet;
+
+  packet.topology_id = wld.map.topology_id;
+  packet.wrap_id = *pset->integer.pvalue;
 
   conn_list_iterate(game.est_connections, pconn) {
     send_packet_set_topology(pconn, &packet);
@@ -1256,11 +1284,11 @@ static bool topology_callback(unsigned value, struct connection *caller,
 #ifdef FREECIV_WEB
   /* Remember to update the help text too if Freeciv-web gets the ability
    * to display other map topologies. */
-  if ((value & (TF_WRAPY)) != 0
-      /* Are you removing this because Freeciv-web gained the ability to
-       * display isometric maps? Why don't you remove the Freeciv-web
-       * specific MAP_DEFAULT_TOPO too? */
-      || (value & (TF_ISO)) != 0
+
+  /* Are you removing this because Freeciv-web gained the ability to
+   * display isometric maps? Why don't you remove the Freeciv-web
+   * specific MAP_DEFAULT_TOPO too? */
+  if ((value & (TF_ISO)) != 0
       || (value & (TF_HEX)) != 0) {
     /* The Freeciv-web client can't display these topologies yet. */
     settings_snprintf(reject_msg, reject_msg_len,
@@ -1272,6 +1300,26 @@ static bool topology_callback(unsigned value, struct connection *caller,
   return TRUE;
 }
 
+/************************************************************************//**
+  Map wrap setting validation callback.
+****************************************************************************/
+static bool wrap_callback(unsigned value, struct connection *caller,
+                          char *reject_msg, size_t reject_msg_len)
+{
+#ifdef FREECIV_WEB
+  /* Remember to update the help text too if Freeciv-web gets the ability
+   * to display other map wraps. */
+  if ((value & (WRAP_Y)) != 0) {
+    /* The Freeciv-web client can't display wraps mapped this way. */
+    settings_snprintf(reject_msg, reject_msg_len,
+                      _("Freeciv-web doesn't support this map wrap."));
+    return FALSE;
+  }
+#endif /* FREECIV_WEB */
+
+  return TRUE;
+}
+
 /************************************************************************//**
   Validate that the player color mode can be used.
 ****************************************************************************/
@@ -1435,14 +1483,11 @@ static struct setting settings[] = {
               N_("Map topology"),
 #ifdef FREECIV_WEB
               /* TRANS: Freeciv-web version of the help text. */
-              N_("Freeciv-web maps are always two-dimensional. They may wrap "
-                 "at the east-west directions to form a flat map or a "
-                 "cylinder.\n"),
+              N_("Freeciv-web maps are always two-dimensional.\n"),
 #else /* FREECIV_WEB */
               /* TRANS: do not edit the ugly ASCII art */
-              N_("Freeciv maps are always two-dimensional. They may wrap at "
-                 "the north-south and east-west directions to form a flat "
-                 "map, a cylinder, or a torus (donut). Individual tiles may "
+              N_("Freeciv maps are always two-dimensional. "
+                 "Individual tiles may "
                  "be rectangular or hexagonal, with either an overhead "
                  "(\"classic\") or isometric alignment.\n"
                  "To play with a particular topology, clients will need a "
@@ -1464,6 +1509,22 @@ static struct setting settings[] = {
 #endif /* FREECIV_WEB */
               topology_callback, topology_action, topology_name, MAP_DEFAULT_TOPO)
 
+  GEN_BITWISE("wrap", wld.map.wrap_id, SSET_MAP_SIZE,
+              SSET_GEOLOGY, SSET_VITAL, ALLOW_NONE, ALLOW_BASIC,
+              N_("Map wrap"),
+#ifdef FREECIV_WEB
+              /* TRANS: Freeciv-web version of the help text. */
+              N_("Freeciv-web maps may wrap "
+                 "at the east-west directions to form a flat map or a "
+                 "cylinder.\n"),
+#else /* FREECIV_WEB */
+              /* TRANS: do not edit the ugly ASCII art */
+              N_("Freeciv maps may wrap at "
+                 "the north-south and east-west directions to form a flat "
+                 "map, a cylinder, or a torus (donut)."),
+#endif /* FREECIV_WEB */
+              wrap_callback, wrap_action, wrap_name, MAP_DEFAULT_WRAP)
+
   GEN_ENUM("generator", wld.map.server.generator,
            SSET_MAP_GEN, SSET_GEOLOGY, SSET_VITAL, ALLOW_NONE, ALLOW_BASIC,
            N_("Method used to generate map"),