Complete.Org: Mailing Lists: Archives: freeciv-dev: March 2004:
[Freeciv-Dev] Re: (PR#7481) meta-ticket for hex maps
Home

[Freeciv-Dev] Re: (PR#7481) meta-ticket for hex maps

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
To: undisclosed-recipients: ;
Subject: [Freeciv-Dev] Re: (PR#7481) meta-ticket for hex maps
From: "Jason Short" <jdorje@xxxxxxxxxxxxxxxxxxxxx>
Date: Wed, 31 Mar 2004 13:11:21 -0800
Reply-to: rt@xxxxxxxxxxx

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

Jason Short wrote:
> <URL: http://rt.freeciv.org/Ticket/Display.html?id=7481 >
> 
> A hex map is a map with hexagonal tiles.  These tiles can come in two forms:
> 
>    ____       /\
>   /    \     /  \
>  /      \    |  |
>  \      /    |  |
>   \____/     \  /
>               \/

OK, throw out everything I said before, except the above.  I will 
henceforth rename these iso-hex (on the left) and hex (on the right).

All of the following has been tested for iso-hex.  I haven't tested hex 
at all, since I don't have a hex tileset.

Hex, iso, and non-iso view are all compatible.  They can all use the 
same map coordinate representation.  However if the view doesn't match 
up with the map you'll get a bad interface.

So, we have two separate tasks: implementing hex maps, and implementing 
a hex view.


We'll start with hex view.  Hex view can be done in almost exactly the 
same way as iso-view!  A while ago Andreas Rosdal made an iso-hex view 
tileset as an extension of iso-view.  You can see a screenshot at

   http://www.freeciv.org/screenshots/1.14.1/index.phtml?only=isophex

and download the tileset at

   ftp://ftp.freeciv.org/freeciv/contrib/tilesets/isophex/

Since iso-view is typically tilted this means the cartesian (map) 
coordinate system for a hex map should be tilted with reference to the 
mapview.  In other words the native and cartesian coordinate systems for 
iso-hex-maps should be the same as for iso-maps (this comes into play 
later).

Only a very few changes are needed to iso-view to support hex maps. 
I've implemented this by setting

   is_isometric = 1
   hex_side = 16

in the tileset.  The hex side specifies how long the "extra" side (in he 
iso-hex-view case, top and bottom) of the hexagon is.  A small amount of 
extra code is needed to support hex-view:

   hex_isometric = 1

the representation of these in the tileset fairly inconsequential. 
What's significant is that both types of hex maps should have 
is_isometric set.

A little bit of extra code is needed in the drawing.  The only thing I 
could find that didn't work solely by changing the tileset sprites is 
the map grid.  Here we need to draw the extra side, which is where the 
hex_side (and potentially hex_isometric) variables come into play.


Now for hex maps.  Both hex and iso-hex will use the same cartesian 
representation; however, this representation is only an approximation. 
We have to filter out certain directions.  This amounts to changing 
is_valid_dir so that southeast and northwest are excluded for a hex-map 
and northeast and southwest excluded for an iso-hex-map.  This change is 
made direclty in is_valid_dir; then we just need to use is_valid_dir in 
a few more places (basically every where DIRSTEP is used).

The topology_id needs an extra bit.  Now there are 16 possible 
topologies.  Marcelo's suggestion of simplifying the list of choices 
becomes more attractive.

The next change is to the map_distance functions.  map_distance_vector 
remains the same but isn't quite as useful.  map_distance, 
real_map_distance, and sq_map_distance should all be the same.  They can 
call map_distance_vector but must interpret the vector a bit to get 
their return values.

There are some mapgen changes needed.  These are fairly non-critical; 
things like make_polar and make_passable need tweaking to get the right 
combinations.

Probably the hardest part is changing the citymap.  In a hex map of 
course the citymap should just be a hex circle.  Changing this will 
probably be fairly hard; something along the lines of Remi's city radius 
work will probably do it.  But this isn't critical for an initial 
implementation.

And that's it!  With these changes, things pretty much work.

Try the attached patch (gtk2 only) with /set topology 13 and the isophex 
tileset from above.  See

   http://freeciv.org/~jdorje/hex.png
   http://freeciv.org/~jdorje/iso.png
   http://freeciv.org/~jdorje/non-iso.png

These screenshots all show an iso-hex map (topology 13) with different 
tilesets.  The hex tileset is obviously the best since it shows the tile 
relations properly.  With the other two, moves in certain directions 
will fail.  Of course you won't see this in a screenshot; you'll only 
see it if you play the game.

jason

P.S.  Much thanks to Andreas Rosdal for creating the isophex tileset! 
Now we just need a hex tileset tilted the other way...


? cma_weirdness
? data/civ3
? data/womoks
Index: client/mapview_common.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/mapview_common.c,v
retrieving revision 1.98
diff -u -r1.98 mapview_common.c
--- client/mapview_common.c     29 Mar 2004 19:17:06 -0000      1.98
+++ client/mapview_common.c     31 Mar 2004 21:09:35 -0000
@@ -1467,6 +1467,7 @@
   }
   canvas_src_x += NORMAL_TILE_WIDTH/2;
   canvas_src_y += NORMAL_TILE_HEIGHT/2;
+  assert(is_valid_dir(dir));
   DIRSTEP(canvas_dest_x, canvas_dest_y, dir);
   canvas_dest_x = canvas_src_x + (NORMAL_TILE_WIDTH * canvas_dest_x) / 2;
   canvas_dest_y = canvas_src_y + (NORMAL_TILE_WIDTH * canvas_dest_y) / 2;
Index: client/tilespec.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/tilespec.c,v
retrieving revision 1.155
diff -u -r1.155 tilespec.c
--- client/tilespec.c   30 Mar 2004 19:07:31 -0000      1.155
+++ client/tilespec.c   31 Mar 2004 21:09:35 -0000
@@ -76,6 +76,7 @@
 int OVERVIEW_TILE_HEIGHT = 2;
 
 bool is_isometric;
+int hex_side;
 
 char *city_names_font;
 char *city_productions_font_name;
@@ -662,6 +663,7 @@
     free(fname);
     return tilespec_read_toplevel(NULL);
   }
+  hex_side = secfile_lookup_int_default(file, 0, "tilespec.hex_side");
 
   NORMAL_TILE_WIDTH = secfile_lookup_int(file, "tilespec.normal_tile_width");
   NORMAL_TILE_HEIGHT = secfile_lookup_int(file, "tilespec.normal_tile_height");
Index: client/tilespec.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/tilespec.h,v
retrieving revision 1.59
diff -u -r1.59 tilespec.h
--- client/tilespec.h   30 Mar 2004 19:07:32 -0000      1.59
+++ client/tilespec.h   31 Mar 2004 21:09:35 -0000
@@ -270,6 +270,7 @@
 extern int OVERVIEW_TILE_HEIGHT;
 
 extern bool is_isometric;
+extern int hex_side;
 
 /* name of font to use to draw city names on main map */
 
Index: client/gui-gtk-2.0/mapview.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/gui-gtk-2.0/mapview.c,v
retrieving revision 1.112
diff -u -r1.112 mapview.c
--- client/gui-gtk-2.0/mapview.c        31 Mar 2004 18:42:45 -0000      1.112
+++ client/gui-gtk-2.0/mapview.c        31 Mar 2004 21:09:36 -0000
@@ -1204,15 +1204,15 @@
 
   /*** Map grid ***/
   if (draw_map_grid && !tile_hilited) {
-    /* we draw the 2 lines on top of the tile; the buttom lines will be
+    /* we draw the 3 lines on top of the tile; the buttom lines will be
        drawn by the tiles underneath. */
     if (draw & D_M_R) {
       gdk_gc_set_foreground(thin_line_gc,
                            colors_standard[get_grid_color
                                            (x, y, x, y - 1)]);
       gdk_draw_line(pm, thin_line_gc,
-                   canvas_x + NORMAL_TILE_WIDTH / 2, canvas_y,
-                   canvas_x + NORMAL_TILE_WIDTH,
+                   canvas_x + (NORMAL_TILE_WIDTH + hex_side) / 2, canvas_y,
+                   canvas_x + NORMAL_TILE_WIDTH - hex_side / 2,
                    canvas_y + NORMAL_TILE_HEIGHT / 2);
     }
 
@@ -1221,8 +1221,18 @@
                            colors_standard[get_grid_color
                                            (x, y, x - 1, y)]);
       gdk_draw_line(pm, thin_line_gc,
-                   canvas_x, canvas_y + NORMAL_TILE_HEIGHT / 2,
-                   canvas_x + NORMAL_TILE_WIDTH / 2, canvas_y);
+                   canvas_x + hex_side / 2,
+                   canvas_y + NORMAL_TILE_HEIGHT / 2,
+                   canvas_x + (NORMAL_TILE_WIDTH - hex_side) / 2, canvas_y);
+    }
+
+    if (hex_side > 0 && (draw & D_M_LR)) {
+      gdk_gc_set_foreground(thin_line_gc,
+                           colors_standard[get_grid_color
+                                           (x, y, x - 1, y - 1)]);
+      gdk_draw_line(pm, thin_line_gc,
+                   canvas_x + (NORMAL_TILE_WIDTH - hex_side) / 2, canvas_y,
+                   canvas_x + (NORMAL_TILE_WIDTH + hex_side) / 2, canvas_y);
     }
   }
 
