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

On 1/27/07, Marko Lindqvist <[EMAIL PROTECTED]> wrote:
>
> On 1/26/07, Marko Lindqvist <[EMAIL PROTECTED]> wrote:
> >
> >  Introduced enum ai_level. Added functions find_ai_level_by_name(),
> > ai_level_name() and ai_level_name_cmd() to common/player.c. Used where
> > applicable.
>
>  - Handle 'away' players. This required new function is_settable_ai_level()
>  - Use these functions also when building gtk -client conn_menu

 - Use ai_level_name_cmd() also when writing ini file by /write command


 - ML

diff -Nurd -X.diff_ignore freeciv/client/connectdlg_common.c freeciv/client/connectdlg_common.c
--- freeciv/client/connectdlg_common.c	2007-01-27 23:46:13.000000000 +0200
+++ freeciv/client/connectdlg_common.c	2007-01-27 23:46:27.000000000 +0200
@@ -75,14 +75,6 @@
 
 int internal_server_port;
 
-const char *skill_level_names[NUM_SKILL_LEVELS] = { 
-  N_("novice"),
-  N_("easy"), 
-  N_("normal"), 
-  N_("hard")
- ,N_("experimental")
-};
-
 /************************************************************************** 
 The general chain of events:
 
diff -Nurd -X.diff_ignore freeciv/client/connectdlg_common.h freeciv/client/connectdlg_common.h
--- freeciv/client/connectdlg_common.h	2007-01-27 23:46:13.000000000 +0200
+++ freeciv/client/connectdlg_common.h	2007-01-27 23:46:27.000000000 +0200
@@ -34,15 +34,4 @@
 extern char player_name[MAX_LEN_NAME];
 extern char *current_filename;
 
-enum skill_levels { 
-  NOVICE, 
-  EASY, 
-  NORMAL, 
-  HARD, 
-  EXPERIMENTAL,
-  NUM_SKILL_LEVELS
-};
-
-extern const char *skill_level_names[NUM_SKILL_LEVELS];
-
 #endif  /* FC__CONNECTDLG_COMMON_H */ 
diff -Nurd -X.diff_ignore freeciv/client/gui-gtk-2.0/gui_main.c freeciv/client/gui-gtk-2.0/gui_main.c
--- freeciv/client/gui-gtk-2.0/gui_main.c	2007-01-27 23:46:13.000000000 +0200
+++ freeciv/client/gui-gtk-2.0/gui_main.c	2007-01-27 23:46:27.000000000 +0200
@@ -1522,21 +1522,9 @@
       } conn_list_iterate_end;
 
       if (pplayer->ai.control) {
-	sz_strlcpy(name, _("<AI>"));
-	switch (pplayer->ai.skill_level) {
-	case 2:
-	  sz_strlcpy(name, _("<Novice AI>"));
-	  break;
-	case 3:
-	  sz_strlcpy(name, _("<Easy AI>"));
-	  break;
-	case 5:
-	  sz_strlcpy(name, _("<Normal AI>"));
-	  break;
-	case 7:
-	  sz_strlcpy(name, _("<Hard AI>"));
-	  break;
-	}
+        /* TRANS: "<Novice AI>" */
+        my_snprintf(name, sizeof(name), _("<%s AI>"),
+                    ai_level_name(pplayer->ai.skill_level));
       } else if (access_level <= ALLOW_INFO) {
 	sz_strlcpy(name, pplayer->username);
       } else {
diff -Nurd -X.diff_ignore freeciv/client/gui-gtk-2.0/pages.c freeciv/client/gui-gtk-2.0/pages.c
--- freeciv/client/gui-gtk-2.0/pages.c	2007-01-27 23:46:13.000000000 +0200
+++ freeciv/client/gui-gtk-2.0/pages.c	2007-01-27 23:46:27.000000000 +0200
@@ -92,7 +92,7 @@
     /* Send new game defaults. */
     send_chat("/set aifill 5");
 
-    my_snprintf(buf, sizeof(buf), "/%s", skill_level_names[0]);
+    my_snprintf(buf, sizeof(buf), "/%s", ai_level_name_cmd(AI_LEVEL_DEFAULT));
     send_chat(buf);
   }
 }
