Complete.Org: Mailing Lists: Archives: freeciv-dev: April 2002:
[Freeciv-Dev] Re: new_natural_city_names updated again (PR#1127)
Home

[Freeciv-Dev] Re: new_natural_city_names updated again (PR#1127)

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
To: freeciv-dev@xxxxxxxxxxx
Cc: bugs@xxxxxxxxxxxxxxxxxxx
Subject: [Freeciv-Dev] Re: new_natural_city_names updated again (PR#1127)
From: Jason Short <jdorje@xxxxxxxxxxxxxxxxxxxxx>
Date: Sat, 27 Apr 2002 20:19:54 -0700 (PDT)

Raimar Falke wrote:
On Wed, Apr 10, 2002 at 12:16:49AM -0700, Jason Short wrote:

(Note: I tried to send this to the list before, but I sent it to bugs@freeciv and it never showed up on freeciv-dev. I suspect the reason was that the attachment was way to big (I shouldn't have included one so big), which could explain the rumours of disappearing posts sent to bugs@freeciv.)

Here's an updated version.

Changes:

- Deallocate the nation's city_names array. I wouldn't call it "nice", but it should get the job done.

- Did some cleanup to the code I added to server/ruleset.c. In particular, I changed some *name to name[0], some 0 to '\0', made the "handled" var a bool, added a comment or two, and fixed the spacing in one place.

- Changed a strcmp to mystrcasecmp.  Oops!

Hmm, I think that's all I did, actually.

On further reflection, changing char* name -> char name[MAX_LEN_NAME] in the city_name struct wouldn't be so bad; it would make some things a little simpler. But if MAX_LEN_NAME were ever increased (it's only 32 now), the increased overhead would potentially become significant. So I'm still for keeping it dynamic.


I have *not* included the data patch or the demo patch here. The three patches are:

(1) code patch (included), needed to get things to work at all. (Most recent: version 10)

(2) data patch (included yesterday), reverts the changes made for "natural city names" (i.e. separate city lists for each terrain), but adds no new terrain information. (Most recent: version 6, probably final)

(3) demo patch (included previously), adds demo terrain information to the American nation's cities.

Parts 1 and 2 are necessary for things to work at all, and should be applied together (but are kept separate to make things easy for reviewers). Part 3 should not be applied to the repository (it's pretty inaccurate), but is useful for testing the patch.

jason


+ * the bonus if the city is close to the terrain.  Both of these entries
+ * may hold a value of 0 (no preference), 1 (city likes the terrain), or -1
+ * (city doesn't like the terrain).


I would still like an enum or a typedef int ternary;
to be used here:

+       int river;
+       int terrain[T_COUNT];   

Ok...

+    value = (float)value / 1.4;
+    value = (float)value * 1.4;
+      value = (float)value / 1.4;
+      value = (float)value * 1.4;


A const float FACTOR=1.4
? And a comment what it affects.

Sure.

+static char *search_for_city_name(int x, int y, struct city_name *city_names)
+{


+  int choice, best_value = -1;


If I see best_value = -1 does this means that
evaluate_city_name_priority only returns values >=0?

Yes.  Comment added.

+  for (choice = 0; city_names[choice].name; choice++) {
+    if (!game_find_city_by_name(city_names[choice].name)) {
+      int value = evaluate_city_name_priority(x, y, &city_names[choice],
+                                             choice);
+


+      if (best_value == -1 || value < best_value) {


Why can't better values be larger. This way we can name it
continuously fitness and not priority or value.

Aside from simply negating the value, there is no good way to make the math work out with larger values being better. And I don't really see a need...I have renamed the variables from "value" to "priority".

+  name = search_for_city_name(x, y, nation->city_names);
+  if (name)
+    return name;


Missing {}s


+  name = search_for_city_name(x, y, misc_city_names);
+  if (name)
+    return name;


Same.

Fixed.

+  /* This had better be impossible! */
+  assert(0);
  return _("A poorly-named city");


Since this really shouldn't happend I have no problem with a
freelog(LOG_FATAL,...) and an exit(...).

But there is really no need...

+#include "nation.h" /* for struct city_name */


indent will place the '/' at column 32.


+  char **cities;

...

+  cities = secfile_lookup_str_vec(file, &dim, "%s%s",
+                                 secfile_str1, secfile_str2);


You can merge these.

Ok...

+  for (j = 0, value = 1; j < dim; j++, value++) {
+    /* This whole thing should be handled more succinctly. */
+    char *name = cities[j], *next;
+
+    /*
+     * Now we wish to determine values for all of the city labels.
+     * A value of 0 means no preference (which is necessary so that
+     * the use of this is optional); -1 means the label is negated
+     * and 1 means it's labelled.  Mostly the parsing just involves
+     * a lot of ugly string handling...
+     */


+    memset(&city_names[j].terrain[0], 0,
+          T_COUNT * sizeof(city_names[j].terrain[0]));


What about
       memset(city_names[j].terrain,0,sizeof(city_names[j].terrain));

Ok.

+    city_names[j].river = 0;
+
+    name = strchr(name, '(');
+    if (name) {
+      /*
+       * 0-terminate the original string, then find the
+       * close-parenthesis so that we can make sure we stop there.
+       */
+      name[0] = '\0';
+      name++;
+      next = strchr(name, ')');


+      assert(next);


Such would be an input error and we should print a more verbose msg.

Ahh, yes.  Good catch.

New patch attached.

jason
Index: common/game.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/game.c,v
retrieving revision 1.138
diff -u -r1.138 game.c
--- common/game.c       2002/04/25 14:09:36     1.138
+++ common/game.c       2002/04/28 03:17:29
@@ -674,6 +674,7 @@
   game.citymindist = GAME_DEFAULT_CITYMINDIST;
   game.civilwarsize= GAME_DEFAULT_CIVILWARSIZE;
   game.savepalace  = GAME_DEFAULT_SAVEPALACE;
+  game.natural_city_names = GAME_DEFAULT_NATURALCITYNAMES;
   game.unhappysize = GAME_DEFAULT_UNHAPPYSIZE;
   game.angrycitizen= GAME_DEFAULT_ANGRYCITIZEN;
   game.foodbox     = GAME_DEFAULT_FOODBOX;
Index: common/game.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/game.h,v
retrieving revision 1.104
diff -u -r1.104 game.h
--- common/game.h       2002/04/25 14:09:36     1.104
+++ common/game.h       2002/04/28 03:17:29
@@ -129,6 +129,7 @@
   int sewer_size;
   int add_to_size_limit;
   bool savepalace;
+  bool natural_city_names;
   bool spacerace;
   bool turnblock;
   bool fixedlength;
@@ -334,6 +335,8 @@
 #define GAME_MAX_CIVILWARSIZE        1000
 
 #define GAME_DEFAULT_SAVEPALACE      TRUE
+
+#define GAME_DEFAULT_NATURALCITYNAMES TRUE
 
 #define GAME_DEFAULT_FOODBOX         10
 #define GAME_MIN_FOODBOX             5
Index: common/nation.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/nation.c,v
retrieving revision 1.21
diff -u -r1.21 nation.c
--- common/nation.c     2002/02/24 11:50:35     1.21
+++ common/nation.c     2002/04/28 03:17:29
@@ -167,6 +167,9 @@
   nations = (struct nation_type *)fc_calloc(num, sizeof(struct nation_type));
 }
 
+/***************************************************************
+ De-allocate the currently allocated nations.
+***************************************************************/
 void free_nations(int num)
 {
   int i, j;
@@ -175,6 +178,15 @@
   for( i = 0; i < num; i++) {
     for( j = 0; j < nations[i].leader_count; j++) {
       free( nations[i].leader_name[j] );
+    }
+    if (nations[i].city_names) {
+      /* Unfortunately, this monstrosity of a loop is necessary given the 
+        setup of city_names.  But that setup does make things simpler
+        elsewhere. */
+      for (j = 0; nations[i].city_names[j].name; j++) {
+       free(nations[i].city_names[j].name);
+      }
+      free(nations[i].city_names);
     }
   }
   free(nations);
Index: common/nation.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/nation.h,v
retrieving revision 1.12
diff -u -r1.12 nation.h
--- common/nation.h     2002/02/14 15:17:15     1.12
+++ common/nation.h     2002/04/28 03:17:30
@@ -28,6 +28,27 @@
 enum advisor_type {ADV_ISLAND, ADV_MILITARY, ADV_TRADE, ADV_SCIENCE, 
ADV_FOREIGN, 
                    ADV_ATTITUDE, ADV_DOMESTIC, ADV_LAST};
 
+/*
+ * The city_name structure holds information about a default choice for
+ * the city name.  The "name" field is, of course, just the name for
+ * the city.  The "river" and "terrain" fields are entries recording
+ * whether the terrain is present near the city - we give higher priority
+ * to cities which have matching terrain.  In the case of a river we only
+ * care if the city is _on_ the river, for other terrain features we give
+ * the bonus if the city is close to the terrain.  Both of these entries
+ * may hold a value of 0 (no preference), 1 (city likes the terrain), or -1
+ * (city doesn't like the terrain).
+ *
+ * This is controlled through the nation's ruleset like this:
+ *   cities = "Washington (ocean, river, swamp)", "New York (!mountains)"
+ */
+typedef int ternary;
+struct city_name {
+  char* name;
+  ternary river;
+  ternary terrain[T_COUNT];    
+};
+
 struct nation_type {
   char name[MAX_LEN_NAME];
   char name_plural[MAX_LEN_NAME];
@@ -37,11 +58,7 @@
   char *leader_name[MAX_NUM_LEADERS];
   bool  leader_is_male[MAX_NUM_LEADERS];
   int city_style;
-  char **default_city_names;
-  char **default_rcity_names;          /* river city names */
-  char **default_crcity_names;         /* coastal-river city names */
-  char **default_ccity_names;          /* coastal city names */
-  char **default_tcity_names[T_COUNT]; /* terrain-specific city names */
+  struct city_name *city_names;                /* The default city names. */
   struct Sprite *flag_sprite;
 
   /* untranslated copies: */
Index: server/citytools.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/citytools.c,v
retrieving revision 1.180
diff -u -r1.180 citytools.c
--- server/citytools.c  2002/04/15 18:50:36     1.180
+++ server/citytools.c  2002/04/28 03:17:31
@@ -52,13 +52,128 @@
 
 #include "citytools.h"
 
+static int evaluate_city_name_priority(int x, int y,
+                                      struct city_name *city_name,
+                                      int default_priority);
+static char *search_for_city_name(int x, int y, struct city_name *city_names);
 static void server_set_tile_city(struct city *pcity, int city_x, int city_y,
                                 enum city_tile_type type);
 
-char **misc_city_names; 
-int num_misc_city_names;
+struct city_name *misc_city_names;
 
 /****************************************************************
+Returns the priority of the city name at the given position,
+using its own internal algorithm.  Lower priority values are
+more desired, and all priorities are non-negative.
+
+This function takes into account game.natural_city_names, and
+should be able to deal with any future options we want to add.
+*****************************************************************/
+static int evaluate_city_name_priority(int x, int y,
+                                      struct city_name *city_name,
+                                      int default_priority)
+{
+  /* Lower values mean higher priority. */
+  float priority = (float)default_priority;
+  int goodness;
+  enum tile_terrain_type type;
+
+  /* Increasing this value will increase the difference caused by
+     (non-)matching terrain.  A matching terrain is mult_factor
+     "better" than an unlisted terrain, which is mult_factor
+     "better" than a non-matching terrain. */
+  const float mult_factor = 1.4;
+
+  /*
+   * If natural city names aren't being used, we just return the
+   * base value.  This will have the effect of the first-listed
+   * city being used.  We do this here rather than special-casing
+   * it elewhere because this localizes everything to this
+   * function, even though it's a bit inefficient.
+   */
+  if (!game.natural_city_names) {
+    return default_priority;
+  }
+
+  /*
+   * Assuming we're using the natural city naming system, we use
+   * an internal alorithm to calculate the priority of each name.
+   * It's a pretty fuzzy algorithm; we basically do the following:
+   *   - Change the priority scale from 0..n to 10..n+10.  This means
+   *     each successive city is 10% lower priority than the first.
+   *   - Multiply by a semi-random number.  This has the effect of
+   *     decreasing rounding errors in the successive calculations,
+   *     as well as introducing a smallish random effect.
+   *   - Check over all the applicable terrains, and
+   *     multiply or divide the priority based on whether or not
+   *     the terrain matches.  See comment below.
+   */
+
+  priority += 10.0;
+  priority *= 10.0 + myrand(5);
+
+  /*
+   * The terrain priority in the city_name struct will be either
+   * -1, 0, or 1.  We therefore take this as-is if the terrain is
+   * present, or negate it if not.
+   *
+   * The reason we multiply as well as divide the value is so
+   * that cities that don't care what terrain they are on (which
+   * is the default) will be left in the middle of the pack.  If
+   * we _only_ multiplied (or divided), then cities that had more
+   * terrain labels would have their priorities hurt (or helped).
+   */
+  goodness = map_has_special(x, y, S_RIVER) ?
+             city_name->river : -city_name->river;
+  if (goodness > 0) {
+    priority /= mult_factor;
+  } else if (goodness < 0) {
+    priority *= mult_factor;
+  }
+
+  for (type = T_FIRST; type < T_COUNT; type++) {
+    /* Now we do the same for every available terrain. */
+    goodness = is_terrain_near_tile(x, y, type) ?
+                city_name->terrain[type] : -city_name->terrain[type];
+    if (goodness > 0) {
+      priority /= mult_factor;
+    } else if (goodness < 0) {
+      priority *= mult_factor;
+    }
+  }
+
+  return (int)priority;        
+}
+
+/****************************************************************
+Searches through a city name list (a struct city_name array)
+to pick the best available city name, and returns a pointer to
+it.  The function checks if the city name is available and calls
+evaluate_city_name_priority to determine the priority of the
+city name.  If the list has no valid entries in it, NULL will be
+returned.
+*****************************************************************/
+static char *search_for_city_name(int x, int y, struct city_name *city_names)
+{
+  int choice, best_priority = -1;
+  char* best_name = NULL;
+
+  for (choice = 0; city_names[choice].name; choice++) {
+    if (!game_find_city_by_name(city_names[choice].name)) {
+      int priority = evaluate_city_name_priority(x, y, &city_names[choice],
+                                                choice);
+
+      if (best_priority == -1 || priority < best_priority) {
+        best_priority = priority;
+        best_name = city_names[choice].name;
+      }
+    }
+  }
+
+  return best_name;
+}
+
+/****************************************************************
 Come up with a default name when a new city is about to be built.
 Handle running out of names etc. gracefully.  Maybe we should keep
 track of which names have been rejected by the player, so that we do
@@ -69,9 +184,10 @@
 *****************************************************************/
 char *city_name_suggestion(struct player *pplayer, int x, int y)
 {
-  char **nptr;
-  int i, j;
+  char *name;
+  int i;
   struct nation_type *nation = get_nation_by_plr(pplayer);
+  /* tempname must be static because it's returned below. */
   static char tempname[MAX_LEN_NAME];
 
   static const int num_tiles = MAP_MAX_WIDTH * MAP_MAX_HEIGHT; 
@@ -81,54 +197,15 @@
   
   CHECK_MAP_POS(x,y);
 
-#define SEARCH_AND_RETURN_CITY_NAME(list)   \
-    for(nptr=list; *nptr; nptr++) {         \
-      if(!game_find_city_by_name(*nptr)) {  \
-        return *nptr;                       \
-      }                                     \
-    }
-
-  /* 
-   * Use a special name if the tile has a river, is coastal or there
-   * is an available name depending on the terrain.
-   */ 
-
-  /* deal with rivers */
-  if (map_has_special(x, y, S_RIVER)) {
-    if (is_terrain_near_tile(x, y, T_OCEAN)) {
-      /* coastal river */
-      SEARCH_AND_RETURN_CITY_NAME(nation->default_crcity_names);
-    } else {
-      /* non-coastal river */
-      SEARCH_AND_RETURN_CITY_NAME(nation->default_rcity_names);
-    }
-  }
-
   /* coastal */
-  if (is_terrain_near_tile(x, y, T_OCEAN)) {
-    SEARCH_AND_RETURN_CITY_NAME(nation->default_ccity_names);
+  name = search_for_city_name(x, y, nation->city_names);
+  if (name) {
+    return name;
   }
-  
-  /* check terrain type */
-  SEARCH_AND_RETURN_CITY_NAME(nation->
-                             default_tcity_names[map_get_terrain(x, y)]);
-
-  /* we haven't found a name: it's a normal tile or they're all used */
-  SEARCH_AND_RETURN_CITY_NAME(nation->default_city_names);
 
-#undef SEARCH_AND_RETURN_CITY_NAME
-
-  if (num_misc_city_names > 0) {
-    j = myrand(num_misc_city_names);
-  
-    for (i = 0; i < num_misc_city_names; i++) {
-      if (j >= num_misc_city_names) {
-       j = 0;
-      }
-      if (!game_find_city_by_name(misc_city_names[j])) 
-       return misc_city_names[j];
-      j++;
-    }
+  name = search_for_city_name(x, y, misc_city_names);
+  if (name) {
+    return name;
   }
 
   for (i = 1; i <= num_tiles; i++ ) {
@@ -137,6 +214,8 @@
       return tempname;
   }
   
+  /* This had better be impossible! */
+  assert(FALSE);
   return _("A poorly-named city");
 }
 
Index: server/citytools.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/citytools.h,v
retrieving revision 1.38
diff -u -r1.38 citytools.h
--- server/citytools.h  2002/03/05 10:56:36     1.38
+++ server/citytools.h  2002/04/28 03:17:32
@@ -15,6 +15,7 @@
 
 #include "packets.h"
 #include "city.h"
+#include "nation.h" /* for struct city_name */
 
 #define FOOD_WEIGHTING 19
 #define SHIELD_WEIGHTING 17
@@ -80,8 +81,7 @@
                         int target, bool is_unit, int event);
 
 char *city_name_suggestion(struct player *pplayer, int x, int y);
-extern char **misc_city_names; 
-extern int num_misc_city_names;
+extern struct city_name *misc_city_names;
 
 
 bool city_can_work_tile(struct city *pcity, int city_x, int city_y);
Index: server/ruleset.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/ruleset.c,v
retrieving revision 1.106
diff -u -r1.106 ruleset.c
--- server/ruleset.c    2002/04/18 09:59:20     1.106
+++ server/ruleset.c    2002/04/28 03:17:33
@@ -31,6 +31,7 @@
 #include "nation.h"
 #include "packets.h"
 #include "registry.h"
+#include "shared.h"
 #include "support.h"
 #include "tech.h"
 #include "unit.h"
@@ -76,6 +77,9 @@
 static void load_terrain_names(struct section_file *file);
 static void load_citystyle_names(struct section_file *file);
 static void load_nation_names(struct section_file *file);
+static struct city_name* load_city_name_list(struct section_file *file,
+                                            char *secfile_str1,
+                                            char *secfile_str2);
 
 static void load_ruleset_techs(struct section_file *file);
 static void load_ruleset_units(struct section_file *file);
@@ -1833,6 +1837,141 @@
 }
 
 /**************************************************************************
+  This function loads a city name list from a section file.  The file and
+  two section names (which will be concatenated) are passed in.  The
+  malloc'ed city name list (which is all filled out) will be returned.
+**************************************************************************/
+static struct city_name* load_city_name_list(struct section_file *file,
+                                            char *secfile_str1,
+                                            char *secfile_str2)
+{
+  int dim, j;
+  struct city_name *city_names;
+  int value;
+
+  /* First we read the strings from the section file (above). */
+  char **cities = secfile_lookup_str_vec(file, &dim, "%s%s",
+                                         secfile_str1, secfile_str2);
+
+  /*
+   * Now we allocate enough room in the city_names array to store
+   * all the name data.  The array is NULL-terminated by
+   * having a NULL name at the end.
+   */
+  city_names = fc_calloc(dim + 1, sizeof(struct city_name));
+  city_names[dim].name = NULL;
+
+  /*
+   * Each string will be of the form
+   * "<cityname> (<label>, <label>, ...)".  The cityname is just the
+   * name for this city, while each "label" matches a terrain type
+   * for the city (or "river"), with a preceeding !, -, or ~ to negate
+   * it.  The parentheses are optional (but necessary to have the
+   * settings, of course).  Our job is now to parse this into the
+   * city_name structure.
+   */
+  for (j = 0, value = 1; j < dim; j++, value++) {
+    char *name = strchr(cities[j], '(');
+
+    /*
+     * Now we wish to determine values for all of the city labels.
+     * A value of 0 means no preference (which is necessary so that
+     * the use of this is optional); -1 means the label is negated
+     * and 1 means it's labelled.  Mostly the parsing just involves
+     * a lot of ugly string handling...
+     */
+    memset(city_names[j].terrain, 0,
+          T_COUNT * sizeof(city_names[j].terrain[0]));
+    city_names[j].river = 0;
+
+    if (name) {
+      /*
+       * 0-terminate the original string, then find the
+       * close-parenthesis so that we can make sure we stop there.
+       */
+      char *next = strchr(name + 1, ')');
+      if (!next) {
+       freelog(LOG_ERROR,
+               "Badly formed city name %s in city name "
+               "ruleset \"%s%s\": unmatched parenthesis.",
+               cities[j], secfile_str1, secfile_str2);
+       assert(FALSE);
+      } else { /* if (!next) */
+        name[0] = next[0] = '\0';
+        name++;
+
+        /* Handle the labels one at a time. */
+        do {
+         int setting;
+
+         next = strchr(name, ',');
+         if (next) {
+           next[0] = '\0';
+         }
+         remove_leading_trailing_spaces(name);
+       
+         /*
+          * The ! is used to mark a negative, which is recorded
+          * with a -1.  Otherwise we use a 1.  '-' and '~' have
+          * the same meaning.
+          */
+         if (name[0] == '!' || name[0] == '-' || name[0] == '~') {
+           name++;
+           setting = -1;
+         } else {
+           setting = 1;
+         }
+       
+         if (!strcmp(name, "river")) {
+           city_names[j].river = setting;
+         } else {
+           /* "handled" tracks whether we find a match (for error handling) */
+           bool handled = FALSE;
+           enum tile_terrain_type type;
+       
+           for (type = T_FIRST; type < T_COUNT && !handled; type++) {
+              /*
+               * Note that at this time (before a call to
+               * translate_data_names) the terrain_name fields contains an
+               * untranslated string.  Note that name of T_RIVER is "".
+               * However this is not a problem because we take care of rivers
+               * separately.
+               */
+             if (!mystrcasecmp(name, tile_types[type].terrain_name)) {
+               city_names[j].terrain[type] = setting;
+               handled = TRUE;
+             }
+           }
+           if (!handled) {
+             freelog(LOG_ERROR, "Unreadable terrain description %s "
+                     "in city name ruleset \"%s%s\" - skipping it.",
+                     name, secfile_str1, secfile_str2);
+             assert(FALSE);
+           }
+         }
+         name = next ? next + 1 : NULL;
+        } while (name && name[0]);
+      } /* if (!next) */
+    } /* if (name) */
+    remove_leading_trailing_spaces(cities[j]);
+    city_names[j].name = mystrdup(cities[j]);
+    if (check_name(city_names[j].name)) {
+      /* The ruleset contains a name that is too long.  This shouldn't
+        happen - if it does, the author should get immediate feedback */
+      freelog(LOG_ERROR, "City name %s in ruleset for %s%s is too long "
+             "- shortening it.",
+              city_names[j].name, secfile_str1, secfile_str2);
+      assert(FALSE);
+      city_names[j].name[MAX_LEN_NAME - 1] = '\0';
+    }
+  }
+  if (cities) {
+    free(cities);
+  }
+  return city_names;
+}
+
+/**************************************************************************
 Load nations.ruleset file
 **************************************************************************/
 static void load_ruleset_nations(struct section_file *file)
@@ -1842,9 +1981,8 @@
   struct government *gov;
   int *res, dim, val, i, j, nval;
   char temp_name[MAX_LEN_NAME];
-  char **cities, **techs, **leaders, **sec;
+  char **techs, **leaders, **sec;
   const char *filename = secfile_filename(file);
-  enum tile_terrain_type type;
 
   datafile_options = check_ruleset_capabilities(file, "+1.9", filename);
 
@@ -2054,72 +2192,13 @@
 
     /* read city names */
 
-#define BASE_READ_CITY_NAME_LIST(target,format,arg1,arg2,arg3)  \
-  cities = secfile_lookup_str_vec(file, &dim, format,           \
-                                  arg1, arg2, arg3);            \
-  target = fc_calloc(dim + 1, sizeof(char *));                  \
-  target[dim] = NULL;                                           \
-  for (j = 0; j < dim; j++) {                                   \
-    target[j] = mystrdup(cities[j]);                            \
-    if (check_name(cities[j])) {                                \
-      target[j][MAX_LEN_NAME - 1] = 0;                          \
-    }                                                           \
-  }                                                             \
-  if (cities) {                                                 \
-    free(cities);                                               \
-  }
-
-#define READ_CITY_NAME_LIST(target_field,format,arg)            \
-  BASE_READ_CITY_NAME_LIST(pl->target_field, "%s." format "%s", \
-                           sec[i], arg, "cities")
-
     /* read "normal" city names */
-    READ_CITY_NAME_LIST(default_city_names, "%s", "");
-
-    /* read river city names */
-    READ_CITY_NAME_LIST(default_rcity_names, "%s", "river_");
-
-    /* read coastal-river city names */
-    READ_CITY_NAME_LIST(default_crcity_names, "%s","coastal_river_");
-
-    /* read coastal city names */
-    READ_CITY_NAME_LIST(default_ccity_names, "%s", "coastal_");
-
-    /* 
-     * Read terrain-specific city names. 
-     */    
-    for (type = T_FIRST; type < T_COUNT; type++) {
-      size_t k;
-      char namebuf[MAX_LEN_NAME];
-
-      /*
-       * Note that at this time (before a call to
-       * translate_data_names) the terrain_name fields contains an
-       * untranslated string. Note that name of T_RIVER is "". However
-       * this is not a problem because we take care of this using
-       * default_rcity_names.
-       */
-      (void) mystrlcpy(namebuf, tile_types[type].terrain_name,
-                      sizeof(tile_types[type].terrain_name));
-
-      /* transform to lower case */
-      for (k = 0; k < strlen(namebuf); k++) {
-       namebuf[k] = tolower(namebuf[k]);
-      }
 
-      READ_CITY_NAME_LIST(default_tcity_names[type], "%s_", namebuf);
-    }
+    pl->city_names = load_city_name_list(file, sec[i], ".cities");
   }
 
   /* read miscellaneous city names */
-  BASE_READ_CITY_NAME_LIST(misc_city_names, "misc.cities%s%s%s", "", "",
-                          "");
-
-  /* dim is set in BASE_READ_CITY_NAME_LIST */
-  num_misc_city_names = dim;
-
-#undef READ_CITY_NAME_LIST
-#undef BASE_READ_CITY_NAME_LIST
+  misc_city_names = load_city_name_list(file, "misc.cities", "");
 
   free(sec);
   section_file_check_unused(file, filename);
Index: server/stdinhand.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/stdinhand.c,v
retrieving revision 1.221
diff -u -r1.221 stdinhand.c
--- server/stdinhand.c  2002/04/25 14:09:38     1.221
+++ server/stdinhand.c  2002/04/28 03:17:36
@@ -607,6 +607,13 @@
              "choosed city, regardless on the knowledge of Masonry."), NULL, 
           GAME_DEFAULT_SAVEPALACE)
 
+  GEN_BOOL("naturalcitynames", game.natural_city_names,
+           SSET_RULES_FLEXIBLE, SSET_TO_CLIENT,
+           N_("Whether to use natural city names"),
+           N_("If enabled, the default city names will be determined based "
+              "on the surrounding terrain."),
+           NULL, GAME_DEFAULT_NATURALCITYNAMES)
+
 /* Meta options: these don't affect the internal rules of the game, but
  * do affect players.  Also options which only produce extra server
  * "output" and don't affect the actual game.

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