Complete.Org: Mailing Lists: Archives: freeciv-dev: November 2005:
[Freeciv-Dev] Re: (PR#14721) City Name Aliases
Home

[Freeciv-Dev] Re: (PR#14721) City Name Aliases

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
Subject: [Freeciv-Dev] Re: (PR#14721) City Name Aliases
From: "Benedict Adamson" <badamson@xxxxxxxxxxx>
Date: Tue, 29 Nov 2005 17:25:59 -0800
Reply-to: bugs@xxxxxxxxxxx

<URL: http://bugs.freeciv.org/Ticket/Display.html?id=14721 >

This version of the patch is complete: it provides the functionality for 
city aliases, aliases for foreign cities, automatic renaming of taken 
cities, and a small selection of example aliases.

diff -Xvendor.freeciv.current/diff_ignore -ruN 
vendor.freeciv.current/common/game.c freeciv.PR14721/common/game.c
--- vendor.freeciv.current/common/game.c        2005-11-29 00:57:25.000000000 
+0000
+++ freeciv.PR14721/common/game.c       2005-11-30 01:07:19.000000000 +0000
@@ -212,6 +212,7 @@
   game.info.celebratesize = GAME_DEFAULT_CELEBRATESIZE;
   game.info.savepalace    = GAME_DEFAULT_SAVEPALACE;
   game.info.natural_city_names = GAME_DEFAULT_NATURALCITYNAMES;
+  game.info.rename_taken_cities = GAME_DEFAULT_RENAMETAKENCITIES;
   game.info.unhappysize   = GAME_DEFAULT_UNHAPPYSIZE;
   game.info.angrycitizen  = GAME_DEFAULT_ANGRYCITIZEN;
   game.info.foodbox       = GAME_DEFAULT_FOODBOX;
diff -Xvendor.freeciv.current/diff_ignore -ruN 
vendor.freeciv.current/common/game.h freeciv.PR14721/common/game.h
--- vendor.freeciv.current/common/game.h        2005-11-29 00:57:25.000000000 
+0000
+++ freeciv.PR14721/common/game.h       2005-11-30 01:07:19.000000000 +0000
@@ -256,6 +256,8 @@
 
 #define GAME_DEFAULT_NATURALCITYNAMES TRUE
 
+#define GAME_DEFAULT_RENAMETAKENCITIES TRUE
+
 #define GAME_DEFAULT_AQUEDUCTLOSS    0
 #define GAME_MIN_AQUEDUCTLOSS        0
 #define GAME_MAX_AQUEDUCTLOSS        100
diff -Xvendor.freeciv.current/diff_ignore -ruN 
vendor.freeciv.current/common/nation.c freeciv.PR14721/common/nation.c
--- vendor.freeciv.current/common/nation.c      2005-11-29 00:57:25.000000000 
+0000
+++ freeciv.PR14721/common/nation.c     2005-11-30 01:07:19.000000000 +0000
@@ -25,6 +25,7 @@
 #include "fcintl.h"
 #include "game.h"
 #include "government.h"
+#include "hash.h"
 #include "log.h"
 #include "mem.h"
 #include "nation.h"
@@ -32,8 +33,20 @@
 #include "support.h"
 #include "tech.h"
 
+
+
 static struct nation_type *nations = NULL;
 
+/*
+ * A hash table mapping a canonical name string to the pointer-to
+ * the struct city_name_aliases for that canonical name.
+ *
+ * We need a hash-table, rather than a simple list, so add_city_name_alias
+ * is efficient. A naive implementation using a list gives O(N^2) performance
+ * because add_city_name_alias is called N times.
+ */
+static struct hash_table *all_city_aliases = NULL;
+
 static int num_nation_groups;
 static struct nation_group nation_groups[MAX_NUM_NATION_GROUPS];
 
@@ -238,12 +251,83 @@
 {
   int i;
 
+  assert(NULL == nations); /* else memory leak */
+  assert(NULL == all_city_aliases); /* else memory leak */
+
   nations = fc_calloc(num, sizeof(nations[0]));
   game.control.nation_count = num;
 
   for (i = 0; i < num; i++) {
     nations[i].index = i;
   }
+
+  all_city_aliases = hash_new(hash_fval_string, hash_fcmp_string);
+}
+
+/**************************************************************************
+ Find the list of aliases known so far for the given canonical name,
+ or NULL if it has no known aliases.
+**************************************************************************/
+static struct city_name_aliases *find_city_name_aliases(const char 
*canonical_name)
+{
+  assert(canonical_name);
+  return hash_lookup_data(all_city_aliases, canonical_name);
+}
+
+/**************************************************************************
+  Add a city-name alias to the list of all aliases.
+  The given name may be the first given alias for the given canonical_name,
+  in which case a new structure will be allocated to record the aliases
+  for that canonical_name.
+**************************************************************************/
+void add_city_name_alias(struct city_name *city_name,
+                         const char *canonical_name)
+{
+  struct city_name_aliases *aliases = find_city_name_aliases(canonical_name);
+
+  assert(all_city_aliases);
+  assert(city_name);
+  assert(canonical_name);
+
+  if (NULL == aliases) {
+    /* This is the first alias for the given canonical name,
+     * so create a struct for recording all aliases for the canonical name. */
+    aliases = fc_malloc(sizeof(struct city_name_aliases));
+    aliases->canonical_name = mystrdup(canonical_name);
+    aliases->names = city_name_list_new();
+
+    /* The hash-table code requires duplication of the key (canonical_name) */
+    hash_insert(all_city_aliases, mystrdup(canonical_name), aliases);
+  }
+
+  /* Add this alias to the list of aliases for the canonical name */
+  city_name_list_prepend(aliases->names, city_name);
+  city_name->aliases = aliases;
+}
+
+/***************************************************************
+ Remove all the cross-references supporting the aliases of a city name
+***************************************************************/
+static void remove_city_name_alias(struct city_name *city_name)
+{
+  assert(all_city_aliases);
+  assert(city_name);
+
+  if (NULL == city_name->aliases) {
+    /* This city name has no aliases */
+    return;
+  }
+
+  city_name_list_unlink(city_name->aliases->names, city_name);
+  if (0 == city_name_list_size(city_name->aliases->names)) {
+    /* That was the last alias for a particular canonical name, so remove
+     * the structure for recording the aliases of that canonical name */
+    hash_delete_entry(all_city_aliases, city_name->aliases->canonical_name);
+    free(city_name->aliases->canonical_name);
+    free(city_name->aliases->names);
+    free(city_name->aliases);
+  }
+  city_name->aliases = NULL;
 }
 
 /***************************************************************
@@ -306,6 +390,8 @@
 
   free(nations);
   nations = NULL;
+  hash_free(all_city_aliases);
+  all_city_aliases = NULL;
   game.control.nation_count = 0;
   num_nation_groups = 0;
 }
@@ -325,7 +411,10 @@
      * simpler elsewhere.
      */
     for (i = 0; city_names[i].name; i++) {
+      remove_city_name_alias(&city_names[i]);
       free(city_names[i].name);
+      /* Do not directly free city_names[i].aliases because it is shared.
+       * remove_city_name_alias should have done that if necessary. */
     }
     free(city_names);
   }
diff -Xvendor.freeciv.current/diff_ignore -ruN 
vendor.freeciv.current/common/nation.h freeciv.PR14721/common/nation.h
--- vendor.freeciv.current/common/nation.h      2005-11-29 00:57:25.000000000 
+0000
+++ freeciv.PR14721/common/nation.h     2005-11-30 01:07:19.000000000 +0000
@@ -31,6 +31,30 @@
 
 #define MAX_NUM_NATION_GROUPS 128
 
+struct city_name;
+
+/* get 'struct city_name_list' and related functions: */
+#define SPECLIST_TAG city_name
+#define SPECLIST_TYPE struct city_name
+#include "speclist.h"
+
+#define city_name_list_iterate(citynamelist, pcityname) \
+    TYPED_LIST_ITERATE(struct city_name, citynamelist, pcityname)
+#define city_name_list_iterate_end  LIST_ITERATE_END
+
+/*
+ * Record (references to) city names that are aliases for each other.
+ * The listed names are all aliases for each other: this is one group
+ * of aliases.
+ * The canonical_name is a the single name used to identify this group
+ * of aliases. Typically, the canonical_name will be equivalent to one of
+ * the listed names, although that is not required.
+ */
+struct city_name_aliases {
+  char* canonical_name;
+  struct city_name_list *names;
+};
+
 /*
  * The city_name structure holds information about a default choice for
  * the city name.  The "name" field is, of course, just the name for
@@ -42,14 +66,24 @@
  * 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)"
+ * If the aliases pointer is NULL, this name does not have aliases.
+ *
+ * This is controlled through the cities list of the nation's ruleset,
+ * like this:
+ *   "Washington (ocean, river, swamp)"
+ *   "Londinium (!mountains,=London)"
+ *   "Londres (<London)"
  */
 typedef int ternary;
 struct city_name {
   char* name;
+  /* A 'foreign' city name is the name used for a non-native city.
+   * Such names will never be used when naming a newly founded city,
+   * but may be used to rename conquests. */
+  bool foreign;
   ternary river;
   ternary terrain[MAX_NUM_TERRAINS];   
+  struct city_name_aliases *aliases;
 };
 
 struct leader {
@@ -125,6 +159,8 @@
 struct nation_type *get_nation_by_idx(Nation_type_id nation);
 bool check_nation_leader_name(const struct nation_type *nation,
                              const char *name);
+void add_city_name_alias(struct city_name *city_name,
+                         const char *canonical_name);
 void nations_alloc(int num);
 void nations_free(void);
 void nation_city_names_free(struct city_name *city_names);
diff -Xvendor.freeciv.current/diff_ignore -ruN 
vendor.freeciv.current/common/packets.def freeciv.PR14721/common/packets.def
--- vendor.freeciv.current/common/packets.def   2005-11-29 00:57:25.000000000 
+0000
+++ freeciv.PR14721/common/packets.def  2005-11-30 01:07:19.000000000 +0000
@@ -402,6 +402,7 @@
   UINT8 razechance;
   BOOL savepalace;
   BOOL natural_city_names;
+  BOOL rename_taken_cities;
   BOOL turnblock;
   BOOL fixedlength;
   BOOL auto_ai_toggle;
diff -Xvendor.freeciv.current/diff_ignore -ruN 
vendor.freeciv.current/data/nation/aborigines.ruleset 
freeciv.PR14721/data/nation/aborigines.ruleset
--- vendor.freeciv.current/data/nation/aborigines.ruleset       2005-11-29 
00:57:15.000000000 +0000
+++ freeciv.PR14721/data/nation/aborigines.ruleset      2005-11-30 
01:06:52.000000000 +0000
@@ -28,7 +28,7 @@
 ; City names are mostly from the state of New South Wales
 ; plus the places of birth for a few of the leaders.
 cities =
- "Kamberra", ; Aboriginal name that became Canberra in English
+ "Kamberra (=Canberra)", ; The original, Aboriginal, name.
  "Beeliar",
  "Raukkan",
  "Ukerabagh",
diff -Xvendor.freeciv.current/diff_ignore -ruN 
vendor.freeciv.current/data/nation/byzantium.ruleset 
freeciv.PR14721/data/nation/byzantium.ruleset
--- vendor.freeciv.current/data/nation/byzantium.ruleset        2005-11-29 
00:57:14.000000000 +0000
+++ freeciv.PR14721/data/nation/byzantium.ruleset       2005-11-30 
01:06:52.000000000 +0000
@@ -37,7 +37,7 @@
 civilwar_nations = "greek", "italian", "turk", "arab"
 
 cities =
- "Constantinople (ocean)",
+ "Constantinople (ocean,=Istanbul)",
  "Ephesus",
  "Iconium",
  "Roma",
@@ -45,7 +45,7 @@
  "Dyrrachium (ocean)",
  "Antioch",
  "Nicaea",
- "Thessalonica (ocean)",
+ "Thessalonica (ocean,=Thessaloniki)",
  "Rhodes (ocean)",
  "Tarsus",
  "Caesaria",
@@ -68,7 +68,7 @@
  "Germaniceia",
  "Sevastoupolis",
  "Prusa",
- "Athinae",
+ "Athinae (=Athina)",
  "Ioannina",
  "Pella", 
  "Doryleo",
diff -Xvendor.freeciv.current/diff_ignore -ruN 
vendor.freeciv.current/data/nation/english.ruleset 
freeciv.PR14721/data/nation/english.ruleset
--- vendor.freeciv.current/data/nation/english.ruleset  2005-11-29 
00:57:15.000000000 +0000
+++ freeciv.PR14721/data/nation/english.ruleset 2005-11-30 01:06:52.000000000 
+0000
@@ -110,4 +110,9 @@
  "Lancaster",
  "Northampton",
  "Salisbury",
- "Cowes"
+ "Cowes",
+ ; Foreign names
+ "Cologne (<Köln)",
+ "Copenhagen (<København)",
+ "Moscow (<Moskva)",
+ "Munich (<München)"
diff -Xvendor.freeciv.current/diff_ignore -ruN 
vendor.freeciv.current/data/nation/french.ruleset 
freeciv.PR14721/data/nation/french.ruleset
--- vendor.freeciv.current/data/nation/french.ruleset   2005-11-29 
00:57:15.000000000 +0000
+++ freeciv.PR14721/data/nation/french.ruleset  2005-11-30 01:06:52.000000000 
+0000
@@ -45,6 +45,8 @@
   "Cherbourg", "Caen", "Calais", "Limoges", "Clermont-Ferrand",
   "Nancy", "Besançon", "St. Etienne", "Brest", "Perpignan",
   "Vichy", "Rennes", "Le Havre", "La Rochelle", "Poitiers",
-  "St. Nazaire", "Bourges", "Le Mans", "Valenciennes", "Montlucon"
+  "St. Nazaire", "Bourges", "Le Mans", "Valenciennes", "Montlucon",
+  ; Foreign cities
+  "Londres (<London)"
 
 
diff -Xvendor.freeciv.current/diff_ignore -ruN 
vendor.freeciv.current/data/nation/german.ruleset 
freeciv.PR14721/data/nation/german.ruleset
--- vendor.freeciv.current/data/nation/german.ruleset   2005-11-29 
00:57:15.000000000 +0000
+++ freeciv.PR14721/data/nation/german.ruleset  2005-11-30 01:06:52.000000000 
+0000
@@ -175,4 +175,6 @@
   "Philippsburg (grassland, forest, hills, !ocean)",
   "Halberstadt (plains, hills)",
   "Bad Hersfeld (forest, hills)",
-  "Altenburg (forest, hills, !ocean)"
+  "Altenburg (forest, hills, !ocean)",
+  ; Foreign names
+  "Danzig (<GdaÅ?sk)"
diff -Xvendor.freeciv.current/diff_ignore -ruN 
vendor.freeciv.current/data/nation/greek.ruleset 
freeciv.PR14721/data/nation/greek.ruleset
--- vendor.freeciv.current/data/nation/greek.ruleset    2005-11-29 
00:57:14.000000000 +0000
+++ freeciv.PR14721/data/nation/greek.ruleset   2005-11-30 01:06:52.000000000 
+0000
@@ -32,7 +32,7 @@
  "Sparti",
  "Thivai",
  "Korinthos",
- "Konstandinoupolis",
+ "Konstandinoupolis (=Istanbul)",
  "Militos",
  "Efesos",
  "Peiraiefs (ocean)",
diff -Xvendor.freeciv.current/diff_ignore -ruN 
vendor.freeciv.current/data/nation/japanese.ruleset 
freeciv.PR14721/data/nation/japanese.ruleset
--- vendor.freeciv.current/data/nation/japanese.ruleset 2005-11-29 
00:57:15.000000000 +0000
+++ freeciv.PR14721/data/nation/japanese.ruleset        2005-11-30 
01:06:52.000000000 +0000
@@ -126,4 +126,6 @@
   "Kurume (!ocean)",
   "Toyohashi (ocean)",
   "Kawagoe (!ocean)",
-  "Tsu (ocean)"
+  "Tsu (ocean)",
+  ; Foreign cities
+  "Keijo (<Seoul)"
diff -Xvendor.freeciv.current/diff_ignore -ruN 
vendor.freeciv.current/data/nation/korean.ruleset 
freeciv.PR14721/data/nation/korean.ruleset
--- vendor.freeciv.current/data/nation/korean.ruleset   2005-11-29 
00:57:15.000000000 +0000
+++ freeciv.PR14721/data/nation/korean.ruleset  2005-11-30 01:06:52.000000000 
+0000
@@ -77,4 +77,6 @@
  "Gaeseong (!ocean)",
  "Nampo (ocean)",
  "Ganggye (!ocean)",
- "Gokseong (!ocean)"
+ "Gokseong (!ocean)",
+ ; Foreign names
+ "Donggyeon (<Tokyo)"
diff -Xvendor.freeciv.current/diff_ignore -ruN 
vendor.freeciv.current/data/nation/maori.ruleset 
freeciv.PR14721/data/nation/maori.ruleset
--- vendor.freeciv.current/data/nation/maori.ruleset    2005-11-29 
00:57:15.000000000 +0000
+++ freeciv.PR14721/data/nation/maori.ruleset   2005-11-30 01:06:52.000000000 
+0000
@@ -33,7 +33,7 @@
 ; Note that putting "(ocean)" as a qualifier for every city probably will not
 ; give very useful results.
 cities =
- "Poneke (ocean)",     ; Maori name for Wellington
+ "Poneke (ocean,=Wellington)",
  "Akarana",
  "Otautahi (ocean)",
  "Otepoti (ocean)",
diff -Xvendor.freeciv.current/diff_ignore -ruN 
vendor.freeciv.current/data/nation/phoenician.ruleset 
freeciv.PR14721/data/nation/phoenician.ruleset
--- vendor.freeciv.current/data/nation/phoenician.ruleset       2005-11-29 
00:57:14.000000000 +0000
+++ freeciv.PR14721/data/nation/phoenician.ruleset      2005-11-30 
01:06:52.000000000 +0000
@@ -67,7 +67,7 @@
  "Solus",
  "Motye",
  "Gaulos",
- "Melita",
+ "Melita (=Malta)",
  "Sulci",
  "Karteia",
  "Panormos",
diff -Xvendor.freeciv.current/diff_ignore -ruN 
vendor.freeciv.current/data/nation/roman.ruleset 
freeciv.PR14721/data/nation/roman.ruleset
--- vendor.freeciv.current/data/nation/roman.ruleset    2005-11-29 
00:57:15.000000000 +0000
+++ freeciv.PR14721/data/nation/roman.ruleset   2005-11-30 01:06:52.000000000 
+0000
@@ -40,10 +40,16 @@
   "Aquileia", "Florentia", "Bononia", "Verona", "Ostia", "Agrigentum",
   "Syracusae", "Neapolis", "Pompeii", "Salernum", "Tarraco",
   "Carthago", "Barcino", "Bracara", "Carthago Nova", "Baiae",
-  "Noviomagus", "Vindobona", "Atuatuca", "Caesarea", "Ierusalem",
+  "Noviomagus", "Vindobona", "Atuatuca", "Caesarea",
+  "Ierusalem (=Jerusalem)",
   "Trapezus", "Durocortorum", "Lutetia", "Burdigala", "Portus Namnetum",
-  "Utica", "Lugdunum", "Londinium", "Aquae Sulis", "Camulodunum", "Dubris",
-  "Eburacum", "Coriovallum", "Perusia", "Leptis Magna", "Corduba", "Edessa",
+  "Utica", "Lugdunum",
+  "Londinium (=London)",
+  "Aquae Sulis (=Bath)",
+  "Camulodunum (=Colchester)",
+  "Dubris",
+  "Eburacum (=York)",
+  "Coriovallum", "Perusia", "Leptis Magna", "Corduba", "Edessa",
   "Ariminum", "Narbo Martius", "Pergamum", "Vienna", "Hispalis",
   "Salonae", "Cyrenae", "Dyrrachium", "Luni", "Noreia", "Ephesus",
   "Panoramus", "Ancyra", "Caralis", "Patavium", "Petra", "Tolosa",
diff -Xvendor.freeciv.current/diff_ignore -ruN 
vendor.freeciv.current/data/nation/viking.ruleset 
freeciv.PR14721/data/nation/viking.ruleset
--- vendor.freeciv.current/data/nation/viking.ruleset   2005-11-29 
00:57:15.000000000 +0000
+++ freeciv.PR14721/data/nation/viking.ruleset  2005-11-30 01:06:52.000000000 
+0000
@@ -43,7 +43,7 @@
  "finnish", "russian", "scottish", "ukrainian"
 
 cities =
- "Skiringssal",                ; Viking era name for Kaupang, Norway
+ "Skiringssal (=Kaupang)",     ; Modern Norway
  "Birka",
  "Ribe",
  "Hedeby",
@@ -51,7 +51,7 @@
  "Reykjavik (ocean)",
  "Torshavn (ocean)",
  "Bjørgvin",
- "Jorvik (ocean)",
+ "Jorvik (ocean,=York)",
  "Fjaler",
  "Vikarskeid",
  "Hvitaby",
@@ -88,4 +88,6 @@
  "Aldeigjuborg (grassland)",
  "Bøle",
  "Jungufurda",
- "Apardjon"
+ "Apardjon",
+ ; Foreign names
+ "Miklagård (<Istanbul)"
diff -Xvendor.freeciv.current/diff_ignore -ruN 
vendor.freeciv.current/doc/README.nations freeciv.PR14721/doc/README.nations
--- vendor.freeciv.current/doc/README.nations   2005-11-29 00:57:08.000000000 
+0000
+++ freeciv.PR14721/doc/README.nations  2005-11-30 01:19:28.000000000 +0000
@@ -349,6 +349,51 @@
 to give bad results. This is a limitation of the system, and should be
 taken into account when labelling cities.)
 
+Freeciv supports aliases for city names. This is useful for rule-sets that have
+ancient nations and their successor nations, for successor nations that changed
+the city names. You indicate names are aliases by selecting one of the names as
+the 'canonical name' for the city, then annotating the aliases to indicate
+their associated canonical name. The annotation is simply a label consisting of
+an equals sign (=) or less-than sign (<) followed by the canonical name. We
+first describe the more common case, which is the use of an equals sign.
+
+For example, the modern English city of York has previously been known as
+Jorvik (to the Vikings) and Eburacum (to the Romans). Therefore a Roman
+rule-set could include an entry like this:
+  "Eburacum (=York)"
+a Viking rule-set could include an entry like this:
+  "Jorvik (=York)"
+and an English rule-set could include an entry like this:
+  "York"
+or, equivalently but redundantly, like this:
+  "York (=York)"
+
+Freeciv tries to avoid selecting as a "default" name a name that is an alias of
+an already existing city name. Freeciv treats all the aliases, including the
+canonical name itself, equally in this respect.
+
+The less-than sign is used to mark a city name as 'foreign'. This means that it
+is the name used by the nation for a city that (historically) belonged to a
+different nation. For example, the English city of London is known as Londres
+to the French. This can be represented in a French rule-set like this:
+  "Londres (<London)"
+
+Freeciv will never select a 'foreign' city name as a default name.
+
+If a nation takes a city from another nation, Freeciv might rename the city so
+it has a name more appropriate to the new owner: if the cities list of the new
+owner has an alias (even a 'foreign' alias) for the city, that alias is
+considered more appropriate. The server setting 'renametakencities' controls
+whether this is done. For example, if the Vikings were to capture the city of
+York, they will rename it to be Jorvik.
+
+The canonical name usually itself appears in the city list of some nation (as
+for "York" in the example above), however, that is not required. You should
+usually select as the canonical name the modern name as used by the modern
+sovereign nation in which it is located. When listing 'foreign' aliases, you
+should not usually bother listing aliases that are trivially different, such as
+resulting from the additional or removal of an accent.
+
 At this point, it is useful to put one city per line, only.
 
 Finally, don't forget to leave a blank line feed in the end of your nation
diff -Xvendor.freeciv.current/diff_ignore -ruN 
vendor.freeciv.current/server/citytools.c freeciv.PR14721/server/citytools.c
--- vendor.freeciv.current/server/citytools.c   2005-11-29 00:57:09.000000000 
+0000
+++ freeciv.PR14721/server/citytools.c  2005-11-30 01:06:43.000000000 +0000
@@ -57,6 +57,14 @@
 
 #include "citytools.h"
 
+
+
+enum city_rename_choice {
+  RENAME_KEEP,
+  RENAME_DUPLICATE,
+  RENAME_ALIAS
+};
+
 static int evaluate_city_name_priority(struct tile *ptile,
                                       struct city_name *city_name,
                                       int default_priority);
@@ -114,6 +122,30 @@
 }
 
 /****************************************************************
+  Whether an existing city has a name that is an alias
+  for a given (candidate) name.
+*****************************************************************/
+static bool is_city_alias_used(const struct city_name *city_name)
+{
+  assert(city_name);
+
+  if (city_name->aliases) {
+    /* Must examine the list of aliases.
+     * Each name typically has only a few aliases,
+     * so checking the entire list is not too expensive.
+     * We check even for 'foreign' aliases, in case someone
+     * has been perverse in manually choosing city names. */
+    city_name_list_iterate(city_name->aliases->names, alias) {
+      if (game_find_city_by_name(alias->name)) {
+        return TRUE;
+      }
+    } city_name_list_iterate_end;
+  }
+
+  return FALSE;
+}
+
+/****************************************************************
 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.
@@ -135,6 +167,8 @@
      "better" than a non-matching terrain. */
   const float mult_factor = 1.4;
 
+  assert(!city_name->foreign); /* else priority is undefined */
+
   /*
    * If natural city names aren't being used, we just return the
    * base value.  This will have the effect of the first-listed
@@ -195,6 +229,11 @@
     }
   } terrain_type_iterate_end;
 
+  /* Avoid choosing names that are merely aliases for existing cities */
+  if (is_city_alias_used(city_name)) {
+    priority *= mult_factor * mult_factor * mult_factor;
+  }
+
   return (int)priority;        
 }
 