@@ -934,8 +934,13 @@
 {
   const char *name;
   char buf[512];
+  enum ai_level level = GPOINTER_TO_UINT(data);
 
-  name = skill_level_names[GPOINTER_TO_UINT(data)];
+  if (level == AI_LEVEL_LAST) {
+    level = AI_LEVEL_DEFAULT;
+  }
+
+  name = ai_level_name_cmd(level);
 
   my_snprintf(buf, sizeof(buf), "/%s", name);
   send_chat(buf);
@@ -1254,9 +1259,7 @@
 
   if (aconnection.access_level >= ALLOW_CTRL
       && pplayer && pplayer->ai.control) {
-    char *difficulty[] = {N_("novice"), N_("easy"),
-			  N_("normal"), N_("hard")};
-    int i;
+    enum ai_level level;
 
     entry = gtk_separator_menu_item_new();
     g_object_set_data_full(G_OBJECT(menu),
@@ -1264,18 +1267,24 @@
 			   (GtkDestroyNotify) gtk_widget_unref);
     gtk_container_add(GTK_CONTAINER(menu), entry);
 
-    for (i = 0; i < ARRAY_SIZE(difficulty); i++) {
-      char text[128];
+    for (level = 0; level < AI_LEVEL_LAST; level++) {
+      if (is_settable_ai_level(level)) {
+        const char *level_name = ai_level_name(level);
+        const char *level_cmd = ai_level_name_cmd(level);
+        static char lvl_cmd_tmp[AI_LEVEL_LAST][50];
 
-      my_snprintf(text, sizeof(text), "%s", _(difficulty[i]));
-      entry = gtk_menu_item_new_with_label(text);
-      g_object_set_data_full(G_OBJECT(menu),
-			     difficulty[i], entry,
-			     (GtkDestroyNotify) gtk_widget_unref);
-      gtk_container_add(GTK_CONTAINER(menu), entry);
-      g_signal_connect(GTK_OBJECT(entry), "activate",
-		       GTK_SIGNAL_FUNC(conn_menu_player_command),
-		       difficulty[i]);
+        /* Copy to non-const string */
+        mystrlcpy(lvl_cmd_tmp[level], level_cmd, sizeof(lvl_cmd_tmp[level]));
+
+        entry = gtk_menu_item_new_with_label(level_name);
+        g_object_set_data_full(G_OBJECT(menu),
+                               lvl_cmd_tmp[level], entry,
+                               (GtkDestroyNotify) gtk_widget_unref);
+        gtk_container_add(GTK_CONTAINER(menu), entry);
+        g_signal_connect(GTK_OBJECT(entry), "activate",
+                         GTK_SIGNAL_FUNC(conn_menu_player_command),
+                         lvl_cmd_tmp[level]);
+      }
     }
   }
 
@@ -1373,7 +1382,7 @@
   GtkWidget *label, *menu, *item;
   GtkCellRenderer *rend;
   GtkTreeViewColumn *col;
-  int i;
+  enum ai_level level;
 
   box = gtk_vbox_new(FALSE, 8);
   gtk_container_set_border_width(GTK_CONTAINER(box), 4);
@@ -1416,13 +1425,17 @@
   option = gtk_option_menu_new();
 
   menu = gtk_menu_new();
-  for (i = 0; i < NUM_SKILL_LEVELS; i++) {
-    item = gtk_menu_item_new_with_label(_(skill_level_names[i]));
-    g_signal_connect(item, "activate",
-	G_CALLBACK(ai_skill_callback), GUINT_TO_POINTER(i));
+  for (level = 0; level < AI_LEVEL_LAST; level++) {
+    if (is_settable_ai_level(level)) {
+      const char *level_name = ai_level_name(level);
 
-    gtk_widget_show(item);
-    gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
+      item = gtk_menu_item_new_with_label(level_name);
+      g_signal_connect(item, "activate",
+                       G_CALLBACK(ai_skill_callback), GUINT_TO_POINTER(level));
+
+      gtk_widget_show(item);
+      gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
+    }
   }
   gtk_option_menu_set_menu(GTK_OPTION_MENU(option), menu);
   gtk_table_attach_defaults(GTK_TABLE(table), option, 1, 2, 1, 2);
@@ -1439,7 +1452,8 @@
   ruleset_combo = gtk_combo_new();
   gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(ruleset_combo)->entry), "default");
   g_signal_connect(GTK_COMBO(ruleset_combo)->entry, "changed",
-		   G_CALLBACK(ruleset_callback), GUINT_TO_POINTER(i));
+                   G_CALLBACK(ruleset_callback),
+                   GUINT_TO_POINTER(AI_LEVEL_LAST));
 
   gtk_table_attach_defaults(GTK_TABLE(table), ruleset_combo, 1, 2, 2, 3);
 
