Complete.Org: Mailing Lists: Archives: freeciv-dev: April 2004:
[Freeciv-Dev] (PR#8622) civ3-style base terrain graphics
Home

[Freeciv-Dev] (PR#8622) civ3-style base terrain graphics

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
To: undisclosed-recipients: ;
Subject: [Freeciv-Dev] (PR#8622) civ3-style base terrain graphics
From: "Jason Short" <jdorje@xxxxxxxxxxxxxxxxxxxxx>
Date: Thu, 29 Apr 2004 22:47:22 -0700
Reply-to: rt@xxxxxxxxxxx

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

http://freeciv.org/~jdorje/civ3.png
http://freeciv.org/~jdorje/civ3.tar.gz (tileset)

It's late so I'll skip over all the explanations.  Suffice it to say 
that the attached patch includes all the drawing code changes needed to 
use the civ3 algorithm for drawing base terrains.  It includes at least 
two separate new features plus one bugfix, includes a number of hacks, 
and has no documentation yet (obviously it's not ready for CVS yet). 
But it does allow some really nice graphics like the ones in the 
screenshot above.

It's worth noting though that drawing code changes are not enough to use 
a civ3 tileset.  The civ3 tileset puts several restrictions on the 
ruleset and mapgen code.  Some of these include:

- No swamps.
- Only 3 terrains can ever meet at a tile vertex (a tile vertex is the 
intersection of 4 tiles).  So you can have 
plains-grassland-desert-plains but not plains-grassland-desert-coast. 
This rarely happens randomly, however (you don't see it at all in the 
screenshot).
- Tundra may only border grassland and coast.  This is a problem for any 
arctic regions.
- Arctic may only border deep ocean, and only in limited ways.  This is 
a problem since freeciv has no deep ocean.
- Terrain transforming must be prohibited, since otherwise these rules 
would quickly be violated.

There are probably more restrictions that just happen rarely.  Or maybe 
I missed some sprites and some of these actually are possible.

jason

? tests/check-output
? tests/header_guard.sh
? tests/va_list.sh
Index: client/tilespec.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/tilespec.c,v
retrieving revision 1.165
diff -u -r1.165 tilespec.c
--- client/tilespec.c   29 Apr 2004 15:11:32 -0000      1.165
+++ client/tilespec.c   30 Apr 2004 05:16:30 -0000
@@ -86,6 +86,15 @@
 static int roadstyle;
 static int flag_offset_x, flag_offset_y;
 
+#define NUM_CORNER_DIRS 4
+#define TILES_PER_CORNER 4
+
+static struct {
+  enum match_style match_style;
+  int count;
+  char **match_types;
+} layers[MAX_NUM_LAYERS];
+
 /* Darkness style.  Don't reorder this enum since tilesets depend on it. */
 static enum {
   /* No darkness sprites are drawn. */
@@ -737,6 +746,26 @@
   minimap_intro_filename = tilespec_gfx_filename(c);
   freelog(LOG_DEBUG, "radar file %s", minimap_intro_filename);
 
+  /* Terrain layer info. */
+  for (i = 0; i < MAX_NUM_LAYERS; i++) {
+    char *style = secfile_lookup_str_default(file, "none",
+                                            "layer%d.match_style", i);
+    int j;
+
+    if (mystrcasecmp(style, "full") == 0) {
+      layers[i].match_style = MATCH_FULL;
+    } else if (mystrcasecmp(style, "bool") == 0) {
+      layers[i].match_style = MATCH_BOOLEAN;
+    } else {
+      layers[i].match_style = MATCH_NONE;
+    }
+
+    layers[i].match_types = secfile_lookup_str_vec(file, &layers[i].count,
+                                                  "layer%d.match_types", i);
+    for (j = 0; j < layers[i].count; j++) {
+      layers[i].match_types[j] = mystrdup(layers[i].match_types[j]);
+    }
+  }
 
   /* Terrain drawing info. */
   terrains = secfile_get_secnames_prefix(file, "terrain_", &num_terrains);
@@ -751,7 +780,7 @@
   for (i = 0; i < num_terrains; i++) {
     struct terrain_drawing_data *terr = fc_malloc(sizeof(*terr));
     char *cell_type;
-    int l;
+    int l, j;
 
     memset(terr, 0, sizeof(*terr));
     terr->name = mystrdup(terrains[i] + strlen("terrain_"));
@@ -759,9 +788,12 @@
                                            terrains[i]);
     terr->num_layers = secfile_lookup_int(file, "%s.num_layers",
                                          terrains[i]);
-    terr->num_layers = MAX(1, terr->num_layers);
+    terr->num_layers = CLIP(1, terr->num_layers, MAX_NUM_LAYERS);
 
     for (l = 0; l < terr->num_layers; l++) {
+      char *match_type;
+      char *match_style;
+
       terr->layer[l].is_tall
        = secfile_lookup_bool_default(file, FALSE, "%s.layer%d_is_tall",
                                      terrains[i], l);
@@ -771,9 +803,61 @@
       terr->layer[l].offset_y
        = secfile_lookup_int_default(file, 0, "%s.layer%d_offset_y",
                                     terrains[i], l);
-      terr->layer[l].match_type
-       = secfile_lookup_int_default(file, 0, "%s.layer%d_match_type",
-                                    terrains[i], l);
+
+      match_style = secfile_lookup_str_default(file, "none",
+                                              "%s.layer%d_match_style",
+                                              terrains[i], l);
+      if (mystrcasecmp(match_style, "full") == 0) {
+       terr->layer[l].match_style = MATCH_FULL;
+      } else if (mystrcasecmp(match_style, "bool") == 0) {
+       terr->layer[l].match_style = MATCH_BOOLEAN;
+      } else {
+       terr->layer[l].match_style = MATCH_NONE;
+      }
+
+      match_type = secfile_lookup_str_default(file, NULL,
+                                             "%s.layer%d_match_type",
+                                             terrains[i], l);
+      if (match_type) {
+       /* Set match_count */
+       switch (terr->layer[l].match_style) {
+       case MATCH_NONE:
+         terr->layer[l].match_count = 0;
+         break;
+       case MATCH_FULL:
+         terr->layer[l].match_count = layers[l].count;
+         break;
+       case MATCH_BOOLEAN:
+         terr->layer[l].match_count = 2;
+         break;
+       }
+
+       /* Determine out match_type. */
+       for (j = 0; j < layers[l].count; j++) {
+         if (mystrcasecmp(layers[l].match_types[j], match_type) == 0) {
+           break;
+         }
+       }
+       terr->layer[l].match_type = j;
+       if (j >= layers[l].count) {
+         freelog(LOG_ERROR, "Invalid match type given for %s.", terrains[i]);
+         terr->layer[l].match_type = 0;
+         terr->layer[l].match_style = MATCH_NONE;
+       }
+      } else {
+       terr->layer[l].match_style = MATCH_NONE;
+       if (layers[l].match_style != MATCH_NONE) {
+         freelog(LOG_ERROR, "Layer %d has a match_style set; all terrains"
+                 " must have a match_type.  %s doesn't.", l, terrains[i]);
+       }
+      }
+
+      if (terr->layer[l].match_style == MATCH_NONE
+         && layers[l].match_style == MATCH_FULL) {
+       freelog(LOG_ERROR, "Layer %d has match_type full set; all terrains"
+               " must match this.  %s doesn't.", l, terrains[i]);
+      }
+
       cell_type
        = secfile_lookup_str_default(file, "single", "%s.layer%d_cell_type",
                                     terrains[i], l);
@@ -1298,15 +1382,13 @@
 
   /* Set up each layer of the drawing. */
   for (l = 0; l < draw->num_layers; l++) {
-    if (draw->layer[l].match_type == 0) {
+    if (draw->layer[l].match_style == MATCH_NONE) {
       /* Load single sprite for this terrain. */
       my_snprintf(buffer1, sizeof(buffer1), "t.%s1", draw->name);
       draw->layer[l].base = lookup_sprite_tag_alt(buffer1, "", TRUE,
                                                  "tile_type",
                                                  tt->terrain_name);
     } else {
-      int j;
-
       switch (draw->layer[l].cell_type) {
       case CELL_SINGLE:
        /* Load 16 cardinally-matched sprites. */
@@ -1320,15 +1402,101 @@
        draw->layer[l].base = draw->layer[l].match[0];
        break;
       case CELL_RECT:
-       for (i = 0; i < 4; i++) {
-         for (j = 0; j < 8; j++) {
+       {
+         const int count = draw->layer[l].match_count;
+         int number;
+
+         /* N directions (NSEW) * 3 dimensions of matching */
+         /* FIXME: should use exp() or expi() here. */
+         number = NUM_CORNER_DIRS * count * count * count;
+         draw->layer[l].cells
+           = fc_malloc(number * sizeof(*draw->layer[l].cells));
+
+         for (i = 0; i < number; i++) {
+           int value = i / NUM_CORNER_DIRS;
+           enum direction4 dir = i % NUM_CORNER_DIRS;
            char *dir2 = "udlr";
 
-           my_snprintf(buffer1, sizeof(buffer1), "t.%s_cell_%c%d",
-                       draw->name, dir2[i], j);
-           draw->layer[l].cells[j][i]
-             = lookup_sprite_tag_alt(buffer1, "", TRUE, "tile_type",
-                                     tt->terrain_name);
+           switch (draw->layer[l].match_style) {
+           case MATCH_NONE:
+             /* Impossible. */
+             assert(0);
+             break;
+           case MATCH_BOOLEAN:
+             my_snprintf(buffer1, sizeof(buffer1), "t.%s_cell_%c%d",
+                         draw->name, dir2[dir], value);
+             draw->layer[l].cells[i]
+               = lookup_sprite_tag_alt(buffer1, "", TRUE, "tile_type",
+                                       tt->terrain_name);
+             break;
+           case MATCH_FULL:
+             {
+               int n = 0, s = 0, e = 0, w = 0;
+               int v1, v2, v3;
+               int this = draw->layer[l].match_type;
+               struct Sprite *sprite;
+
+               v1 = value % count;
+               value /= count;
+               v2 = value % count;
+               value /= count;
+               v3 = value % count;
+
+               assert(v1 < count && v2 < count && v3 < count);
+
+               /* Assume merged cells.  This should be a separate option. */
+               switch (dir) {
+               case DIR4_NORTH:
+                 s = this;
+                 w = v1;
+                 n = v2;
+                 e = v3;
+                 break;
+               case DIR4_EAST:
+                 w = this;
+                 n = v1;
+                 e = v2;
+                 s = v3;
+                 break;
+               case DIR4_SOUTH:
+                 n = this;
+                 e = v1;
+                 s = v2;
+                 w = v3;
+                 break;
+               case DIR4_WEST:
+                 e = this;
+                 s = v1;
+                 w = v2;
+                 n = v3;
+                 break;
+               }
+               my_snprintf(buffer1, sizeof(buffer1),
+                           "t.cellgroup_%s_%s_%s_%s",
+                           layers[l].match_types[n],
+                           layers[l].match_types[e],
+                           layers[l].match_types[s],
+                           layers[l].match_types[w]);
+               sprite = load_sprite(buffer1);
+
+               if (sprite) {
+                 /* Crop the sprite to separate this cell. */
+                 const int W = NORMAL_TILE_WIDTH, H = NORMAL_TILE_HEIGHT;
+                 int x[4] = {W / 4, W / 4, 0, W / 2};
+                 int y[4] = {H / 2, 0, H / 4, H / 4};
+                 int xo[4] = {0, 0, -W / 2, W / 2};
+                 int yo[4] = {H / 2, -H / 2, 0, 0};
+
+                 sprite = crop_sprite(sprite,
+                                      x[dir], y[dir], W / 2, H / 2,
+                                      sprites.black_tile, xo[dir], yo[dir]);
+                                 
+               }
+
+               draw->layer[l].cells[i] = sprite;
+               break;
+             }
+           }
          }
        }
        my_snprintf(buffer1, sizeof(buffer1), "t.%s1", draw->name);
@@ -1985,14 +2153,13 @@
   }
 
   for (l = 0; l < draw->num_layers; l++) {
-    if (draw->layer[l].match_type == 0) {
+    if (draw->layer[l].match_style == MATCH_NONE) {
       ADD_SPRITE_SIMPLE(draw->layer[l].base);
     } else {
       int match_type = draw->layer[l].match_type;
 
 #define MATCH(dir)                                               \
-      ((sprites.terrain[ttype_near[(dir)]]->layer[l].match_type) \
-       == match_type)
+      (sprites.terrain[ttype_near[(dir)]]->layer[l].match_type)
 
       if (draw->layer[l].cell_type == CELL_SINGLE) {
        int tileno;
@@ -2011,26 +2178,50 @@
         * for each of the 4 cells (32 sprites total). */
        const int W = NORMAL_TILE_WIDTH, H = NORMAL_TILE_HEIGHT;
        const enum direction8 dirs[4] = {
-         DIR8_NORTHWEST, DIR8_SOUTHEAST, DIR8_SOUTHWEST, DIR8_NORTHEAST
+         DIR8_NORTHWEST, DIR8_SOUTHEAST, DIR8_NORTHEAST, DIR8_SOUTHWEST
        };
        const int iso_offsets[4][2] = {
-         {W / 4, 0}, {W / 4, H / 2}, {0, H / 4}, {W / 2, H / 4},
+         {W / 4, 0}, {W / 4, H / 2}, {W / 2, H / 4}, {0, H / 4}
        };
        const int noniso_offsets[4][2] = {
          {0, 0}, {W / 2, H / 2}, {0, H / 2}, {W / 2, 0}
        };
        int i;
 
-       /* put coasts */
-       for (i = 0; i < 4; i++) {
-         int array_index = ((!MATCH(dir_ccw(dirs[i])) ? 1 : 0)
-                            + (!MATCH(dirs[i]) ? 2 : 0)
-                            + (!MATCH(dir_cw(dirs[i])) ? 4 : 0));
+       /* put corner cells */
+       for (i = 0; i < NUM_CORNER_DIRS; i++) {
+         const int count = draw->layer[l].match_count;
+         int array_index = 0;
          int x = (is_isometric ? iso_offsets[i][0] : noniso_offsets[i][0]);
          int y = (is_isometric ? iso_offsets[i][1] : noniso_offsets[i][1]);
+         int m[3] = {MATCH(dir_ccw(dirs[i])),
+                     MATCH(dirs[i]),
+                     MATCH(dir_cw(dirs[i]))};
+         struct Sprite *s;
+
+         switch (draw->layer[l].match_style) {
+         case MATCH_NONE:
+           /* Impossible */
+           assert(0);
+           break;
+         case MATCH_BOOLEAN:
+           assert(count == 2);
+           array_index = array_index * count + (m[2] != match_type);
+           array_index = array_index * count + (m[1] != match_type);
+           array_index = array_index * count + (m[0] != match_type);
+           break;
+         case MATCH_FULL:
+           array_index = array_index * count + m[2];
+           array_index = array_index * count + m[1];
+           array_index = array_index * count + m[0];
+           break;
+         }
+         array_index = array_index * NUM_CORNER_DIRS + i;
 
-         ADD_SPRITE(draw->layer[l].cells[array_index][i],
-                    DRAW_NORMAL, TRUE, x, y);
+         s = draw->layer[l].cells[array_index];
+         if (s) {
+           ADD_SPRITE(s, DRAW_NORMAL, TRUE, x, y);
+         }
        }
       }
 #undef MATCH
Index: client/tilespec.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/tilespec.h,v
retrieving revision 1.66
diff -u -r1.66 tilespec.h
--- client/tilespec.h   29 Apr 2004 15:11:32 -0000      1.66
+++ client/tilespec.h   30 Apr 2004 05:16:32 -0000
@@ -104,10 +104,16 @@
   DIR4_NORTH = 0, DIR4_SOUTH, DIR4_EAST, DIR4_WEST
 };
 
+enum match_style {
+  MATCH_NONE, MATCH_BOOLEAN, MATCH_FULL
+};
+
 enum cell_type {
   CELL_SINGLE, CELL_RECT
 };
 
+#define MAX_NUM_LAYERS 2
+
 struct terrain_drawing_data {
   char *name;
   char *mine_tag;
@@ -116,13 +122,16 @@
   struct {
     bool is_tall;
     int offset_x, offset_y;
-    int match_type;
+
+    enum match_style match_style;
+    int match_type, match_count;
+
     enum cell_type cell_type;
 
     struct Sprite *base;
     struct Sprite *match[NUM_DIRECTION_NSEW];
-    struct Sprite *cells[8][4]; /* 4 = up down left right */
-  } layer[2];
+    struct Sprite **cells;
+  } layer[MAX_NUM_LAYERS];
 
   bool is_blended;
   struct Sprite *blend[4]; /* indexed by a direction4 */
Index: data/isotrident.tilespec
===================================================================
RCS file: /home/freeciv/CVS/freeciv/data/isotrident.tilespec,v
retrieving revision 1.16
diff -u -r1.16 isotrident.tilespec
--- data/isotrident.tilespec    1 Apr 2004 23:11:23 -0000       1.16
+++ data/isotrident.tilespec    30 Apr 2004 05:16:32 -0000
@@ -64,55 +64,74 @@
 
 ; Terrain info - see README.graphics
 
+[layer0]
+match_style = "BOOL"
+match_types = "ocean", "other"
+
+[layer1]
+match_style = "BOOL"
+match_types = "forest", "hills", "mountains"
+
 [terrain_arctic]
 is_blended = 1
 num_layers = 1
+layer0_match_type = "other"
 mine_sprite = "tx.oil_mine"
 
 [terrain_desert]
 is_blended = 1
 num_layers = 1
+layer0_match_type = "other"
 mine_sprite = "tx.oil_mine"
 
 [terrain_forest]
 is_blended = 1
 num_layers = 2
-layer1_match_type = 1
+layer0_match_type = "other"
+layer1_match_type = "forest"
 
 [terrain_grassland]
 is_blended = 1
 num_layers = 1
+layer0_match_type = "other"
 
 [terrain_hills]
 is_blended = 1
 num_layers = 2
-layer1_match_type = 2
+layer0_match_type = "other"
+layer1_match_type = "hills"
 mine_sprite = "tx.mine"
 
 [terrain_jungle]
 is_blended = 1
 num_layers = 1
+layer0_match_type = "other"
 
 [terrain_mountains]
 is_blended = 1
 num_layers = 2
-layer1_match_type = 3
+layer0_match_type = "other"
+layer1_match_type = "mountains"
 mine_sprite = "tx.mine"
 
 [terrain_ocean]
 is_blended = 1
 num_layers = 1
-layer0_match_type = 6
+layer0_match_style = "bool"
+layer0_match_type = "ocean"
 layer0_cell_type = "rect"
 
 [terrain_plains]
 is_blended = 1
 num_layers = 1
+layer0_match_type = "other"
 
 [terrain_swamp]
 is_blended = 1
 num_layers = 1
+layer0_match_type = "other"
 
 [terrain_tundra]
 is_blended = 1
 num_layers = 1
+layer0_match_type = "other"

[Prev in Thread] Current Thread [Next in Thread]
  • [Freeciv-Dev] (PR#8622) civ3-style base terrain graphics, Jason Short <=