@@ -232,6 +271,7 @@
 
   for (choice = 0; city_names[choice].name; choice++) {
     if (!game_find_city_by_name(city_names[choice].name)
+        && !city_names[choice].foreign
        && is_allowed_city_name(pplayer, city_names[choice].name, NULL, 0)) {
       int priority = evaluate_city_name_priority(ptile, &city_names[choice],
                                                 choice);
@@ -744,6 +784,110 @@
 }
 
 /**********************************************************************
+  If a nation could rename a city to any one of the aliases of that name,
+  which alias would be best?
+
+  The function returns the given name if none of the aliases are
+  preferred.
+***********************************************************************/
+static const char *get_preferred_city_alias(struct player *pplayer,
+                                            const char *old_name)
+{
+  int choice;
+  struct nation_type *nation = get_nation_by_plr(pplayer);
+
+  for (choice = 0; nation->city_names[choice].name; choice++) {
+    struct city_name *city_name = &nation->city_names[choice];
+    if (mystrcasecmp(old_name, city_name->name) == 0) {
+      /* The old name appears in the city list of this nation,
+       * so it is already the preferred name */
+      return city_name->name;
+    } else if (city_name->aliases) {
+      /* Must check whether the old name is an aliase for this listed name */
+      city_name_list_iterate(city_name->aliases->names, alias) {
+        if (mystrcasecmp(old_name, alias->name) == 0) {
+          /* The old name is an alias for this listed name,
+           * so prefer the listed name. */
+          return city_name->name;
+        }
+      } city_name_list_iterate_end;
+    }
+  }
+
+  return old_name; /* no preference */
+}
+
+/**********************************************************************
+  Change the name of a transferred city, if necessary.
+
+  This function prevents a player having two cities with the same name,
+  if that is not permitted and renames cities to their preferred names,
+  if that option is set and such renaming is possible.
+***********************************************************************/
+static void update_transferred_city_name(struct player *ptaker,
+                                         struct city *pcity)
+{
+  const char *new_city_name = pcity->name; /*no change*/
+  const bool must_rename = (game.info.allowed_city_names == 1
+                            && city_list_find_name(ptaker->cities,
+                                                   pcity->name));
+  enum city_rename_choice choice = RENAME_KEEP;
+
+  /* Decide what to do */
+  if (game.info.rename_taken_cities && must_rename) {
+    const char *preferred_name = get_preferred_city_alias(ptaker, pcity->name);
+    if (strcmp(preferred_name, pcity->name) != 0
+        && !game_find_city_by_name(preferred_name)) {
+      /* Peculiar, but not impossible */
+      choice = RENAME_ALIAS;
+      new_city_name = preferred_name;
+    } else {
+      /* Can not use the preferred alias; use anything instead */
+      choice = RENAME_DUPLICATE;
+      new_city_name = city_name_suggestion(ptaker, pcity->tile);
+    }
+  } else if (game.info.rename_taken_cities) { /* && !must_rename */
+    const char *preferred_name = get_preferred_city_alias(ptaker, pcity->name);
+    if (strcmp(preferred_name, pcity->name) != 0
+        && !game_find_city_by_name(preferred_name)) {
+      choice = RENAME_ALIAS;
+      new_city_name = preferred_name;
+    } else {
+      /* Can not use the preferred name */
+      choice = RENAME_KEEP;
+    }
+  } else if (must_rename) { /* && !game.info.rename_taken_cities */
+    choice = RENAME_DUPLICATE;
+    new_city_name = city_name_suggestion(ptaker, pcity->tile);
+  } else { /*!must_rename && !game.info.rename_taken_cities */
+    choice = RENAME_KEEP;
+  }
+
+  if (choice != RENAME_KEEP) {
+    char old_city_name[MAX_LEN_NAME];
+    sz_strlcpy(old_city_name, pcity->name);
+    sz_strlcpy(pcity->name, new_city_name);
+
+    switch (choice) {
+    case RENAME_KEEP:
+      assert(FALSE); /*never happens*/
+      break;
+    case RENAME_DUPLICATE:
+      notify_player(ptaker, pcity->tile, E_BAD_COMMAND,
+                    _("You already had a city called %s."
+                      " The city was renamed to %s."), old_city_name,
+                    pcity->name);
+      break;
+    case RENAME_ALIAS:
+      notify_player(ptaker, pcity->tile, E_BAD_COMMAND,
+                    _("You rename %s to be %s."), old_city_name,
+                    pcity->name);
+      break;
+    }
+  }
+}
+
+/**********************************************************************
 Handles all transactions in relation to transferring a city.
 
 The kill_outside and transfer_unit_verbose arguments are passed to
@@ -758,7 +902,6 @@
   struct player *pgiver = city_owner(pcity);
   int old_trade_routes[NUM_TRADEROUTES];
   bv_imprs had_small_wonders;
-  char old_city_name[MAX_LEN_NAME];
   struct vision *old_vision;
 
   assert(pgiver != ptaker);
@@ -796,16 +939,7 @@
                        vision_get_sight(old_vision, v));
   } vision_layer_iterate_end;
 
-  sz_strlcpy(old_city_name, pcity->name);
-  if (game.info.allowed_city_names == 1
-      && city_list_find_name(ptaker->cities, pcity->name)) {
-    sz_strlcpy(pcity->name,
-              city_name_suggestion(ptaker, pcity->tile));
-    notify_player(ptaker, pcity->tile, E_BAD_COMMAND,
-                    _("You already had a city called %s."
-                      " The city was renamed to %s."), old_city_name,
-                    pcity->name);
-  }
+  update_transferred_city_name(ptaker, pcity);
 
   /* Has to follow the unfog call above. */
   city_list_unlink(pgiver->cities, pcity);
diff -Xvendor.freeciv.current/diff_ignore -ruN 
vendor.freeciv.current/server/ruleset.c freeciv.PR14721/server/ruleset.c
--- vendor.freeciv.current/server/ruleset.c     2005-11-29 00:57:09.000000000 
+0000
+++ freeciv.PR14721/server/ruleset.c    2005-11-30 01:06:43.000000000 +0000
@@ -1910,6 +1910,7 @@
   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.
+  Also, update the lists of city aliases.
 **************************************************************************/
 static struct city_name* load_city_name_list(struct section_file *file,
                                             const char *secfile_str1,
@@ -1930,6 +1931,8 @@
    */
   city_names = fc_calloc(dim + 1, sizeof(*city_names));
   city_names[dim].name = NULL;
+  city_names[dim].foreign = TRUE;
+  city_names[dim].aliases = NULL;
 
   /*
    * Each string will be of the form
@@ -1941,6 +1944,7 @@
    * city_name structure.
    */
   for (j = 0, value = 1; j < dim; j++, value++) {
+    char *canonical_name = NULL;
     char *name = strchr(cities[j], '(');
 
     /*
@@ -1952,6 +1956,7 @@
      */
     memset(city_names[j].terrain, 0,
           T_COUNT * sizeof(city_names[j].terrain[0]));
+    city_names[j].foreign = FALSE;
     city_names[j].river = 0;
 
     if (name) {
@@ -1966,12 +1971,13 @@
                "ruleset \"%s%s\": unmatched parenthesis.",
                cities[j], secfile_str1, secfile_str2);
        assert(FALSE);
-      } else { /* if (!next) */
+      } else { /* if (next) */
         name[0] = next[0] = '\0';
         name++;
 
         /* Handle the labels one at a time. */
         do {
+         bool alias = FALSE;
          int setting;
 
          next = strchr(name, ',');
@@ -1981,17 +1987,32 @@
          remove_leading_trailing_spaces(name);
        
          /*
+           * The = is used to mark the canonical name.
+           * The < is used to mark the canonical name for an alias
+           * for a foreign city.
           * The ! is used to mark a negative, which is recorded
           * with a -1.  Otherwise we use a 1.
           */
-         if (name[0] == '!') {
+         if (name[0] == '=' || name[0] == '<') {
+            city_names[j].foreign = (name[0] == '<');
+           name++;
+            alias = TRUE;
+         } else if (name[0] == '!') {
            name++;
            setting = -1;
          } else {
            setting = 1;
          }
        
-         if (mystrcasecmp(name, "river") == 0) {
+          if (alias && canonical_name) {
+            freelog(LOG_ERROR, "More than one canonical name for city %s "
+                               "in city name ruleset \"%s%s\" - skipping it.",
+                               cities[j], secfile_str1, secfile_str2);
+            assert(FALSE);
+          } else if (alias) {
+            canonical_name = name;
+            /* No need to strdup the name; it will remain available. */
+         } else if (mystrcasecmp(name, "river") == 0) {
            city_names[j].river = setting;
          } else {
            /* "handled" tracks whether we find a match (for error handling) */
@@ -2033,6 +2054,18 @@
       assert(FALSE);
       city_names[j].name[MAX_LEN_NAME - 1] = '\0';
     }
+
+    if (canonical_name) {
+      add_city_name_alias(&city_names[j], canonical_name);
+    } else {
+      /* Take the given name as the canonical name.
+       * This makes it easier to incrementally add canonical names to
+       * rulesets: only names that are non-canonical need to be
+       * annotated with a canonical name.
+       * Thus "York" is equivalent to "York (=York)"
+       */
+      add_city_name_alias(&city_names[j], city_names[j].name);
+    }
   }
   if (cities) {
     free(cities);
diff -Xvendor.freeciv.current/diff_ignore -ruN 
vendor.freeciv.current/server/settings.c freeciv.PR14721/server/settings.c
--- vendor.freeciv.current/server/settings.c    2005-11-29 00:57:09.000000000 
+0000
+++ freeciv.PR14721/server/settings.c   2005-11-30 01:06:43.000000000 +0000
@@ -795,6 +795,14 @@
            N_("If enabled, the default city names will be determined based "
               "on the surrounding terrain."),
            NULL, GAME_DEFAULT_NATURALCITYNAMES)
+  GEN_BOOL("renametakencities", game.info.rename_taken_cities,
+           SSET_RULES_FLEXIBLE, SSET_SOCIOLOGY, SSET_RARE, SSET_TO_CLIENT,
+           N_("Whether to rename captured cities"),
+           N_("If enabled, captured cities will be renamed to the name used "
+              "by the new owning nation. "
+              "For example, if London were captured by the French "
+              "it would become Londres."),
+           NULL, GAME_DEFAULT_RENAMETAKENCITIES)
 
   /* Meta options: these don't affect the internal rules of the game, but
    * do affect players.  Also options which only produce extra server

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