diff -Nurd -X.diff_ignore freeciv/client/gui-gtk-2.0/plrdlg.c freeciv/client/gui-gtk-2.0/plrdlg.c
--- freeciv/client/gui-gtk-2.0/plrdlg.c	2007-01-27 23:46:13.000000000 +0200
+++ freeciv/client/gui-gtk-2.0/plrdlg.c	2007-01-27 23:46:27.000000000 +0200
@@ -349,6 +349,7 @@
   int i;
   GtkWidget *sep, *sw;
   GtkWidget *menubar, *menu, *item, *vbox;
+  enum ai_level level;
 
   gui_dialog_new(&players_dialog_shell, GTK_NOTEBOOK(top_notebook), NULL);
   gui_dialog_set_title(players_dialog_shell, _("Players"));
@@ -510,11 +511,16 @@
   sep = gtk_separator_menu_item_new();
   gtk_menu_shell_append(GTK_MENU_SHELL(menu), sep);
 
-  for (i = 0; i < NUM_SKILL_LEVELS; i++) {
-    item = gtk_menu_item_new_with_label(_(skill_level_names[i]));
-    g_signal_connect(item, "activate",
-	G_CALLBACK(players_ai_skill_callback), GUINT_TO_POINTER(i));
-    gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
+  for (level = 0; level < AI_LEVEL_LAST; level++) {
+    if (is_settable_ai_level(level)) {
+      const char *level_name = ai_level_name(level);
+
+      item = gtk_menu_item_new_with_label(level_name);
+      g_signal_connect(item, "activate",
+                       G_CALLBACK(players_ai_skill_callback),
+                       GUINT_TO_POINTER(level));
+      gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
+    }
   }
   gtk_widget_show_all(menu);
 
@@ -849,8 +855,8 @@
     gtk_tree_model_get(model, &it, ncolumns - 1, &plrno, -1);
 
     my_snprintf(buf, sizeof(buf), "/%s %s",
-	skill_level_names[GPOINTER_TO_UINT(data)],
-	get_player(plrno)->name);
+                ai_level_name_cmd(GPOINTER_TO_UINT(data)),
+                get_player(plrno)->name);
     send_chat(buf);
   }
 }
diff -Nurd -X.diff_ignore freeciv/client/gui-sdl/pages.c freeciv/client/gui-sdl/pages.c
--- freeciv/client/gui-sdl/pages.c	2007-01-27 23:46:13.000000000 +0200
+++ freeciv/client/gui-sdl/pages.c	2007-01-27 23:46:27.000000000 +0200
@@ -62,7 +62,7 @@
       /* Send new game defaults. */
       send_chat("/set aifill 5");
   
-      my_snprintf(buf, sizeof(buf), "/%s", skill_level_names[0]);
+      my_snprintf(buf, sizeof(buf), "/%s", ai_level_name_cmd(AI_LEVEL_DEFAULT));
       send_chat(buf);
     }
   }
diff -Nurd -X.diff_ignore freeciv/common/fc_types.h freeciv/common/fc_types.h
--- freeciv/common/fc_types.h	2007-01-27 23:46:13.000000000 +0200
+++ freeciv/common/fc_types.h	2007-01-27 23:46:27.000000000 +0200
@@ -162,4 +162,15 @@
 #define DIR8_LAST 8
 #define DIR8_COUNT DIR8_LAST
 
+/* AI levels. This must correspond to ai_level_names[] in player.c */
+enum ai_level { AI_LEVEL_AWAY         = 1,
+                AI_LEVEL_NOVICE       = 2,
+                AI_LEVEL_EASY         = 3,
+                AI_LEVEL_NORMAL       = 5,
+                AI_LEVEL_HARD         = 7,
+                AI_LEVEL_EXPERIMENTAL = 10,
+                AI_LEVEL_LAST};
+
+#define AI_LEVEL_DEFAULT AI_LEVEL_NOVICE
+
 #endif /* FC__FC_TYPES_H */
