Complete.Org: Mailing Lists: Archives: freeciv-dev: September 2004:
[Freeciv-Dev] (PR#8877) patch: design for generalized specialists
Home

[Freeciv-Dev] (PR#8877) patch: design for generalized specialists

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
To: undisclosed-recipients: ;
Subject: [Freeciv-Dev] (PR#8877) patch: design for generalized specialists
From: "Jason Short" <jdorje@xxxxxxxxxxxxxxxxxxxxx>
Date: Fri, 3 Sep 2004 15:45:00 -0700
Reply-to: rt@xxxxxxxxxxx

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

Here is an updated (but not improved at all) patch.

jason

Index: ai/advdomestic.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/advdomestic.c,v
retrieving revision 1.118
diff -u -r1.118 advdomestic.c
--- ai/advdomestic.c    31 Aug 2004 15:52:46 -0000      1.118
+++ ai/advdomestic.c    3 Sep 2004 22:44:23 -0000
@@ -177,6 +177,54 @@
   return (ai->threats.missile && vulnerable) ? TRADE_WEIGHTING + 1 : 1;
 }
 
+#if 0
+/**************************************************************************
+Get value of best usable tile on city map.
+**************************************************************************/
+static int ai_best_tile_value(struct city *pcity)
+{
+  int best = 0;
+  int food;
+
+  /* food = (pcity->size * 2 - get_food_tile(2,2, pcity)) + 
settler_eats(pcity); */
+  /* Following simply works better, as far as I can tell. */
+  food = 0;
+  
+  city_map_iterate(x, y) {
+    if (can_place_worker_here(pcity, x, y)) {
+      int value = city_tile_value(pcity, x, y, food, 0);
+      
+      if (value > best) {
+       best = value;
+      }
+    }
+  } city_map_iterate_end;
+  
+  return best;
+}
+#endif
+
+static int get_num_entertainers(struct city *pcity)
+{
+  int elvis = 0;
+
+  /* Count all luxury-producing specialists.  We "estimate" that for every
+   * extra citizen made content one of these can be put to work elsewhere.
+   * This calculation should be improved. */
+  specialist_type_iterate(i) {
+    if (game.rgame.specialists[i].bonus_lux >= 2) {
+      elvis += pcity->specialists[i];
+    }
+  } specialist_type_iterate_end;
+
+  return elvis;
+}
+
+static int get_num_nonentertainers(struct city *pcity)
+{
+  return city_specialists(pcity) - get_num_entertainers(pcity);
+}
+
 /**************************************************************************
 Returns the value (desire to build it) of the improvement for keeping
 of order in the city.
@@ -198,8 +246,7 @@
 {
   /* How much one rebeling citizen counts - 16 is debatable value */
 #define SADVAL 16
-  /* Number of elvises in the city */
-  int elvis = pcity->specialists[SP_ELVIS];
+  int elvis = get_num_entertainers(pcity);
   /* Raw number of unhappy people */
   int sad = pcity->ppl_unhappy[0];
   /* Final number of content people */
@@ -233,9 +280,11 @@
    * re: Chapel -- Syela */
   while (happy > 0) { happy--; value += SADVAL; }
   
+#if 0
   freelog(LOG_DEBUG, "%s: %d elvis %d sad %d content %d size %d val",
          pcity->name, pcity->specialists[SP_ELVIS], pcity->ppl_unhappy[4],
          pcity->ppl_content[4], pcity->size, value);
+#endif
 
   return value;
 #undef SADVAL
@@ -477,7 +526,7 @@
   struct player *pplayer = city_owner(pcity);
   int bar, est_food, food, grana, hunger, needpower;
   int tprod, prod, sci, tax, t, val, wwtv;
-  int j, k;
+  int j, k, sp;
   int values[B_LAST];
   int nplayers = game.nplayers 
                  - team_count_members_alive(pplayer->team);
@@ -496,9 +545,7 @@
     sci = 0;
   }
 
-  est_food = (2 * pcity->specialists[SP_SCIENTIST]
-             + 2 * pcity->specialists[SP_TAXMAN]
-             + pcity->food_surplus);
+  est_food = (2 * get_num_nonentertainers(pcity) + pcity->food_surplus);
   prod = 
     (pcity->shield_prod * SHIELD_WEIGHTING * 100) / city_shield_bonus(pcity);
   needpower = (city_got_building(pcity, B_MFG) ? 2 :
@@ -515,9 +562,7 @@
   grana = food_weighting(grana + 1);
   hunger = 1;
   j = (pcity->size * 2) + settler_eats(pcity) - pcity->food_prod;
-  if (j >= 0
-      && pcity->specialists[SP_SCIENTIST] <= 0
-      && pcity->specialists[SP_TAXMAN] <= 0) {
+  if (j >= 0 && get_num_nonentertainers(pcity) == 0) {
     hunger += j + 1;
   }
 
@@ -603,8 +648,22 @@
     case B_MARKETPLACE:
     case B_BANK:
     case B_STOCK:
-      values[id] = (tax + 3 * pcity->specialists[SP_TAXMAN]
-                   + pcity->specialists[SP_ELVIS] * wwtv) / 2;
+      values[id] = tax; /* +50% to taxes */
+
+      for (sp = 0; sp < SP_COUNT; sp++) {
+       /* +50% to taxmen. */
+       values[id] += (pcity->specialists[sp]
+                      * game.rgame.specialists[sp].bonus_tax);
+
+       /* +50% to luxuries => converted to workers.  That's 1 more
+        * worker per 4 luxuries. */
+       values[id] += (pcity->specialists[sp]
+                      * game.rgame.specialists[sp].bonus_lux
+                      * wwtv) / 2;
+      }
+
+      /* Bonus is 50% */
+      values[id] /= 2;
       break;
     case B_SUPERHIGHWAYS:
       values[id] = road_trade(pcity) * t;
@@ -1026,9 +1085,7 @@
   Unit_Type_id unit_type;
   /* Food surplus assuming that workers and elvii are already accounted for
    * and properly balanced. */
-  int est_food = pcity->food_surplus
-                 + 2 * pcity->specialists[SP_SCIENTIST]
-                 + 2 * pcity->specialists[SP_TAXMAN];
+  int est_food = pcity->food_surplus + 2 * get_num_nonentertainers(pcity);
 
   init_choice(choice);
 
Index: ai/aicity.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aicity.c,v
retrieving revision 1.161
diff -u -r1.161 aicity.c
--- ai/aicity.c 2 Sep 2004 14:53:08 -0000       1.161
+++ ai/aicity.c 3 Sep 2004 22:44:23 -0000
@@ -677,7 +677,7 @@
       is_valid = map_to_city_map(&city_map_x, &city_map_y, acity, x, y);
       assert(is_valid);
       server_remove_worker_city(acity, city_map_x, city_map_y);
-      acity->specialists[SP_ELVIS]++;
+      acity->specialists[DEFAULT_SPECIALIST]++;
       if (!city_list_find_id(&minilist, acity->id)) {
        city_list_insert(&minilist, acity);
       }
Index: client/citydlg_common.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/citydlg_common.c,v
retrieving revision 1.44
diff -u -r1.44 citydlg_common.c
--- client/citydlg_common.c     1 Sep 2004 03:16:47 -0000       1.44
+++ client/citydlg_common.c     3 Sep 2004 22:44:24 -0000
@@ -422,7 +422,7 @@
 void city_rotate_specialist(struct city *pcity, int citizen_index)
 {
   struct citizen_type citizens[MAX_CITY_SIZE];
-  enum specialist_type from, to;
+  int from, to;
 
   if (citizen_index < 0 || citizen_index >= pcity->size) {
     return;
@@ -573,8 +573,7 @@
 /**************************************************************************
   Change a specialist in the given city.  Return the request ID.
 **************************************************************************/
-int city_change_specialist(struct city *pcity, enum specialist_type from,
-                          enum specialist_type to)
+int city_change_specialist(struct city *pcity, int from, int to)
 {
   return dsend_packet_city_change_specialist(&aconnection, pcity->id, from,
                                             to);
Index: client/citydlg_common.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/citydlg_common.h,v
retrieving revision 1.24
diff -u -r1.24 citydlg_common.h
--- client/citydlg_common.h     3 Sep 2004 04:22:36 -0000       1.24
+++ client/citydlg_common.h     3 Sep 2004 22:44:24 -0000
@@ -32,7 +32,7 @@
     CITIZEN_ANGRY,
     CITIZEN_LAST
   } type;
-  enum specialist_type spec_type;
+  int spec_type;
 };
 
 int get_citydlg_canvas_width(void);
@@ -67,8 +67,7 @@
 bool city_can_buy(const struct city *pcity);
 int city_sell_improvement(struct city *pcity, Impr_Type_id sell_id);
 int city_buy_production(struct city *pcity);
-int city_change_specialist(struct city *pcity, enum specialist_type from,
-                          enum specialist_type to);
+int city_change_specialist(struct city *pcity, int from, int to);
 int city_toggle_worker(struct city *pcity, int city_x, int city_y);
 int city_rename(struct city *pcity, const char *name);
 
Index: client/cityrepdata.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/cityrepdata.c,v
retrieving revision 1.34
diff -u -r1.34 cityrepdata.c
--- client/cityrepdata.c        28 Aug 2004 19:15:39 -0000      1.34
+++ client/cityrepdata.c        3 Sep 2004 22:44:24 -0000
@@ -115,13 +115,19 @@
 static char *cr_entry_specialists(struct city *pcity)
 {
   static char buf[32];
-  my_snprintf(buf, sizeof(buf), "%d/%d/%d",
-             pcity->specialists[SP_ELVIS],
-             pcity->specialists[SP_SCIENTIST],
-             pcity->specialists[SP_TAXMAN]);
+
+  buf[0] = '\0';
+  specialist_type_iterate(i) {
+    if (i != 0) {
+      sz_strlcat(buf, "/");
+    }
+    my_snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
+               "%d", pcity->specialists[i]);
+  } specialist_type_iterate_end;
   return buf;
 }
 
+#if 0
 static char *cr_entry_entertainers(struct city *pcity)
 {
   static char buf[8];
@@ -142,6 +148,7 @@
   my_snprintf(buf, sizeof(buf), "%2d", pcity->specialists[SP_TAXMAN]);
   return buf;
 }
+#endif
 
 static char *cr_entry_attack(struct city *pcity)
 {
@@ -442,12 +449,14 @@
     N_("?entertainers/scientists/taxmen:E/S/T"),
     N_("Entertainers, Scientists, Taxmen"),
     FUNC_TAG(specialists) },
+#if 0
   { FALSE, 2, 1, NULL, N_("?Entertainers:E"), N_("Entertainers"),
     FUNC_TAG(entertainers) },
   { FALSE, 2, 1, NULL, N_("?Scientists:S"), N_("Scientists"),
     FUNC_TAG(scientists) },
   { FALSE, 2, 1, NULL, N_("?Taxmen:T"), N_("Taxmen"),
     FUNC_TAG(taxmen) },
+#endif
   { FALSE, 8, 1, N_("Best"), N_("attack"),
     N_("Best attacking units"), FUNC_TAG(attack)},
   { FALSE, 8, 1, N_("Best"), N_("defense"),
Index: client/cityrepdata.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/cityrepdata.h,v
retrieving revision 1.10
diff -u -r1.10 cityrepdata.h
--- client/cityrepdata.h        3 Sep 2004 04:22:36 -0000       1.10
+++ client/cityrepdata.h        3 Sep 2004 22:44:24 -0000
@@ -21,7 +21,7 @@
 #define REPORT_CITYNAME_ABBREV 15
 
 /* Number of city report columns: have to set this manually now... */
-#define NUM_CREPORT_COLS 33
+#define NUM_CREPORT_COLS 30
 
 struct city_report_spec {
   bool show;                   /* modify this to customize */
Index: client/packhand.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/packhand.c,v
retrieving revision 1.400
diff -u -r1.400 packhand.c
--- client/packhand.c   31 Aug 2004 04:40:48 -0000      1.400
+++ client/packhand.c   3 Sep 2004 22:44:24 -0000
@@ -2709,10 +2709,15 @@
 {
   int i;
 
+  /* Must set num_specialist_types before iterating over them. */
+  game.rgame.num_specialist_types = packet->num_specialist_types;
+  game.rgame.default_specialist = packet->default_specialist;
   specialist_type_iterate(sp) {
     sz_strlcpy(game.rgame.specialists[sp].name, packet->specialist_name[sp]);
     game.rgame.specialists[sp].min_size = packet->specialist_min_size[sp];
-    game.rgame.specialists[sp].bonus = packet->specialist_bonus[sp];
+    game.rgame.specialists[sp].bonus_sci = packet->specialist_bonus_sci[sp];
+    game.rgame.specialists[sp].bonus_tax = packet->specialist_bonus_tax[sp];
+    game.rgame.specialists[sp].bonus_lux = packet->specialist_bonus_lux[sp];
   } specialist_type_iterate_end;
   tilespec_setup_specialist_types();
 
Index: client/tilespec.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/tilespec.h,v
retrieving revision 1.85
diff -u -r1.85 tilespec.h
--- client/tilespec.h   3 Sep 2004 04:22:36 -0000       1.85
+++ client/tilespec.h   3 Sep 2004 22:44:24 -0000
@@ -151,7 +151,7 @@
      * sprites, as defined by the tileset. */
     int count;
     struct Sprite *sprite[MAX_NUM_CITIZEN_SPRITES];
-  } citizen[NUM_TILES_CITIZEN], specialist[SP_COUNT];
+  } citizen[NUM_TILES_CITIZEN], specialist[MAX_NUM_SPECIALISTS];
   struct {
     struct Sprite
       *solar_panels,
Index: client/agents/cma_core.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/agents/cma_core.c,v
retrieving revision 1.61
diff -u -r1.61 cma_core.c
--- client/agents/cma_core.c    23 Jul 2004 05:06:12 -0000      1.61
+++ client/agents/cma_core.c    3 Sep 2004 22:44:24 -0000
@@ -104,9 +104,10 @@
 
   T(disorder);
   T(happy);
-  T(specialists[SP_ELVIS]);
-  T(specialists[SP_SCIENTIST]);
-  T(specialists[SP_TAXMAN]);
+
+  specialist_type_iterate(sp) {
+    T(specialists[sp]);
+  } specialist_type_iterate_end;
 
   for (stat = 0; stat < NUM_STATS; stat++) {
     T(surplus[stat]);
@@ -194,7 +195,7 @@
 static bool apply_result_on_server(struct city *pcity,
                                   const struct cm_result *const result)
 {
-  int first_request_id = 0, last_request_id = 0, i, sp;
+  int first_request_id = 0, last_request_id = 0, i;
   struct cm_result current_state;
   bool success;
 
@@ -232,22 +233,24 @@
     }
   } my_city_map_iterate_end;
 
-  /* Change the excess non-elvis specialists to elvises. */
-  assert(SP_ELVIS == 0);
-  for (sp = 1; sp < SP_COUNT; sp++) {
+  /* Change the excess non-(default) specialists to (default). */
+  specialist_type_iterate(sp) {
+    if (sp == DEFAULT_SPECIALIST) {
+      continue;
+    }
     for (i = 0; i < pcity->specialists[sp] - result->specialists[sp]; i++) {
-      last_request_id = city_change_specialist(pcity, sp, SP_ELVIS);
+      last_request_id = city_change_specialist(pcity, sp, DEFAULT_SPECIALIST);
       if (first_request_id == 0) {
        first_request_id = last_request_id;
       }
     }
-  }
+  } specialist_type_iterate_end;
 
   /* now all surplus people are enterainers */
 
   /* Set workers */
-  /* FIXME: This code assumes that any toggled worker will turn into an
-   * elvis! */
+  /* FIXME: This code assumes that any toggled worker will turn into a
+   * DEFAULT_SPECIALIST! */
   my_city_map_iterate(pcity, x, y) {
     if (result->worker_positions_used[x][y] &&
        pcity->city_map[x][y] != C_TILE_WORKER) {
@@ -259,17 +262,19 @@
     }
   } my_city_map_iterate_end;
 
-  /* Set all specialists except SP_ELVIS (all the unchanged ones remain
-   * as elvises). */
-  assert(SP_ELVIS == 0);
-  for (sp = 1; sp < SP_COUNT; sp++) {
+  /* Set all specialists except DEFAULT_SPECIALIST (all the unchanged ones
+   * remain as DEFAULT_SPECIALIST). */
+  specialist_type_iterate(sp) {
+    if (sp == DEFAULT_SPECIALIST) {
+      continue;
+    }
     for (i = 0; i < result->specialists[sp] - pcity->specialists[sp]; i++) {
-      last_request_id = city_change_specialist(pcity, SP_ELVIS, sp);
+      last_request_id = city_change_specialist(pcity, DEFAULT_SPECIALIST, sp);
       if (first_request_id == 0) {
        first_request_id = last_request_id;
       }
     }
-  }
+  } specialist_type_iterate_end;
 
   if (last_request_id == 0 || ALWAYS_APPLY_AT_SERVER) {
       /*
Index: client/agents/cma_fec.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/agents/cma_fec.c,v
retrieving revision 1.24
diff -u -r1.24 cma_fec.c
--- client/agents/cma_fec.c     28 Aug 2004 19:15:39 -0000      1.24
+++ client/agents/cma_fec.c     3 Sep 2004 22:44:24 -0000
@@ -337,6 +337,7 @@
       my_snprintf(buf[j], BUFFER_SIZE, "%+3d", result->surplus[j]);
     }
 
+#if 0
     my_snprintf(buf[6], BUFFER_SIZE, "%d/%d/%d/%d%s",
                pcity->size -
                (result->specialists[SP_ELVIS]
@@ -346,6 +347,9 @@
                result->specialists[SP_SCIENTIST],
                result->specialists[SP_TAXMAN],
                result->happy ? _(" happy") : "");
+#else
+    my_snprintf(buf[6], BUFFER_SIZE, "FIXME");
+#endif
 
     my_snprintf(buf[7], BUFFER_SIZE, "%s",
                get_city_growth_string(pcity, result->surplus[FOOD]));
Index: common/capstr.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/capstr.c,v
retrieving revision 1.183
diff -u -r1.183 capstr.c
--- common/capstr.c     3 Sep 2004 03:56:58 -0000       1.183
+++ common/capstr.c     3 Sep 2004 22:44:24 -0000
@@ -73,7 +73,7 @@
  * are not directly related to the capability strings discussed here.)
  */
 
-#define CAPABILITY "+Freeciv.Devel.2004.Sep.2"
+#define CAPABILITY "+Freeciv.Devel.2004.Sep.3"
 
 void init_our_capability(void)
 {
Index: common/city.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/city.c,v
retrieving revision 1.240
diff -u -r1.240 city.c
--- common/city.c       1 Sep 2004 19:45:19 -0000       1.240
+++ common/city.c       3 Sep 2004 22:44:25 -0000
@@ -515,8 +515,7 @@
 /****************************************************************************
   Returns TRUE iff if the given city can use this kind of specialist.
 ****************************************************************************/
-bool city_can_use_specialist(const struct city *pcity,
-                            enum specialist_type type)
+bool city_can_use_specialist(const struct city *pcity, int type)
 {
   return pcity->size >= game.rgame.specialists[type].min_size;
 }
@@ -1850,12 +1849,15 @@
   get_tax_income(city_owner(pcity), pcity->trade_prod, &pcity->science_total, 
                  &pcity->luxury_total, &pcity->tax_total);
 
-  pcity->luxury_total += (pcity->specialists[SP_ELVIS]
-                         * game.rgame.specialists[SP_ELVIS].bonus);
-  pcity->science_total += (pcity->specialists[SP_SCIENTIST]
-                          * game.rgame.specialists[SP_SCIENTIST].bonus);
-  pcity->tax_total += (pcity->specialists[SP_TAXMAN]
-                       * game.rgame.specialists[SP_TAXMAN].bonus);
+  specialist_type_iterate(sp) {
+    pcity->luxury_total
+      += pcity->specialists[sp] * game.rgame.specialists[sp].bonus_lux;
+    pcity->science_total
+      += pcity->specialists[sp] * game.rgame.specialists[sp].bonus_sci;
+    pcity->tax_total
+      += pcity->specialists[sp] * game.rgame.specialists[sp].bonus_tax;
+  } specialist_type_iterate_end;
+
   pcity->tax_total += get_city_tithes_bonus(pcity);
 }
 
@@ -2463,6 +2465,48 @@
   return count;
 }
 
+int best_science_specialist(void)
+{
+  int best = DEFAULT_SPECIALIST, val = -1;
+
+  specialist_type_iterate(i) {
+    if (game.rgame.specialists[i].bonus_sci > val) {
+      best = i;
+      val = game.rgame.specialists[i].bonus_sci;
+    }
+  } specialist_type_iterate_end;
+
+  return best;
+}
+
+int best_tax_specialist(void)
+{
+  int best = DEFAULT_SPECIALIST, val = -1;
+
+  specialist_type_iterate(i) {
+    if (game.rgame.specialists[i].bonus_tax > val) {
+      best = i;
+      val = game.rgame.specialists[i].bonus_tax;
+    }
+  } specialist_type_iterate_end;
+
+  return best;
+}
+
+int best_luxury_specialist(void)
+{
+  int best = DEFAULT_SPECIALIST, val = -1;
+
+  specialist_type_iterate(i) {
+    if (game.rgame.specialists[i].bonus_lux > val) {
+      best = i;
+      val = game.rgame.specialists[i].bonus_lux;
+    }
+  } specialist_type_iterate_end;
+
+  return best;
+}
+
 /**************************************************************************
   Return the power (pacifying effect) of temples in the city.
 **************************************************************************/
@@ -2613,7 +2657,7 @@
   specialist_type_iterate(sp) {
     pcity->specialists[sp] = 0;
   } specialist_type_iterate_end;
-  pcity->specialists[SP_ELVIS] = 1;
+  pcity->specialists[DEFAULT_SPECIALIST] = 1;
   pcity->ppl_happy[4] = 0;
   pcity->ppl_content[4] = 1;
   pcity->ppl_unhappy[4] = 0;
Index: common/city.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/city.h,v
retrieving revision 1.158
diff -u -r1.158 city.h
--- common/city.h       3 Sep 2004 04:22:36 -0000       1.158
+++ common/city.h       3 Sep 2004 22:44:25 -0000
@@ -22,10 +22,6 @@
   TYPE_UNIT, TYPE_NORMAL_IMPROVEMENT, TYPE_WONDER
 };
 
-enum specialist_type {
-  SP_ELVIS, SP_SCIENTIST, SP_TAXMAN, SP_COUNT
-};
-
 enum city_tile_type {
   C_TILE_EMPTY, C_TILE_WORKER, C_TILE_UNAVAILABLE
 };
@@ -79,6 +75,9 @@
  */
 #define MAX_CITY_SIZE                                  100
 
+/* Maximum number of specialist types. */
+#define MAX_NUM_SPECIALISTS 20
+
 /*
  * Iterate a city map.  This iterates over all city positions in the
  * city map (i.e., positions that are workable by the city) in unspecified
@@ -226,7 +225,7 @@
   int ppl_happy[5], ppl_content[5], ppl_unhappy[5], ppl_angry[5];
 
   /* Specialists */
-  int specialists[SP_COUNT];
+  int specialists[MAX_NUM_SPECIALISTS];
 
   /* trade routes */
   int trade[NUM_TRADEROUTES], trade_value[NUM_TRADEROUTES];
@@ -363,7 +362,7 @@
 bool can_build_unit(const struct city *pcity, Unit_Type_id id);
 bool can_build_unit_direct(const struct city *pcity, Unit_Type_id id);
 bool can_eventually_build_unit(const struct city *pcity, Unit_Type_id id);
-bool city_can_use_specialist(const struct city *pcity, enum specialist_type 
type);
+bool city_can_use_specialist(const struct city *pcity, int type);
 bool city_got_building(const struct city *pcity,  Impr_Type_id id); 
 bool city_affected_by_wonder(const struct city *pcity, Impr_Type_id id);
 bool city_got_effect(const struct city *pcity, Impr_Type_id id);
@@ -483,6 +482,9 @@
 int city_corruption(const struct city *pcity, int trade);
 int city_waste(const struct city *pcity, int shields);
 int city_specialists(const struct city *pcity);                 /* 
elv+tax+scie */
+int best_science_specialist(void);
+int best_tax_specialist(void);
+int best_luxury_specialist(void);
 int get_temple_power(const struct city *pcity);
 int get_cathedral_power(const struct city *pcity);
 int get_colosseum_power(const struct city *pcity);
Index: common/game.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/game.c,v
retrieving revision 1.183
diff -u -r1.183 game.c
--- common/game.c       21 Jul 2004 16:34:33 -0000      1.183
+++ common/game.c       3 Sep 2004 22:44:25 -0000
@@ -70,13 +70,16 @@
 **************************************************************************/
 int total_player_citizens(struct player *pplayer)
 {
-  return (pplayer->score.happy
-         +pplayer->score.content
-         +pplayer->score.unhappy
-         +pplayer->score.angry
-         +pplayer->score.scientists
-         +pplayer->score.elvis
-         +pplayer->score.taxmen);
+  int count = (pplayer->score.happy
+              +pplayer->score.content
+              +pplayer->score.unhappy
+              +pplayer->score.angry), sp;
+
+  for (sp = 0; sp < SP_COUNT; sp++) {
+    count += pplayer->score.specialists[sp];
+  }
+
+  return count;
 }
 
 /**************************************************************************
Index: common/game.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/game.h,v
retrieving revision 1.149
diff -u -r1.149 game.h
--- common/game.h       3 Sep 2004 04:22:37 -0000       1.149
+++ common/game.h       3 Sep 2004 22:44:25 -0000
@@ -187,10 +187,15 @@
 
   /* values from game.ruleset */
   struct {
-    struct {
+    struct specialist_type {
       char name[MAX_LEN_NAME];
-      int min_size, bonus;
-    } specialists[SP_COUNT];
+      int min_size;
+      int bonus_sci, bonus_tax, bonus_lux;
+    } specialists[MAX_NUM_SPECIALISTS];
+#define DEFAULT_SPECIALIST game.rgame.default_specialist
+#define SP_COUNT game.rgame.num_specialist_types
+    int default_specialist, num_specialist_types;
+
     bool changable_tax;
     int forced_science; /* only relevant if !changable_tax */
     int forced_luxury;
Index: common/packets.def
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/packets.def,v
retrieving revision 1.45
diff -u -r1.45 packets.def
--- common/packets.def  3 Sep 2004 03:56:58 -0000       1.45
+++ common/packets.def  3 Sep 2004 22:44:25 -0000
@@ -165,7 +165,7 @@
 type EVENT             = sint16(enum event_type)
 type TERRAIN           = sint16(Terrain_type_id)
 type SPECIAL           = uint16(enum tile_special_type)
-type SPECIALIST                = uint8(enum specialist_type)
+type SPECIALIST                = uint8(int)
 type DIPLOMAT_ACTION   = uint8(enum diplomat_actions)
 type CMDLEVEL          = uint8(enum cmdlevel_id)
 type PLACE_TYPE                = uint8(enum spaceship_place_type)
@@ -391,7 +391,7 @@
 
   UINT8 ppl_happy[5], ppl_content[5], ppl_unhappy[5], ppl_angry[5];
 
-  UINT8 specialists[SP_COUNT];
+  UINT8 specialists[MAX_NUM_SPECIALISTS];
 
   UINT16 food_prod, shield_prod, trade_prod;
   SINT16 food_surplus, shield_surplus, tile_trade;
@@ -967,9 +967,12 @@
 end
 
 PACKET_RULESET_GAME=97;sc,lsend
-  STRING specialist_name[SP_COUNT][MAX_LEN_NAME];
-  UINT8 specialist_min_size[SP_COUNT];
-  UINT8 specialist_bonus[SP_COUNT];
+  UINT8 default_specialist, num_specialist_types;
+  STRING 
specialist_name[MAX_NUM_SPECIALISTS:num_specialist_types][MAX_LEN_NAME];
+  UINT8 specialist_min_size[MAX_NUM_SPECIALISTS:num_specialist_types];
+  UINT8 specialist_bonus_sci[MAX_NUM_SPECIALISTS:num_specialist_types];
+  UINT8 specialist_bonus_tax[MAX_NUM_SPECIALISTS:num_specialist_types];
+  UINT8 specialist_bonus_lux[MAX_NUM_SPECIALISTS:num_specialist_types];
   BOOL changable_tax;
   UINT8 forced_science;
   UINT8 forced_luxury;
Index: common/player.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/player.c,v
retrieving revision 1.150
diff -u -r1.150 player.c
--- common/player.c     29 Aug 2004 19:03:31 -0000      1.150
+++ common/player.c     3 Sep 2004 22:44:26 -0000
@@ -419,8 +419,10 @@
 
     get_tax_income(pplayer, trade, &sci, &lux, &tax);
     income += tax;
-    income += pcity->specialists[SP_TAXMAN]
-            * game.rgame.specialists[SP_TAXMAN].bonus;
+    specialist_type_iterate(sp) {
+      income
+       += pcity->specialists[sp] * game.rgame.specialists[sp].bonus_tax;
+    } specialist_type_iterate_end;
     income += get_city_tithes_bonus(pcity);
 
     /* Improvement upkeep. */
Index: common/player.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/player.h,v
retrieving revision 1.122
diff -u -r1.122 player.h
--- common/player.h     3 Sep 2004 04:22:37 -0000       1.122
+++ common/player.h     3 Sep 2004 22:44:26 -0000
@@ -103,9 +103,7 @@
   int content;
   int unhappy;
   int angry;
-  int taxmen;
-  int scientists;
-  int elvis;
+  int specialists[MAX_NUM_SPECIALISTS];
   int wonders;
   int techs;
   int techout;
Index: common/aicore/cm.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/aicore/cm.c,v
retrieving revision 1.39
diff -u -r1.39 cm.c
--- common/aicore/cm.c  24 Jul 2004 03:43:35 -0000      1.39
+++ common/aicore/cm.c  3 Sep 2004 22:44:26 -0000
@@ -149,27 +149,8 @@
 #define SHOW_CM_STORAGE_USED                            FALSE
 #define DISABLE_CACHE3                                  FALSE
 
-#define NUM_SPECIALISTS_ROLES                          3
 #define MAX_FIELDS_USED                                        (CITY_TILES - 1)
 
-/*
- * Maps (trade, taxmen) -> (gold_production, gold_surplus)
- * Maps (trade, entertainers) -> (luxury_production, luxury_surplus)
- * Maps (trade, scientists) -> (science_production, science_surplus)
- * Maps (luxury, workers) -> (city_is_in_disorder, city_is_happy)
- */
-static struct {
-  int allocated_trade, allocated_size, allocated_luxury;
-
-  struct secondary_stat {
-    bool is_valid;
-    short int surplus;
-  } *secondary_stats;
-  struct city_status {
-    bool is_valid, disorder, happy;
-  } *city_status;
-} cache2;
-
 /* 
  * Contains all combinations. Caches all the data about a city across
  * multiple cm_query_result calls about the same city.
@@ -187,7 +168,9 @@
 
   struct results_set {
     struct combination {
-      int max_scientists, max_taxmen, worker;
+      int worker;
+      int max_specialists[MAX_NUM_SPECIALISTS];
+      int cache1_size;
       int production2[NUM_PRIMARY_STATS];
       enum city_tile_type worker_positions[CITY_MAP_SIZE][CITY_MAP_SIZE];
 
@@ -213,7 +196,7 @@
   int queries;
   struct cache_stats {
     int hits, misses;
-  } cache1, cache2, cache3;
+  } cache1, cache3;
 } stats;
 
 /*
@@ -236,6 +219,9 @@
   } city_map_checked_iterate_end;    \
 }
 
+/* The specialist with best luxury production. */
+static int best_entertainer;
+
 /****************************************************************************
  * implementation of utility functions (these are relatively independent
  * of the algorithms used)
@@ -382,11 +368,13 @@
 {
   freelog(LOG_NORMAL, "print_city(city='%s'(id=%d))",
          pcity->name, pcity->id);
+#if 0
   freelog(LOG_NORMAL,
          "  size=%d, entertainers=%d, scientists=%d, taxmen=%d",
          pcity->size, pcity->specialists[SP_ELVIS],
          pcity->specialists[SP_SCIENTIST],
          pcity->specialists[SP_TAXMAN]);
+#endif
   freelog(LOG_NORMAL, "  workers at:");
   my_city_map_iterate(pcity, x, y) {
     if (pcity->city_map[x][y] == C_TILE_WORKER) {
@@ -413,7 +401,7 @@
 void cm_print_result(const struct city *pcity,
                     const struct cm_result *const result)
 {
-  int y, i, worker = cm_count_worker(pcity, result);
+  int y, i;
 
   freelog(LOG_NORMAL, "print_result(result=%p)", result);
   freelog(LOG_NORMAL,
@@ -448,11 +436,13 @@
     freelog(LOG_NORMAL, "print_result: %s", line);
   }
 
+#if 0
   freelog(LOG_NORMAL,
          "print_result:  people: W/E/S/T %d/%d/%d/%d",
-         worker, result->specialists[SP_ELVIS],
+         count_worker(pcity, result), result->specialists[SP_ELVIS],
          result->specialists[SP_SCIENTIST],
          result->specialists[SP_TAXMAN]);
+#endif
 
   for (i = 0; i < NUM_STATS; i++) {
     freelog(LOG_NORMAL,
@@ -510,105 +500,8 @@
 }
 
 /****************************************************************************
- Wraps the array access to cache2.secondary_stats.
-*****************************************************************************/
-static struct secondary_stat *get_secondary_stat(int trade, int specialists,
-                                                enum specialist_type
-                                                specialist_type)
-{
-  freelog(LOG_DEBUG, "second: trade=%d spec=%d type=%d", trade, specialists,
-         specialist_type);
-
-  assert(trade >= 0 && trade < cache2.allocated_trade);
-  assert(specialists >= 0 && specialists < cache2.allocated_size);
-
-  return &cache2.secondary_stats[NUM_SPECIALISTS_ROLES *
-                                (cache2.allocated_size * trade +
-                                 specialists) + specialist_type];
-}
-
-/****************************************************************************
- Wraps the array access to cache2.city_status.
-*****************************************************************************/
-static struct city_status *get_city_status(int luxury, int workers)
-{
-  freelog(LOG_DEBUG, "status: lux=%d worker=%d", luxury, workers);
-
-  assert(luxury >=0 && luxury < cache2.allocated_luxury);
-  assert(workers >= 0 && workers < cache2.allocated_size);
-
-  return &cache2.city_status[cache2.allocated_size * luxury + workers];
-}
-
-/****************************************************************************
- Update the cache2 according to the filled out result. If the info is
- already in the cache check that the two match.
+...
 *****************************************************************************/
-static void update_cache2(struct city *pcity,
-                         const struct cm_result *const result)
-{
-  struct secondary_stat *p;
-  struct city_status *q;
-
-  /*
-   * Science is set to 0 if the city is unhappy/in disorder. See
-   * unhappy_city_check.
-   */
-  if (!result->disorder) {
-    p = get_secondary_stat(result->surplus[TRADE],
-                          result->specialists[SP_SCIENTIST],
-                          SP_SCIENTIST);
-    if (!p->is_valid) {
-      p->surplus = result->surplus[SCIENCE];
-      p->is_valid = TRUE;
-    } else {
-      assert(p->surplus == result->surplus[SCIENCE]);
-    }
-  }
-
-  /*
-   * Gold is set to 0 if the city is unhappy/in disorder. See
-   * unhappy_city_check.
-   */
-  if (!result->disorder) {
-    p = get_secondary_stat(result->surplus[TRADE],
-                          result->specialists[SP_TAXMAN],
-                          SP_TAXMAN);
-    if (!p->is_valid && !result->disorder) {
-      p->surplus = result->surplus[GOLD];
-      p->is_valid = TRUE;
-    } else {
-      assert(p->surplus == result->surplus[GOLD]);
-    }
-  }
-
-  p = get_secondary_stat(result->surplus[TRADE],
-                        result->specialists[SP_ELVIS],
-                        SP_ELVIS);
-  if (!p->is_valid) {
-    p->surplus = result->surplus[LUXURY];
-    p->is_valid = TRUE;
-  } else {
-    if (!result->disorder) {
-      assert(p->surplus == result->surplus[LUXURY]);
-    }
-  }
-
-  q = get_city_status(result->surplus[LUXURY],
-                     cm_count_worker(pcity, result));
-  if (!q->is_valid) {
-    q->disorder = result->disorder;
-    q->happy = result->happy;
-    q->is_valid = TRUE;
-  } else {
-    assert(q->disorder == result->disorder && q->happy == result->happy);
-  }
-}
-
-/****************************************************************************
-  Clear the results of the cache.  Does NOT free all storage; arrays stay
-  allocated so we don't thrash memory.
-****************************************************************************/
 static void clear_cache(void)
 {
   int i, j;
@@ -661,9 +554,9 @@
     }
   } my_city_map_iterate_end;
 
-  pcity->specialists[SP_ELVIS] = result->specialists[SP_ELVIS];
-  pcity->specialists[SP_SCIENTIST] = result->specialists[SP_SCIENTIST];
-  pcity->specialists[SP_TAXMAN] = result->specialists[SP_TAXMAN];
+  specialist_type_iterate(sp) {
+    pcity->specialists[sp] = result->specialists[sp];
+  } specialist_type_iterate_end;
 
   /* Do a local recalculation of the city */
   generic_city_refresh(pcity, FALSE, NULL);
@@ -673,6 +566,7 @@
   /* Restore */
   memcpy(pcity, &backup, sizeof(struct city));
 
+#if 0
   freelog(LOG_DEBUG, "xyz: w=%d e=%d s=%d t=%d trade=%d "
          "sci=%d lux=%d tax=%d dis=%s happy=%s",
          cm_count_worker(pcity, result), result->specialists[SP_ELVIS],
@@ -682,7 +576,7 @@
          result->surplus[LUXURY],
          result->surplus[GOLD],
          result->disorder ? "yes" : "no", result->happy ? "yes" : "no");
-  update_cache2(pcity, result);
+#endif
 }
 
 /****************************************************************************
@@ -793,7 +687,6 @@
 
 #if SHOW_CACHE_STATS
   report_one_cache_stat(&stats.cache1, "CACHE1");
-  report_one_cache_stat(&stats.cache2, "CACHE2");
   report_one_cache_stat(&stats.cache3, "CACHE3");
 #endif
 
@@ -806,38 +699,75 @@
                            algorithmic functions
 *****************************************************************************/
 
+static int combination_spec_index(struct combination *combination,
+                                 int *specialists)
+{
+  int sp, index = 0;
+
+  for (sp = 0; sp < SP_COUNT; sp++) {
+    if (sp == best_entertainer) {
+      continue;
+    }
+
+    index *= combination->max_specialists[sp] + 1;
+    index += specialists[sp];
+  }
+
+  return index;
+}
+
 /****************************************************************************
  Frontend cache for real_fill_out_result. This method tries to avoid
  calling real_fill_out_result by all means.
 *****************************************************************************/
 static void fill_out_result(struct city *pcity, struct cm_result *result,
                            struct combination *base_combination,
-                           int scientists, int taxmen)
+                           int *specialists)
 {
   struct cm_result *slot;
-  bool got_all;
+  int sp;
+  bool all_zero = TRUE;
 
   /*
    * First try to get a filled out result from cache1 or from the
    * all_entertainer result.
    */
-  if (scientists == 0 && taxmen == 0) {
+  for (sp = 0; sp < SP_COUNT; sp++) {
+    if (sp != best_entertainer
+       && specialists[sp] != 0) {
+      /* I used to call combination_spec_index up above.  Then if all
+       * the specialists were zero the index would be zero.  Duh.  But
+       * in some cases (with all 0's) the max_specialists fields are not
+       * filled out.  This
+       * causes valgrind to give a spurious error.  Of course 0 times and
+       * unknown value is 0, but valgrind thinks it is unknown.
+       *
+       * So, to make debugging easier I just do the check manually. */
+      all_zero = FALSE;
+      break;
+    }
+  }
+  if (all_zero) {
     slot = &base_combination->all_entertainer;
   } else {
-    assert(scientists <= base_combination->max_scientists);
-    assert(taxmen <= base_combination->max_taxmen);
+    int index = combination_spec_index(base_combination, specialists);
+
+    for (sp = 0; sp < SP_COUNT; sp++) {
+      if (sp == best_entertainer) continue;
+      assert(specialists[sp] <= base_combination->max_specialists[sp]);
+    }
     assert(base_combination->cache1 != NULL);
     assert(base_combination->all_entertainer.found_a_valid);
 
-    slot = &base_combination->cache1[scientists *
-                                    (base_combination->max_taxmen + 1) +
-                                    taxmen];
+    slot = &base_combination->cache1[index];
   }
 
+#if 0
   freelog(LOG_DEBUG,
          "fill_out_result(base_comb=%p (w=%d), scientists=%d, taxmen=%d) %s",
          base_combination, base_combination->worker, scientists,
          taxmen, slot->found_a_valid ? "CACHED" : "unknown");
+#endif
 
   if (slot->found_a_valid) {
     /* Cache1 contains the result */
@@ -852,84 +782,21 @@
        (base_combination->worker_positions[x][y] == C_TILE_WORKER);
   } my_city_map_iterate_end;
 
-  result->specialists[SP_SCIENTIST] = scientists;
-  result->specialists[SP_TAXMAN] = taxmen;
-  result->specialists[SP_ELVIS] =
-      pcity->size - (base_combination->worker + scientists + taxmen);
+  result->specialists[best_entertainer]
+    = pcity->size - base_combination->worker;
+  specialist_type_iterate(sp) {
+    if (sp != best_entertainer) {
+      result->specialists[sp] = specialists[sp];
+      result->specialists[best_entertainer] -= specialists[sp];
+    }
+  } specialist_type_iterate_end;
 
+#if 0
   freelog(LOG_DEBUG,
          "fill_out_result(city='%s'(%d), entrt.s=%d, scien.s=%d, taxmen=%d)",
          pcity->name, pcity->id, result->specialists[SP_ELVIS],
          result->specialists[SP_SCIENTIST], result->specialists[SP_TAXMAN]);
-
-  /* try to fill result from cache2 */
-  if (!base_combination->all_entertainer.found_a_valid) {
-    got_all = FALSE;
-  } else {
-    struct secondary_stat *p;
-    struct city_status *q;
-    int i;
-
-    got_all = TRUE;
-
-    /*
-     * fill out the primary stats that are known from the
-     * all_entertainer result
-     */
-    for (i = 0; i < NUM_PRIMARY_STATS; i++) {
-      result->surplus[i] = base_combination->all_entertainer.surplus[i];
-    }
-
-    p = get_secondary_stat(result->surplus[TRADE],
-                          result->specialists[SP_SCIENTIST],
-                          SP_SCIENTIST);
-    if (!p->is_valid) {
-      got_all = FALSE;
-    } else {
-      result->surplus[SCIENCE] = p->surplus;
-    }
-
-    p = get_secondary_stat(result->surplus[TRADE],
-                          result->specialists[SP_TAXMAN],
-                          SP_TAXMAN);
-    if (!p->is_valid) {
-      got_all = FALSE;
-    } else {
-      result->surplus[GOLD] = p->surplus;
-    }
-
-    p = get_secondary_stat(result->surplus[TRADE],
-                          result->specialists[SP_ELVIS],
-                          SP_ELVIS);
-    if (!p->is_valid) {
-      got_all = FALSE;
-    } else {
-      result->surplus[LUXURY] = p->surplus;
-    }
-
-    q = get_city_status(result->surplus[LUXURY],
-                       base_combination->worker);
-    if (!q->is_valid) {
-      got_all = FALSE;
-    } else {
-      result->disorder = q->disorder;
-      result->happy = q->happy;
-    }
-  }
-
-  if (got_all) {
-    /*
-     * All secondary stats and the city status have been filled from
-     * cache2.
-     */
-
-    stats.cache2.hits++;
-    memcpy(slot, result, sizeof(struct cm_result));
-    slot->found_a_valid = TRUE;
-    return;
-  }
-
-  stats.cache2.misses++;
+#endif
 
   /*
    * Result can't be constructed from caches. Do the slow
@@ -1081,86 +948,6 @@
 }
 
 /****************************************************************************
- Expand the secondary_stats and city_status fields of cache2 if this
- is necessary. For this the function tries to estimate the upper limit
- of trade and luxury. It will also invalidate cache2.
-*****************************************************************************/
-static void ensure_invalid_cache2(struct city *pcity, int total_tile_trade)
-{
-  bool change_size = FALSE;
-  int backup,i, luxury, total_trade = total_tile_trade;
-
-  /* Hack since trade_between_cities accesses pcity->tile_trade */
-  backup = pcity->tile_trade;
-  pcity->tile_trade = total_tile_trade;
-  for (i = 0; i < NUM_TRADEROUTES; i++) {
-    struct city *pc2 = find_city_by_id(pcity->trade[i]);
-
-    total_trade += trade_between_cities(pcity, pc2);
-  }
-  pcity->tile_trade = backup;
-
-  /*
-   * Estimate an upper limit for the luxury. We assume that the player
-   * has set the luxury rate to 100%. There are two extremal cases: all
-   * citizen are entertainers (yielding a luxury of "(pcity->size * 2
-   * * get_city_luxury_bonus(pcity))/100" = A) or all citizen are
-   * working on tiles and the resulting trade is converted to luxury
-   * (yielding a luxury of "(total_trade * get_city_luxury_bonus(pcity))
-   * / 100" = B) . We can't use MAX(A, B) since there may be cases in
-   * between them which are better than these two exremal cases. So we
-   * use A+B as upper limit.
-   */
-  luxury
-    = ((pcity->size * 2 + total_trade) * get_city_luxury_bonus(pcity)) / 100;
-
-  /* +1 because we want to index from 0 to pcity->size inclusive */
-  if (pcity->size + 1 > cache2.allocated_size) {
-    cache2.allocated_size = pcity->size + 1;
-    change_size = TRUE;
-  }
-
-  if (total_trade + 1 > cache2.allocated_trade) {
-    cache2.allocated_trade = total_trade + 1;
-    change_size = TRUE;
-  }
-
-  if (luxury + 1 > cache2.allocated_luxury) {
-    cache2.allocated_luxury = luxury + 1;
-    change_size = TRUE;
-  }
-
-  if (change_size) {
-    freelog(LOG_DEBUG,
-           "CM: expanding cache2 to size=%d, trade=%d, luxury=%d",
-           cache2.allocated_size, cache2.allocated_trade,
-           cache2.allocated_luxury);
-    if (cache2.secondary_stats) {
-      free(cache2.secondary_stats);
-      cache2.secondary_stats = NULL;
-    }
-    cache2.secondary_stats =
-       fc_malloc(cache2.allocated_trade * cache2.allocated_size *
-                 NUM_SPECIALISTS_ROLES * sizeof(struct secondary_stat));
-
-    if (cache2.city_status) {
-      free(cache2.city_status);
-      cache2.city_status = NULL;
-    }
-    cache2.city_status =
-       fc_malloc(cache2.allocated_luxury * cache2.allocated_size *
-                 sizeof(struct city_status));
-  }
-
-  /* Make cache2 invalid */
-  memset(cache2.secondary_stats, 0,
-        cache2.allocated_trade * cache2.allocated_size *
-        NUM_SPECIALISTS_ROLES * sizeof(struct secondary_stat));
-  memset(cache2.city_status, 0,
-        cache2.allocated_luxury * cache2.allocated_size *
-        sizeof(struct city_status));
-}
-/****************************************************************************
  Setup. Adds the root combination (the combination which doesn't use
  any worker but the production of the city center). Incrementaly calls
  expand_cache3.
@@ -1237,8 +1024,6 @@
   for (i = 1; i <= MIN(cache3.fields_available_total, pcity->size); i++) {
     expand_cache3(pcity, i, &tile_stats);
   }
-
-  ensure_invalid_cache2(pcity, total_tile_trade);
 }
 
 /****************************************************************************
@@ -1252,28 +1037,35 @@
                                             int *best_minor_fitness)
 {
   int worker = base_combination->worker;
-  int specialists = pcity->size - worker;
-  int scientists, taxmen;
+  int num_specialists = pcity->size - worker;
+  int index;
 
   if (!base_combination->cache1) {
 
     /* setup cache1 */
 
-    int i, items;
+    int i, items = 1, sp;
 
-    if (city_can_use_specialist(pcity, SP_SCIENTIST)) {
-      base_combination->max_scientists = specialists;
-    } else {
-      base_combination->max_scientists = 0;
-    }
+    for (sp = 0; sp < SP_COUNT; sp++) {
+      int max;
 
-    if (city_can_use_specialist(pcity, SP_TAXMAN)) {
-      base_combination->max_taxmen = specialists;
-    } else {
-      base_combination->max_taxmen = 0;
+      if (sp == best_entertainer) {
+       base_combination->max_specialists[sp] = num_specialists;
+       assert(city_can_use_specialist(pcity, sp));
+       continue;
+      }
+
+      if (city_can_use_specialist(pcity, sp)) {
+       max = num_specialists;
+      } else {
+       max = 0;
+      }
+
+      base_combination->max_specialists[sp] = max;
+      items *= (max + 1);
     }
-    items = (base_combination->max_scientists + 1) *
-       (base_combination->max_taxmen + 1);
+
+    base_combination->cache1_size = items;
     base_combination->cache1 =
        fc_malloc(sizeof(struct cm_result) * items);
     for (i = 0; i < items; i++) {
@@ -1283,54 +1075,75 @@
 
   best_result->found_a_valid = FALSE;
 
-  for (scientists = 0;
-       scientists <= base_combination->max_scientists; scientists++) {
-    for (taxmen = 0;
-        taxmen <= base_combination->max_taxmen - scientists; taxmen++) {
-      int major_fitness, minor_fitness;
-      struct cm_result result;
+  for (index = 0; index < base_combination->cache1_size; index++) {
+    int specialists[SP_COUNT], sp, count = 0, myindex = index;
+    int major_fitness, minor_fitness;
+    struct cm_result result;
+
+    for (sp = SP_COUNT - 1; sp >= 0; sp--) {
+      /* This loop is the inverse of combination_spec_index(). */
+      int max = base_combination->max_specialists[sp] + 1;
 
-      freelog(FIND_BEST_SPECIALIST_ARRANGEMENT_LOG_LEVEL,
-             "  optimize_people: using (W/E/S/T) %d/%d/%d/%d",
-             worker, pcity->size - (worker + scientists + taxmen),
-             scientists, taxmen);
+      if (sp == best_entertainer) {
+       continue;
+      }
+      if (max == 0) {
+       specialists[sp] = 0;
+       continue;
+      }
 
-      fill_out_result(pcity, &result, base_combination, scientists,
-                     taxmen);
+      specialists[sp] = myindex % max;
+      myindex /= max;
 
-      freelog(FIND_BEST_SPECIALIST_ARRANGEMENT_LOG_LEVEL,
-             "  optimize_people: got extra=(tax=%d, luxury=%d, "
-             "science=%d)",
-             result.surplus[GOLD] - parameter->minimal_surplus[GOLD],
-             result.surplus[LUXURY] -
-             parameter->minimal_surplus[LUXURY],
-             result.surplus[SCIENCE] -
-             parameter->minimal_surplus[SCIENCE]);
+      count += specialists[sp];
+    }
+    if (count > num_specialists) {
+      /* FIXME: cache1 is much bigger than it needs to be! */
+      continue;
+    }
+    specialists[best_entertainer] = num_specialists - count;
 
-      if (!is_valid_result(parameter, &result)) {
-       freelog(FIND_BEST_SPECIALIST_ARRANGEMENT_LOG_LEVEL,
-               "  optimize_people: doesn't have enough surplus or disorder");
-       continue;
-      }
+#if 0
+    freelog(FIND_BEST_SPECIALIST_ARRANGEMENT_LOG_LEVEL,
+           "  optimize_people: using (W/E/S/T) %d/%d/%d/%d",
+           worker, pcity->size - (worker + scientists + taxmen),
+           scientists, taxmen);
+#endif
 
-      calc_fitness(pcity, parameter, &result, &major_fitness,
-                  &minor_fitness);
+    fill_out_result(pcity, &result, base_combination, specialists);
 
+    freelog(FIND_BEST_SPECIALIST_ARRANGEMENT_LOG_LEVEL,
+           "  optimize_people: got extra=(tax=%d, luxury=%d, "
+           "science=%d)",
+           result.surplus[GOLD] - parameter->minimal_surplus[GOLD],
+           result.surplus[LUXURY] -
+           parameter->minimal_surplus[LUXURY],
+           result.surplus[SCIENCE] -
+           parameter->minimal_surplus[SCIENCE]);
+
+    if (!is_valid_result(parameter, &result)) {
       freelog(FIND_BEST_SPECIALIST_ARRANGEMENT_LOG_LEVEL,
-             "  optimize_people: fitness=(%d,%d)", major_fitness,
-             minor_fitness);
+             "  optimize_people: doesn't have enough surplus or disorder");
+      continue;
+    }
 
-      result.found_a_valid = TRUE;
-      if (!best_result->found_a_valid
-         || ((major_fitness > *best_major_fitness)
-             || (major_fitness == *best_major_fitness
-                 && minor_fitness > *best_minor_fitness))) {
-       memcpy(best_result, &result, sizeof(struct cm_result));
-       *best_major_fitness = major_fitness;
-       *best_minor_fitness = minor_fitness;
-      }
-    }                          /* for taxmen */
-  }                            /* for scientists */
+    calc_fitness(pcity, parameter, &result, &major_fitness,
+                &minor_fitness);
+
+    freelog(FIND_BEST_SPECIALIST_ARRANGEMENT_LOG_LEVEL,
+           "  optimize_people: fitness=(%d,%d)", major_fitness,
+           minor_fitness);
+
+    result.found_a_valid = TRUE;
+    if (!best_result->found_a_valid
+       || ((major_fitness > *best_major_fitness)
+           || (major_fitness == *best_major_fitness
+               && minor_fitness > *best_minor_fitness))) {
+      memcpy(best_result, &result, sizeof(struct cm_result));
+      *best_major_fitness = major_fitness;
+      *best_minor_fitness = minor_fitness;
+    }
+  }
 }
 
 /****************************************************************************
@@ -1362,11 +1175,18 @@
       struct combination *current = results->combinations[i];
       int major_fitness, minor_fitness;
       struct cm_result result;
+      int specialists[SP_COUNT], sp;
 
       freelog(OPTIMIZE_FINAL_LOG_LEVEL2, "  trying combination %d", i);
 
       /* this will set the all_entertainer result */
-      fill_out_result(pcity, &result, current, 0, 0);
+      //      memset(specialists, 0, sizeof(specialists));
+      for (sp = 0; sp < SP_COUNT; sp++) {
+       specialists[sp] = 0;
+      }
+      /* FIXME: although specialists[i] == 0 max_specialists[i] has not
+       * been filled out.  Is this a bug?  At the least it's risky... */
+      fill_out_result(pcity, &result, current, specialists);
 
 
       /*
@@ -1465,16 +1285,6 @@
   free_timer(stats.wall_timer);
   stats.wall_timer = NULL;
 
-  free(cache2.secondary_stats);
-  cache2.secondary_stats = NULL;
-
-  free(cache2.city_status);
-  cache2.city_status = NULL;
-
-  cache2.allocated_size = 0;
-  cache2.allocated_trade = 0;
-  cache2.allocated_luxury = 0;
-
   clear_cache();
   for (i = 0; i <= MAX_FIELDS_USED; i++) {
     free(cache3.results[i].combinations);
Index: common/aicore/cm.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/aicore/cm.h,v
retrieving revision 1.10
diff -u -r1.10 cm.h
--- common/aicore/cm.h  22 Jul 2004 15:20:47 -0000      1.10
+++ common/aicore/cm.h  3 Sep 2004 22:44:26 -0000
@@ -49,7 +49,7 @@
   int surplus[NUM_STATS];
 
   bool worker_positions_used[CITY_MAP_SIZE][CITY_MAP_SIZE];
-  int specialists[SP_COUNT];
+  int specialists[MAX_NUM_SPECIALISTS];
 };
 
 void cm_init(void);
Index: data/default/cities.ruleset
===================================================================
RCS file: /home/freeciv/CVS/freeciv/data/default/cities.ruleset,v
retrieving revision 1.11
diff -u -r1.11 cities.ruleset
--- data/default/cities.ruleset 9 Jun 2004 04:39:13 -0000       1.11
+++ data/default/cities.ruleset 3 Sep 2004 22:44:26 -0000
@@ -21,14 +21,13 @@
 ; the city is of a certain size.
 [specialist]
 
-; Changing the order or names of specialists will break things
 types = "elvis", "scientist", "taxman"
 elvis_min_size = 0
-elvis_base_bonus = 2
+elvis_bonus_lux = 2
 scientist_min_size = 5
-scientist_base_bonus = 3
+scientist_bonus_sci = 3
 taxman_min_size = 5
-taxman_base_bonus = 3
+taxman_bonus_tax = 3
 
 changable_tax = 1
 ;forced_science = 0
Index: server/cityhand.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/cityhand.c,v
retrieving revision 1.134
diff -u -r1.134 cityhand.c
--- server/cityhand.c   28 Aug 2004 19:15:39 -0000      1.134
+++ server/cityhand.c   3 Sep 2004 22:44:27 -0000
@@ -63,8 +63,7 @@
 ...
 **************************************************************************/
 void handle_city_change_specialist(struct player *pplayer, int city_id,
-                                  enum specialist_type from,
-                                  enum specialist_type to)
+                                  int from, int to)
 {
   struct city *pcity = player_find_city_by_id(pplayer, city_id);
 
@@ -106,7 +105,7 @@
   }
   if (is_worker_here(pcity, worker_x, worker_y)) {
     server_remove_worker_city(pcity, worker_x, worker_y);
-    pcity->specialists[SP_ELVIS]++;
+    pcity->specialists[DEFAULT_SPECIALIST]++;
     city_refresh(pcity);
     sync_cities();
   } else {
Index: server/citytools.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/citytools.c,v
retrieving revision 1.271
diff -u -r1.271 citytools.c
--- server/citytools.c  31 Aug 2004 04:40:50 -0000      1.271
+++ server/citytools.c  3 Sep 2004 22:44:27 -0000
@@ -1525,9 +1525,9 @@
     packet->ppl_unhappy[i]=pcity->ppl_unhappy[i];
     packet->ppl_angry[i]=pcity->ppl_angry[i];
   }
-  packet->specialists[SP_ELVIS] = pcity->specialists[SP_ELVIS];
-  packet->specialists[SP_SCIENTIST] = pcity->specialists[SP_SCIENTIST];
-  packet->specialists[SP_TAXMAN] = pcity->specialists[SP_TAXMAN];
+  for (i = 0; i < SP_COUNT; i++) {
+    packet->specialists[i] = pcity->specialists[i];
+  }
   for (i = 0; i < NUM_TRADEROUTES; i++) {
     packet->trade[i]=pcity->trade[i];
     packet->trade_value[i]=pcity->trade_value[i];
@@ -1989,7 +1989,7 @@
   case C_TILE_WORKER:
     if (!is_available) {
       server_set_tile_city(pcity, city_x, city_y, C_TILE_UNAVAILABLE);
-      pcity->specialists[SP_ELVIS]++; /* keep city sanity */
+      pcity->specialists[DEFAULT_SPECIALIST]++; /* keep city sanity */
       auto_arrange_workers(pcity); /* will place the displaced */
       city_refresh(pcity);
       send_city_info(NULL, pcity);
Index: server/cityturn.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/cityturn.c,v
retrieving revision 1.258
diff -u -r1.258 cityturn.c
--- server/cityturn.c   28 Aug 2004 19:15:39 -0000      1.258
+++ server/cityturn.c   3 Sep 2004 22:44:27 -0000
@@ -374,6 +374,8 @@
 **************************************************************************/
 bool city_reduce_size(struct city *pcity, int pop_loss)
 {
+  int i;
+
   if (pop_loss == 0) {
     return TRUE;
   }
@@ -391,16 +393,15 @@
 
   /* First try to kill off the specialists */
   while (pop_loss > 0 && city_specialists(pcity) > 0) {
-    if (pcity->specialists[SP_TAXMAN] > 0) {
-      pcity->specialists[SP_TAXMAN]--;
-    } else if (pcity->specialists[SP_SCIENTIST] > 0) {
-      pcity->specialists[SP_SCIENTIST]--;
-    } else {
-      assert(pcity->specialists[SP_ELVIS] > 0);
-      pcity->specialists[SP_ELVIS]--; 
+    for (i = SP_COUNT - 1; i >= 0; i--) {
+      if (pcity->specialists[i] > 0) {
+       pcity->specialists[i]--;
+       pop_loss--;
+       break;
+      }
     }
-    pop_loss--;
   }
+  assert(pop_loss == 0 || city_specialists(pcity) == 0);
 
   /* we consumed all the pop_loss in specialists */
   if (pop_loss == 0) {
@@ -434,6 +435,7 @@
   bool has_granary = city_got_effect(pcity, B_GRANARY);
   bool rapture_grow = city_rapture_grow(pcity); /* check before size increase! 
*/
   int new_food;
+  int best_sci = best_science_specialist(), best_tax = best_tax_specialist();
 
   if (!city_got_building(pcity, B_AQUEDUCT)
       && pcity->size>=game.aqueduct_size) {/* need aqueduct */
@@ -494,18 +496,17 @@
       have_square = TRUE;
     }
   } city_map_iterate_end;
-  if (((pcity->food_surplus >= 2) || !have_square)  &&  pcity->size >= 5  &&
-      (is_city_option_set(pcity, CITYO_NEW_EINSTEIN) || 
-       is_city_option_set(pcity, CITYO_NEW_TAXMAN))) {
-
-    if (is_city_option_set(pcity, CITYO_NEW_EINSTEIN)) {
-      pcity->specialists[SP_SCIENTIST]++;
-    } else { /* now pcity->city_options & (1<<CITYO_NEW_TAXMAN) is true */
-      pcity->specialists[SP_TAXMAN]++;
-    }
 
+  if ((pcity->food_surplus >= 2 || !have_square)
+      && is_city_option_set(pcity, CITYO_NEW_EINSTEIN)
+      && pcity->size >= game.rgame.specialists[best_sci].min_size) {
+    pcity->specialists[best_sci]++;
+  } else if ((pcity->food_surplus >= 2 || !have_square)
+            && is_city_option_set(pcity, CITYO_NEW_TAXMAN)
+            && pcity->size >= game.rgame.specialists[best_tax].min_size) {
+    pcity->specialists[best_tax]++;
   } else {
-    pcity->specialists[SP_TAXMAN]++; /* or else city is !sane */
+    pcity->specialists[DEFAULT_SPECIALIST]++; /* or else city is !sane */
     auto_arrange_workers(pcity);
   }
 
Index: server/report.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/report.c,v
retrieving revision 1.53
diff -u -r1.53 report.c
--- server/report.c     18 Aug 2004 19:37:26 -0000      1.53
+++ server/report.c     3 Sep 2004 22:44:27 -0000
@@ -492,19 +492,15 @@
   return pplayer->score.unhappy;
 }
 
-static int get_taxmen(struct player *pplayer)
+static int get_specialists(struct player *pplayer)
 {
-  return pplayer->score.taxmen;
-}
+  int i, count = 0;
 
-static int get_scientists(struct player *pplayer)
-{
-  return pplayer->score.scientists;
-}
+  for (i = 0; i < SP_COUNT; i++) {
+    count += pplayer->score.specialists[i];
+  }
 
-static int get_elvis(struct player *pplayer)
-{
-  return pplayer->score.elvis;
+  return count;
 }
 
 static int get_gov(struct player *pplayer)
@@ -898,9 +894,7 @@
     {"happypop",        get_happypop},
     {"contentpop",      get_contentpop},
     {"unhappypop",      get_unhappypop},
-    {"taxmen",          get_taxmen},
-    {"scientists",      get_scientists},
-    {"elvis",           get_elvis},
+    {"specialists",     get_specialists},
     {"gov",             get_gov},
     {"corruption",      get_corruption} /* new 1.11.5 tags end here */
   };
Index: server/ruleset.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/ruleset.c,v
retrieving revision 1.192
diff -u -r1.192 ruleset.c
--- server/ruleset.c    31 Aug 2004 04:40:50 -0000      1.192
+++ server/ruleset.c    3 Sep 2004 22:44:28 -0000
@@ -2539,11 +2539,6 @@
 
   /* Specialist options */
   specialist_names = secfile_lookup_str_vec(file, &nval, "specialist.types");
-  if (nval != SP_COUNT) {
-    freelog(LOG_FATAL, "There must be exactly %d types of specialist.",
-           SP_COUNT);
-    exit(EXIT_FAILURE);
-  }
 
   for (i = 0; i < nval; i++) {
     const char *name = specialist_names[i];
@@ -2551,10 +2546,20 @@
     sz_strlcpy(game.rgame.specialists[i].name, name);
     game.rgame.specialists[i].min_size
       = secfile_lookup_int(file, "specialist.%s_min_size", name);
-    game.rgame.specialists[i].bonus
-      = secfile_lookup_int(file, "specialist.%s_base_bonus", name);
-    
+    game.rgame.specialists[i].bonus_sci
+      = secfile_lookup_int_default(file, 0, "specialist.%s_bonus_sci", name);
+    game.rgame.specialists[i].bonus_tax
+      = secfile_lookup_int_default(file, 0, "specialist.%s_bonus_tax", name);
+    game.rgame.specialists[i].bonus_lux
+      = secfile_lookup_int_default(file, 0, "specialist.%s_bonus_lux", name);
+
+    if (game.rgame.specialists[i].min_size == 0
+       && game.rgame.default_specialist == -1) {
+      game.rgame.default_specialist = i;
+    }
   }
+  assert(game.rgame.default_specialist != -1);
+  game.rgame.num_specialist_types = nval;
 
   game.rgame.changable_tax = 
     secfile_lookup_bool_default(file, TRUE, "specialist.changable_tax");
@@ -2569,11 +2574,6 @@
     freelog(LOG_FATAL, "Forced taxes do not add up in ruleset!");
     exit(EXIT_FAILURE);
   }
-  if (game.rgame.specialists[SP_ELVIS].min_size > 0) {
-    freelog(LOG_FATAL, "Elvises must be available without a "
-           "city size restriction!");
-    exit(EXIT_FAILURE);
-  }
 
   /* City Parameters */
 
@@ -3147,10 +3147,14 @@
   int i;
   struct packet_ruleset_game misc_p;
 
+  misc_p.num_specialist_types = game.rgame.num_specialist_types;
+  misc_p.default_specialist = game.rgame.default_specialist;
   specialist_type_iterate(sp) {
     sz_strlcpy(misc_p.specialist_name[sp], game.rgame.specialists[sp].name);
     misc_p.specialist_min_size[sp] = game.rgame.specialists[sp].min_size;
-    misc_p.specialist_bonus[sp] = game.rgame.specialists[sp].bonus;
+    misc_p.specialist_bonus_sci[sp] = game.rgame.specialists[sp].bonus_sci;
+    misc_p.specialist_bonus_tax[sp] = game.rgame.specialists[sp].bonus_tax;
+    misc_p.specialist_bonus_lux[sp] = game.rgame.specialists[sp].bonus_lux;
   } specialist_type_iterate_end;
   misc_p.changable_tax = game.rgame.changable_tax;
   misc_p.forced_science = game.rgame.forced_science;
Index: server/sanitycheck.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/sanitycheck.c,v
retrieving revision 1.50
diff -u -r1.50 sanitycheck.c
--- server/sanitycheck.c        29 Aug 2004 19:38:07 -0000      1.50
+++ server/sanitycheck.c        3 Sep 2004 22:44:28 -0000
@@ -235,9 +235,12 @@
     }
   } city_map_iterate_end;
   if (workers + city_specialists(pcity) != pcity->size + 1) {
+    die("%s is illegal", pcity->name);
+#if 0
     die("%s is illegal (size%d w%d e%d t%d s%d) in %s line %d",
         pcity->name, pcity->size, workers, pcity->specialists[SP_ELVIS],
         pcity->specialists[SP_TAXMAN], pcity->specialists[SP_SCIENTIST], file, 
line);
+#endif
   }
 }
 
Index: server/savegame.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/savegame.c,v
retrieving revision 1.183
diff -u -r1.183 savegame.c
--- server/savegame.c   3 Sep 2004 01:21:03 -0000       1.183
+++ server/savegame.c   3 Sep 2004 22:44:29 -0000
@@ -1912,7 +1912,7 @@
            /* oops, inconsistent savegame; minimal fix: */
            freelog(LOG_VERBOSE, "Inconsistent worked for %s (%d,%d), "
                    "converting to elvis", pcity->name, x, y);
-           pcity->specialists[SP_ELVIS]++;
+           pcity->specialists[DEFAULT_SPECIALIST]++;
            set_worker_city(pcity, x, y, C_TILE_UNAVAILABLE);
          } else {
            set_worker_city(pcity, x, y, C_TILE_WORKER);
@@ -2781,7 +2781,7 @@
        int map_x, map_y;
        bool is_real;
 
-       pcity->specialists[SP_ELVIS]++;
+       pcity->specialists[DEFAULT_SPECIALIST]++;
        set_worker_city(pcity, x, y, C_TILE_UNAVAILABLE);
        freelog(LOG_DEBUG, "Worked tile was unavailable!");
 
Index: server/score.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/score.c,v
retrieving revision 1.6
diff -u -r1.6 score.c
--- server/score.c      3 Sep 2004 01:21:03 -0000       1.6
+++ server/score.c      3 Sep 2004 22:44:29 -0000
@@ -376,16 +376,16 @@
 int civ_score(struct player *pplayer)
 {
   struct city *pcity;
-  int landarea, settledarea;
+  int landarea, settledarea, sp;
   static struct claim_map cmap = { NULL, NULL, NULL,NULL };
 
   pplayer->score.happy = 0;
   pplayer->score.content = 0;
   pplayer->score.unhappy = 0;
   pplayer->score.angry = 0;
-  pplayer->score.taxmen = 0;
-  pplayer->score.scientists = 0;
-  pplayer->score.elvis = 0;
+  for (sp = 0; sp < SP_COUNT; sp++) {
+    pplayer->score.specialists[sp] = 0;
+  }
   pplayer->score.wonders = 0;
   pplayer->score.techs = 0;
   pplayer->score.techout = 0;
@@ -408,13 +408,15 @@
   }
 
   city_list_iterate(pplayer->cities, pcity) {
+    int sp;
+
     pplayer->score.happy += pcity->ppl_happy[4];
     pplayer->score.content += pcity->ppl_content[4];
     pplayer->score.unhappy += pcity->ppl_unhappy[4];
     pplayer->score.angry += pcity->ppl_angry[4];
-    pplayer->score.taxmen += pcity->specialists[SP_TAXMAN];
-    pplayer->score.scientists += pcity->specialists[SP_SCIENTIST];
-    pplayer->score.elvis += pcity->specialists[SP_ELVIS];
+    for (sp = 0; sp < SP_COUNT; sp++) {
+      pplayer->score.specialists[sp] += pcity->specialists[sp];
+    }
     pplayer->score.population += city_population(pcity);
     pplayer->score.cities++;
     pplayer->score.pollution += pcity->pollution;
Index: server/unithand.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/unithand.c,v
retrieving revision 1.303
diff -u -r1.303 unithand.c
--- server/unithand.c   26 Aug 2004 18:37:52 -0000      1.303
+++ server/unithand.c   3 Sep 2004 22:44:29 -0000
@@ -479,7 +479,7 @@
   assert(unit_pop_value(punit->type) > 0);
   pcity->size += unit_pop_value(punit->type);
   /* Make the new people something, otherwise city fails the checks */
-  pcity->specialists[SP_TAXMAN] += unit_pop_value(punit->type);
+  pcity->specialists[DEFAULT_SPECIALIST] += unit_pop_value(punit->type);
   auto_arrange_workers(pcity);
   wipe_unit(punit);
   send_city_info(NULL, pcity);

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