[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]
<URL: http://rt.freeciv.org/Ticket/Display.html?id=7481 >
Excellent stuff ...
I think this hits all the right spots and done things in
the way that generalizing topologies suggested would make
ongoing improvement rapid and incremental rather than big
bang and one-off.
There is of course still a lot of work, but it is clearly
just work and not a minefield of unexpected problems.
Congratulations to all involved in getting things to this stage.
Cheers
RossW
=====
Jason Short wrote:
> <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)
>
|
|