diff -Nurd -X.diff_ignore freeciv/common/player.c freeciv/common/player.c
--- freeciv/common/player.c	2007-01-27 23:46:13.000000000 +0200
+++ freeciv/common/player.c	2007-01-27 23:46:27.000000000 +0200
@@ -34,6 +34,14 @@
 
 #include "player.h"
 
+/* Names of AI levels. These must correspond to enum ai_level in
+ * player.h. Also commands to set AI level in server/commands.c
+ * must match these. */
+static const char *ai_level_names[] = {
+  NULL, N_("Away"), N_("Novice"), N_("Easy"), NULL, N_("Normal"),
+  NULL, N_("Hard"), NULL, NULL, N_("Experimental")
+};
+
 /***************************************************************
   Returns true iff p1 can cancel treaty on p2.
 
@@ -834,3 +842,65 @@
   }
   return &(plr->team->research);
 }
+
+/****************************************************************************
+  Returns AI level associated with level name
+****************************************************************************/
+enum ai_level find_ai_level_by_name(const char *name)
+{
+  enum ai_level level;
+
+  for (level = 0; level < AI_LEVEL_LAST; level++) {
+    if (ai_level_names[level] != NULL) {
+      /* Only consider levels that really have names */
+      if (mystrcasecmp(ai_level_names[level], name) == 0) {
+        return level;
+      }
+    }
+  }
+
+  /* No level matches name */
+  return AI_LEVEL_LAST;
+}
+
+/***************************************************************
+  Return localized name of the AI level
+***************************************************************/
+const char *ai_level_name(enum ai_level level)
+{
+  assert(level >= 0 && level < AI_LEVEL_LAST);
+
+  if (ai_level_names[level] == NULL) {
+    return NULL;
+  }
+
+  return _(ai_level_names[level]);
+}
+
+/***************************************************************
+  Return cmd that sets given ai level
+***************************************************************/
+const char *ai_level_name_cmd(enum ai_level level)
+{
+  assert(level >= 0 && level < AI_LEVEL_LAST);
+
+  if (ai_level_names[level] == NULL) {
+    return NULL;
+  }
+
+  return ai_level_names[level];
+}
+
+/***************************************************************
+  Return is AI can be set to given level
+***************************************************************/
+bool is_settable_ai_level(enum ai_level level)
+{
+  if (level == AI_LEVEL_AWAY) {
+    /* Cannot set away level for AI */
+    return FALSE;
+  }
+
+  /* It's usable if it has name */
+  return ai_level_name_cmd(level) != NULL;
+}
diff -Nurd -X.diff_ignore freeciv/common/player.h freeciv/common/player.h
--- freeciv/common/player.h	2007-01-27 23:46:13.000000000 +0200
+++ freeciv/common/player.h	2007-01-27 23:46:27.000000000 +0200
@@ -104,7 +104,7 @@
   /* The units of tech_want seem to be shields */
   int tech_want[A_LAST+1];
   int handicap;			/* sum of enum handicap_type */
-  int skill_level;		/* 0-10 value for save/load/display */
+  enum ai_level skill_level;   	/* 0-10 value for save/load/display */
   int fuzzy;			/* chance in 1000 to mis-decide */
   int expand;			/* percentage factor to value new cities */
   int science_cost;             /* Cost in bulbs to get new tech, relative
@@ -304,5 +304,9 @@
 /* User functions. */
 bool is_valid_username(const char *name);
 
+enum ai_level find_ai_level_by_name(const char *name);
+const char *ai_level_name(enum ai_level level);
+const char *ai_level_name_cmd(enum ai_level level);
+bool is_settable_ai_level(enum ai_level level);
 
 #endif  /* FC__PLAYER_H */