Index: common/map.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/map.c,v
retrieving revision 1.163
diff -u -r1.163 map.c
--- common/map.c        26 Feb 2004 13:19:47 -0000      1.163
+++ common/map.c        31 Mar 2004 21:09:36 -0000
@@ -323,11 +323,15 @@
 ***************************************************************/
 int real_map_distance(int x0, int y0, int x1, int y1)
 {
-  int dx, dy;
+  if (topo_has_flag(TF_HEX)) {
+    return map_distance(x0, y0, x1, y1);
+  } else {
+    int dx, dy;
 
-  map_distance_vector(&dx, &dy, x0, y0, x1, y1);
+    map_distance_vector(&dx, &dy, x0, y0, x1, y1);
 
-  return MAX(abs(dx), abs(dy));
+    return MAX(abs(dx), abs(dy));
+  }
 }
 
 /***************************************************************
@@ -335,13 +339,22 @@
 ***************************************************************/
 int sq_map_distance(int x0, int y0, int x1, int y1)
 {
-  /* We assume map_distance_vector gives us the vector with the
-     minimum squared distance. Right now this is true. */
-  int dx, dy;
+  if (topo_has_flag(TF_HEX)) {
+    int dist = map_distance(x0, y0, x1, y1);
 
-  map_distance_vector(&dx, &dy, x0, y0, x1, y1);
+    /* In a hex map the pythagorean distance is not significantly different
+     * from the map distance.  If you think about what the value's used
+     * for it becomes apparent the two should be the same. */
+    return dist * dist;
+  } else {
+    /* We assume map_distance_vector gives us the vector with the
+     * minimum squared distance. Right now this is true. */
+    int dx, dy;
 
-  return (dx*dx + dy*dy);
+    map_distance_vector(&dx, &dy, x0, y0, x1, y1);
+
+    return dx * dx + dy * dy;
+  }
 }
 
 /***************************************************************
@@ -355,7 +368,33 @@
 
   map_distance_vector(&dx, &dy, x0, y0, x1, y1);
 
-  return abs(dx) + abs(dy);
+  if (topo_has_flag(TF_HEX)) {
+    if (topo_has_flag(TF_ISO)) {
+      /* Iso-hex: you can't move NE or SW. */
+      if ((dx < 0 && dy > 0)
+         || (dx > 0 && dy < 0)) {
+       /* Diagonal moves in this direction aren't allowed, so it will take
+        * the full number of moves. */
+       return abs(dx) + abs(dy);
+      } else {
+       /* Diagonal moves in this direction *are* allowed. */
+       return MAX(abs(dx), abs(dy));
+      }
+    } else {
+      /* Hex: you can't move SE or NW. */
+      if ((dx > 0 && dy > 0)
+         || (dx < 0 && dy < 0)) {
+       /* Diagonal moves in this direction aren't allowed, so it will take
+        * the full number of moves. */
+       return abs(dx) + abs(dy);
+      } else {
+       /* Diagonal moves in this direction *are* allowed. */
+       return MAX(abs(dx), abs(dy));
+      }
+    }
+  } else {
+    return abs(dx) + abs(dy);
+  }
 }
 
 /***************************************************************
@@ -1520,14 +1559,16 @@
 bool is_valid_dir(enum direction8 dir)
 {
   switch (dir) {
-  case DIR8_NORTH:
+  case DIR8_SOUTHEAST:
+  case DIR8_NORTHWEST:
+    return !(topo_has_flag(TF_HEX) && !topo_has_flag(TF_ISO));
   case DIR8_NORTHEAST:
+  case DIR8_SOUTHWEST:
+    return !(topo_has_flag(TF_HEX) && topo_has_flag(TF_ISO));
+  case DIR8_NORTH:
   case DIR8_EAST:
-  case DIR8_SOUTHEAST:
   case DIR8_SOUTH:
-  case DIR8_SOUTHWEST:
   case DIR8_WEST:
-  case DIR8_NORTHWEST:
     return TRUE;
   default:
     return FALSE;
Index: common/map.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/map.h,v
retrieving revision 1.177
diff -u -r1.177 map.h
--- common/map.h        26 Feb 2004 13:19:47 -0000      1.177
+++ common/map.h        31 Mar 2004 21:09:36 -0000
@@ -150,7 +150,8 @@
   /* Bit-values. */
   TF_WRAPX = 1,
   TF_WRAPY = 2,