diff -Nurd -X.diff_ignore freeciv/server/srv_main.c freeciv/server/srv_main.c
--- freeciv/server/srv_main.c	2007-01-27 23:46:13.000000000 +0200
+++ freeciv/server/srv_main.c	2007-01-27 23:46:27.000000000 +0200
@@ -1500,11 +1500,11 @@
     freelog(LOG_NORMAL,
 	    _("%s has been added as %s level AI-controlled player."),
             pplayer->name,
-	    name_of_skill_level(pplayer->ai.skill_level));
+	    ai_level_name(pplayer->ai.skill_level));
     notify_conn(NULL, NULL, E_SETTING,
 		_("%s has been added as %s level AI-controlled player."),
 		pplayer->name,
-		name_of_skill_level(pplayer->ai.skill_level));
+		ai_level_name(pplayer->ai.skill_level));
 
     game.info.nplayers++;
 
diff -Nurd -X.diff_ignore freeciv/server/stdinhand.c freeciv/server/stdinhand.c
--- freeciv/server/stdinhand.c	2007-01-27 23:46:13.000000000 +0200
+++ freeciv/server/stdinhand.c	2007-01-27 23:47:45.000000000 +0200
@@ -85,8 +85,10 @@
 static void show_teams(struct connection *caller);
 static void show_connections(struct connection *caller);
 static void show_scenarios(struct connection *caller);
-static bool set_ai_level(struct connection *caller, char *name, int level, 
-                         bool check);
+static bool set_ai_level_named(struct connection *caller, char *name,
+                               const char *level_name, bool check);
+static bool set_ai_level(struct connection *caller, char *name,
+                         enum ai_level level, bool check);
 static bool set_away(struct connection *caller, char *name, bool check);
 
 static bool observe_command(struct connection *caller, char *name, bool check);
@@ -659,20 +661,6 @@
 }
 
 /***************************************************************
- This could be in common/player if the client ever gets
- told the ai player skill levels.
-***************************************************************/
-const char *name_of_skill_level(int level)
-{
-  const char *nm[11] = { "UNUSED", "away", "novice", "easy",
-			 "UNKNOWN", "normal", "UNKNOWN", "hard",
-			 "UNKNOWN", "UNKNOWN", "experimental" };
-  
-  assert(level>0 && level<=10);
-  return nm[level];
-}
-
-/***************************************************************
 ...
 ***************************************************************/
 static int handicap_of_skill_level(int level)
@@ -1087,12 +1075,7 @@
 	cmdlevel_name(first_access_level));
 
     fprintf(script_file, "%s\n",
-        (game.info.skill_level == 1) ?       "away" :
-	(game.info.skill_level == 2) ?	"novice" :
-	(game.info.skill_level == 3) ?	"easy" :
-	(game.info.skill_level == 5) ?	"medium" :
-	(game.info.skill_level < 10) ?	"hard" :
-					"experimental");
+            ai_level_name_cmd(game.info.skill_level));
 
     if (*srvarg.metaserver_addr != '\0' &&
 	((0 != strcmp(srvarg.metaserver_addr, DEFAULT_META_SERVER_ADDR)))) {
@@ -1735,7 +1718,7 @@
 /******************************************************************
   Set an AI level and related quantities, with no feedback.
 ******************************************************************/
-void set_ai_level_directer(struct player *pplayer, int level)
+void set_ai_level_directer(struct player *pplayer, enum ai_level level)
 {
   pplayer->ai.handicap = handicap_of_skill_level(level);
   pplayer->ai.fuzzy = fuzzy_of_skill_level(level);
@@ -1765,21 +1748,31 @@
 /******************************************************************
   Set an AI level from the server prompt.
 ******************************************************************/
-void set_ai_level_direct(struct player *pplayer, int level)
+void set_ai_level_direct(struct player *pplayer, enum ai_level level)
 {
   set_ai_level_directer(pplayer,level);
   send_player_info(pplayer, NULL);
   cmd_reply(cmd_of_level(level), NULL, C_OK,
 	_("Player '%s' now has AI skill level '%s'."),
-	pplayer->name, name_of_skill_level(level));
+	pplayer->name, ai_level_name(level));
   
 }
 
 /******************************************************************
   Handle a user command to set an AI level.
 ******************************************************************/
+static bool set_ai_level_named(struct connection *caller, char *name,
+                               const char *level_name, bool check)
+{
+  enum ai_level level = find_ai_level_by_name(level_name);
+  return set_ai_level(caller, name, level, check);
+}
+
+/******************************************************************
+  Set AI level
+******************************************************************/
 static bool set_ai_level(struct connection *caller, char *name, 
-                         int level, bool check)
+                         enum ai_level level, bool check)
 {
   enum m_pre_result match_result;
   struct player *pplayer;
@@ -1797,7 +1790,7 @@
       send_player_info(pplayer, NULL);
       cmd_reply(cmd_of_level(level), caller, C_OK,
 		_("Player '%s' now has AI skill level '%s'."),
-		pplayer->name, name_of_skill_level(level));
+		pplayer->name, ai_level_name(level));
     } else {
       cmd_reply(cmd_of_level(level), caller, C_FAIL,
 		_("%s is not controlled by the AI."), pplayer->name);
@@ -1813,13 +1806,13 @@
 	send_player_info(pplayer, NULL);
         cmd_reply(cmd_of_level(level), caller, C_OK,
 		_("Player '%s' now has AI skill level '%s'."),
-		pplayer->name, name_of_skill_level(level));
+                  pplayer->name, ai_level_name(level));
       }
     } players_iterate_end;
     game.info.skill_level = level;
     cmd_reply(cmd_of_level(level), caller, C_OK,
 		_("Default AI skill level set to '%s'."),
-		name_of_skill_level(level));
+              ai_level_name(level));
   } else {
     cmd_reply_no_such_player(cmd_of_level(level), caller, name, match_result);
     return FALSE;
@@ -1848,7 +1841,7 @@
 		_("%s set to away mode."), 
                 caller->player->name);
     send_player_info(caller->player, NULL);