-  TF_ISO = 4
+  TF_ISO = 4,
+  TF_HEX = 8
 };
 
 #define CURRENT_TOPOLOGY (map.topology_id)
@@ -199,13 +200,13 @@
 
 /* Obscure math.  See explanation in doc/HACKING. */
 #define native_to_map_pos(pmap_x, pmap_y, nat_x, nat_y)                     \
-  (topo_has_flag(TF_ISO)                                                    \
+  ((topo_has_flag(TF_ISO) || topo_has_flag(TF_HEX))                         \
    ? (*(pmap_x) = ((nat_y) + ((nat_y) & 1)) / 2 + (nat_x),                  \
       *(pmap_y) = (nat_y) - *(pmap_x) + map.xsize)                          \
    : (*(pmap_x) = (nat_x), *(pmap_y) = (nat_y)))
 
 #define map_to_native_pos(pnat_x, pnat_y, map_x, map_y)                     \
-  (topo_has_flag(TF_ISO)                                                    \
+  ((topo_has_flag(TF_ISO) || topo_has_flag(TF_HEX))                        \
    ? (*(pnat_y) = (map_x) + (map_y) - map.xsize,                            \
       *(pnat_x) = (2 * (map_x) - *(pnat_y) - (*(pnat_y) & 1)) / 2)          \
    : (*(pnat_x) = (map_x), *(pnat_y) = (map_y)))
@@ -232,9 +233,10 @@
  * we bend this rule here.
  */
 #define MAPSTEP(dest_x, dest_y, src_x, src_y, dir)     \
-(    (dest_x) = (src_x) + DIR_DX[(dir)],               \
-     (dest_y) = (src_y) + DIR_DY[(dir)],               \
-     normalize_map_pos(&(dest_x), &(dest_y)))
+  (is_valid_dir(dir)                                    \
+   && ((dest_x) = (src_x) + DIR_DX[(dir)],             \
+       (dest_y) = (src_y) + DIR_DY[(dir)],             \
+       normalize_map_pos(&(dest_x), &(dest_y))))
 
 struct player *map_get_owner(int x, int y);
 void map_set_owner(int x, int y, struct player *pplayer);
@@ -269,7 +271,7 @@
  * TODO: implement this for iso-maps.
  */
 #define IS_BORDER_MAP_POS(x, y, dist)                         \
-  (topo_has_flag(TF_ISO)                                      \
+  (topo_has_flag(TF_ISO) || topo_has_flag(TF_HEX)            \
    || (x) < (dist) || (x) >= map.xsize - (dist)               \
    || (y) < (dist) || (y) >= map.ysize - (dist))
 
@@ -488,6 +490,9 @@
   int MACRO_center_y = (center_y);                                            \
   CHECK_MAP_POS(MACRO_center_x, MACRO_center_y);                              \
   for (dir_itr = 0; dir_itr < 8; dir_itr++) {                                 \
+    if (!is_valid_dir(dir_itr)) {                                            \
+      continue;                                                                
      \
+    }                                                                        \
     DIRSTEP(x_itr, y_itr, dir_itr);                                           \
     x_itr += MACRO_center_x;                                                  \
     y_itr += MACRO_center_y;                                                  \
@@ -577,6 +582,7 @@
   bool _is_border = IS_BORDER_MAP_POS(x, y, 1);                               \
   CHECK_MAP_POS(x, y);                                                        \
   for (IAC_i = 0; IAC_i < 4; IAC_i++) {                                       \
+    /* All cardinal directions are valid. */                                 \
     IAC_x = x + CAR_DIR_DX[IAC_i];                                            \
     IAC_y = y + CAR_DIR_DY[IAC_i];                                            \
                                                                               \
@@ -606,7 +612,7 @@
 #define MAP_ORIGINAL_TOPO        TF_WRAPX
 #define MAP_DEFAULT_TOPO         TF_WRAPX
 #define MAP_MIN_TOPO             0
-#define MAP_MAX_TOPO             7
+#define MAP_MAX_TOPO             15
 
 #define MAP_DEFAULT_SEED         0
 #define MAP_MIN_SEED             0
Index: server/mapgen.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/mapgen.c,v
retrieving revision 1.131
diff -u -r1.131 mapgen.c
--- server/mapgen.c     12 Mar 2004 05:50:48 -0000      1.131
+++ server/mapgen.c     31 Mar 2004 21:09:37 -0000
@@ -813,7 +813,7 @@
     nat_set_terrain(x, 2, T_OCEAN);
 
     /* Iso-maps need two lines of ocean. */
-    if (myrand(2) != 0 || topo_has_flag(TF_ISO)) {
+    if (myrand(2) != 0 || (topo_has_flag(TF_ISO) && !topo_has_flag(TF_HEX))) {
       nat_set_terrain(x, 1, T_OCEAN);
     }
 
@@ -823,7 +823,7 @@
 
     nat_set_terrain(x, map.ysize - 3, T_OCEAN);
 
-    if (myrand(2) != 0 || topo_has_flag(TF_ISO)) {
+    if (myrand(2) != 0 || (topo_has_flag(TF_ISO) && topo_has_flag(TF_HEX))) {
       nat_set_terrain(x, map.ysize - 2, T_OCEAN);
     }
     if (myrand(2) != 0) {
Index: server/srv_main.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/srv_main.c,v
retrieving revision 1.157
diff -u -r1.157 srv_main.c
--- server/srv_main.c   26 Mar 2004 18:11:32 -0000      1.157
+++ server/srv_main.c   31 Mar 2004 21:09:37 -0000
@@ -1603,7 +1603,7 @@
   test_random1(200000);
 #endif
     
-  if (topo_has_flag(TF_ISO) && topo_has_flag(TF_WRAPY)
+  if ((topo_has_flag(TF_ISO) || topo_has_flag(TF_HEX)) && 
topo_has_flag(TF_WRAPY)
       && (map.ysize % 2 == 1)) {
     /* To wrap north-south an iso-map must have even Y dimension.  Since the
      * X dimension is compressed this isn't an issue for east-west
Index: server/stdinhand.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/stdinhand.c,v
retrieving revision 1.311
diff -u -r1.311 stdinhand.c
--- server/stdinhand.c  26 Mar 2004 16:45:13 -0000      1.311
+++ server/stdinhand.c  31 Mar 2004 21:09:38 -0000
@@ -249,10 +249,14 @@
          N_("Two-dimensional maps can wrap at the north-south or \n"
             "east-west edges, and use a cartesian or isometric \n"
             "rectangular grid.  See the manual for further explanation.\n"
-             "  0 Flat Earth (unwrapped)          4 Flat Earth (isometric)\n"
-             "  1 Earth (wraps E-W)               5 Earth (isometric)\n"
-             "  2 Uranus (wraps N-S)              6 Uranus (isometric)\n"
-             "  3 Donut World (wraps N-S, E-W)    7 Donut World (isometric)"
+             "  0 Flat Earth (unwrapped)\n"
+            "    4 => isometric, 8 => hexagonal; 12 => iso-hex\n"
+             "  1 Earth (wraps E-W)\n"
+            "    5 => isometric, 9 => hexagonal; 13 => iso-hex\n"
+             "  2 Uranus (wraps N-S)\n"
+            "    6 => isometric, 10 => hexagonal; 14 => iso-hex\n"
+             "  3 Donut World (wraps N-S, E-W)\n"
+            "    7 => isometric, 11 => hexagonal; 15 => iso-hex"
           ), NULL, 
          MAP_MIN_TOPO, MAP_MAX_TOPO, MAP_DEFAULT_TOPO)
 

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