-    set_ai_level_directer(caller->player, 1);
+    set_ai_level_directer(caller->player, AI_LEVEL_AWAY);
     caller->player->ai.control = TRUE;
     cancel_all_meetings(caller->player);
   } else if (!check) {
@@ -3556,15 +3549,11 @@
   case CMD_AWAY:
     return set_away(caller, arg, check);
   case CMD_NOVICE:
-    return set_ai_level(caller, arg, 2, check);
   case CMD_EASY:
-    return set_ai_level(caller, arg, 3, check);
   case CMD_NORMAL:
-    return set_ai_level(caller, arg, 5, check);
   case CMD_HARD:
-    return set_ai_level(caller, arg, 7, check);
   case CMD_EXPERIMENTAL:
-    return set_ai_level(caller, arg, 10, check);
+    return set_ai_level_named(caller, arg, commands[cmd].name, check);
   case CMD_QUIT:
     return quit_game(caller, check);
   case CMD_CUT:
@@ -4073,7 +4062,7 @@
       }
       if(pplayer->ai.control) {
 	cat_snprintf(buf2, sizeof(buf2), _(", difficulty level %s"),
-		     name_of_skill_level(pplayer->ai.skill_level));
+                     ai_level_name(pplayer->ai.skill_level));
       }
       if (!game.info.is_new_game) {
 	cat_snprintf(buf2, sizeof(buf2), _(", nation %s"),
diff -Nurd -X.diff_ignore freeciv/server/stdinhand.h freeciv/server/stdinhand.h
--- freeciv/server/stdinhand.h	2007-01-27 23:46:13.000000000 +0200
+++ freeciv/server/stdinhand.h	2007-01-27 23:46:27.000000000 +0200
@@ -26,8 +26,8 @@
 bool handle_stdin_input(struct connection *caller, char *str, bool check);
 void report_server_options(struct conn_list *dest, int which);
 void send_server_settings(struct conn_list *dest);
-void set_ai_level_direct(struct player *pplayer, int level);
-void set_ai_level_directer(struct player *pplayer, int level);
+void set_ai_level_direct(struct player *pplayer, enum ai_level level);
+void set_ai_level_directer(struct player *pplayer, enum ai_level level);
 bool read_init_script(struct connection *caller, char *script_filename,
                       bool from_cmdline);
 void show_players(struct connection *caller);
@@ -38,8 +38,6 @@
 void toggle_ai_player_direct(struct connection *caller,
 			     struct player *pplayer);
 
-const char *name_of_skill_level(int level);
-
 /* for sernet.c in initing a new connection */
 enum cmdlevel_id access_level_for_next_connection(void);
 
_______________________________________________
Freeciv-dev mailing list
Freeciv-dev@gna.org
https://mail.gna.org/listinfo/freeciv-dev

Reply via email to