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

[Freeciv-Dev] (PR#8877) RFC: 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) RFC: design for generalized specialists
From: "Jason Short" <jdorje@xxxxxxxxxxxxxxxxxxxxx>
Date: Sun, 30 May 2004 16:35:59 -0700
Reply-to: rt@xxxxxxxxxxx

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

Elsewhere the general concepts have been explained.  They're pretty simple.

- Specialist data (including number of specialists and all statistics 
about them) should be stored in an array.

- All information about the specialists should be read from the ruleset.

- Later more information about specialists can be added.  A tech_req is 
the most obvious choice.

So the overall design is pretty obvious.  However in local areas there 
are a few problems.  There are three cases in particular.

- A lot of places do something like

   "freelog(LOG_NORMAL, "%d/%d/%d", pcity->ppl_elvis, ...);"

ironically this is pretty annoying to fix.  I guess we need a function 
to write a buffer containing the list of specialist counts.

- The client's city report dialog contains separate entries for each 
specialist type.  Keeping this will mean changing a lot of this array. 
The array must be dynamic and we have to add the new entries at runtime. 
  Also we need a single callback function with an added callback 
parameter.  The alternative of just removing these entries is probably fine.

- The CM is of course the major problem.  The biggest problem is that 
the CM assumes all specialists are orthogonal.  It assumes that you can 
calculate the goodness of having N scientists independently of the 
number of elvises and taxmen (in other words, it assumes that no two 
specialists can produce the same secondary output).  It also assumes 
(but this is easier to fix) that each specialist only produces one kind 
of secondary output.  These are no longer true (and for instance, aren't 
true under SMAC specialists).  The only real solution is to remove this 
assumption entirely...which means the cache2 concept no longer works.


The attached patch is a proof-of-concept for this.  All needed changes 
are made so that all specialist info is loaded from the ruleset. 
Autogames seem to be unchanged, and (surprisingly) the runtime is not 
impacted either (I could be wrong on this though, it's easy to make a 
mistake when running autogames).

As for the above problems, I commented out all the impacted freelogs, 
removed the cityrep entries, and removed cache2 entirely.

Of course I haven't tested it exhaustively, and there are sure to be bugs.

jason

? diff
? ferries
? flags
? foo
? new
? orig
? data/flags
Index: ai/advdomestic.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/advdomestic.c,v
retrieving revision 1.109
diff -u -r1.109 advdomestic.c
--- ai/advdomestic.c    27 May 2004 22:14:17 -0000      1.109
+++ ai/advdomestic.c    30 May 2004 23:21:12 -0000
@@ -190,6 +190,27 @@
   return best;
 }
 
+static int get_num_entertainers(struct city *pcity)
+{
+  int elvis = 0, i;
+
+  /* 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. */
+  for (i = 0; i < SP_COUNT; i++) {
+    if (game.rgame.specialists[i].bonus_lux >= 2) {
+      elvis += pcity->specialists[i];
+    }
+  }
+
+  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.
@@ -211,8 +232,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 */
@@ -246,9 +266,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
@@ -407,7 +429,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);
@@ -426,9 +448,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 :
@@ -445,9 +465,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;
   }
 
@@ -526,15 +544,30 @@
         break;
     case B_UNIVERSITY:
     case B_LIBRARY:
-      values[id] = sci/2;
+      values[id] = sci / 2;
+      /* FIXME: scientists */
       break;
   
     /* happiness generation - trade production */
     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);
+      }
+
+      /* Bonus is 50% */
+      values[id] /= 2;
       break;
     case B_SUPERHIGHWAYS:
       values[id] = road_trade(pcity) * t;
@@ -956,9 +989,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.155
diff -u -r1.155 aicity.c
--- ai/aicity.c 27 May 2004 22:14:17 -0000      1.155
+++ ai/aicity.c 30 May 2004 23:21:12 -0000
@@ -700,7 +700,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.34
diff -u -r1.34 citydlg_common.c
--- client/citydlg_common.c     27 May 2004 22:14:18 -0000      1.34
+++ client/citydlg_common.c     30 May 2004 23:21:12 -0000
@@ -372,32 +372,29 @@
   citizens (use MAX_CITY_SIZE to be on the safe side).
 **************************************************************************/
 void get_city_citizen_types(struct city *pcity, int index,
-                           enum citizen_type *citizens)
+                           struct citizen_type *citizens)
 {
-  int i = 0, n;
+  int i = 0, n, sp;
   assert(index >= 0 && index < 5);
 
   for (n = 0; n < pcity->ppl_happy[index]; n++, i++) {
-    citizens[i] = CITIZEN_HAPPY;
+    citizens[i].type = CITIZEN_HAPPY;
   }
   for (n = 0; n < pcity->ppl_content[index]; n++, i++) {
-    citizens[i] = CITIZEN_CONTENT;
+    citizens[i].type = CITIZEN_CONTENT;
   }
   for (n = 0; n < pcity->ppl_unhappy[index]; n++, i++) {
-    citizens[i] = CITIZEN_UNHAPPY;
+    citizens[i].type = CITIZEN_UNHAPPY;
   }
   for (n = 0; n < pcity->ppl_angry[index]; n++, i++) {
-    citizens[i] = CITIZEN_ANGRY;
+    citizens[i].type = CITIZEN_ANGRY;
   }
 
-  for (n = 0; n < pcity->specialists[SP_ELVIS]; n++, i++) {
-    citizens[i] = CITIZEN_ELVIS;
-  }
-  for (n = 0; n < pcity->specialists[SP_SCIENTIST]; n++, i++) {
-    citizens[i] = CITIZEN_SCIENTIST;
-  }
-  for (n = 0; n < pcity->specialists[SP_TAXMAN]; n++, i++) {
-    citizens[i] = CITIZEN_TAXMAN;
+  for (sp = 0; sp < SP_COUNT; sp++) {
+    for (n = 0; n < pcity->specialists[sp]; n++, i++) {
+      citizens[i].type = CITIZEN_SPECIALIST;
+      citizens[i].spec_type = sp;
+    }
   }
 
   assert(i == pcity->size);
@@ -408,8 +405,8 @@
 **************************************************************************/
 void city_rotate_specialist(struct city *pcity, int citizen_index)
 {
-  enum citizen_type citizens[MAX_CITY_SIZE];
-  enum specialist_type from, to;
+  struct citizen_type citizens[MAX_CITY_SIZE];
+  int from, to;
 
   if (citizen_index < 0 || citizen_index >= pcity->size) {
     return;
@@ -417,23 +414,12 @@
 
   get_city_citizen_types(pcity, 4, citizens);
 
-  switch (citizens[citizen_index]) {
-  case CITIZEN_ELVIS:
-    from = SP_ELVIS;
-    to = SP_SCIENTIST;
-    break;
-  case CITIZEN_SCIENTIST:
-    from = SP_SCIENTIST;
-    to = SP_TAXMAN;
-    break;
-  case CITIZEN_TAXMAN:
-    from = SP_TAXMAN;
-    to = SP_ELVIS;
-    break;
-  default:
+  if (citizens[citizen_index].type != CITIZEN_SPECIALIST) {
     return;
   }
 
+  from = citizens[citizen_index].spec_type;
+  to = (from + 1) % SP_COUNT;
   city_change_specialist(pcity, from, to);
 }
     
@@ -552,8 +538,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.20
diff -u -r1.20 citydlg_common.h
--- client/citydlg_common.h     4 Apr 2004 14:49:10 -0000       1.20
+++ client/citydlg_common.h     30 May 2004 23:21:12 -0000
@@ -23,15 +23,16 @@
 struct city;
 struct canvas;
 
-enum citizen_type {
-  CITIZEN_ELVIS,
-  CITIZEN_SCIENTIST,
-  CITIZEN_TAXMAN,
-  CITIZEN_CONTENT,
-  CITIZEN_HAPPY,
-  CITIZEN_UNHAPPY,
-  CITIZEN_ANGRY,
-  CITIZEN_LAST
+struct citizen_type {
+  enum {
+    CITIZEN_SPECIALIST,
+    CITIZEN_CONTENT,
+    CITIZEN_HAPPY,
+    CITIZEN_UNHAPPY,
+    CITIZEN_ANGRY,
+    CITIZEN_LAST
+  } type;
+  int spec_type;
 };
 
 int get_citydlg_canvas_width(void);
@@ -53,7 +54,7 @@
                                    bool is_unit, struct city *pcity);
 
 void get_city_citizen_types(struct city *pcity, int index,
-                           enum citizen_type *citizens);
+                           struct citizen_type *citizens);
 void city_rotate_specialist(struct city *pcity, int citizen_index);
 
 void activate_all_units(int map_x, int map_y);
@@ -64,8 +65,7 @@
 void city_set_queue(struct city *pcity, struct worklist *pqueue);
 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.33
diff -u -r1.33 cityrepdata.c
--- client/cityrepdata.c        27 May 2004 22:14:18 -0000      1.33
+++ client/cityrepdata.c        30 May 2004 23:21:13 -0000
@@ -115,13 +115,20 @@
 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]);
+  int i;
+
+  buf[0] = '\0';
+  for (i = 0; i < SP_COUNT; i++) {
+    if (i != 0) {
+      sz_strlcat(buf, "/");
+    }
+    my_snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
+               "%d", pcity->specialists[i]);
+  }
   return buf;
 }
 
+#if 0
 static char *cr_entry_entertainers(struct city *pcity)
 {
   static char buf[8];
@@ -142,6 +149,7 @@
   my_snprintf(buf, sizeof(buf), "%2d", pcity->specialists[SP_TAXMAN]);
   return buf;
 }
+#endif
 
 static char *cr_entry_attack(struct city *pcity)
 {
@@ -442,12 +450,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.9
diff -u -r1.9 cityrepdata.h
--- client/cityrepdata.h        4 Feb 2003 22:13:27 -0000       1.9
+++ client/cityrepdata.h        30 May 2004 23:21:13 -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.373
diff -u -r1.373 packhand.c
--- client/packhand.c   29 May 2004 20:57:16 -0000      1.373
+++ client/packhand.c   30 May 2004 23:21:13 -0000
@@ -2675,12 +2675,17 @@
 {
   int i;
 
-  game.rgame.min_size_elvis = packet->min_size_elvis;
-  game.rgame.min_size_taxman = packet->min_size_taxman;
-  game.rgame.min_size_scientist = packet->min_size_scientist;
-  game.rgame.base_elvis = packet->base_elvis;
-  game.rgame.base_scientist = packet->base_scientist;
-  game.rgame.base_taxman = packet->base_taxman;
+  game.rgame.num_specialist_types = packet->num_specialist_types;
+  game.rgame.default_specialist = packet->default_specialist;
+  for (i = 0; i < SP_COUNT; i++) {
+    sz_strlcpy(game.rgame.specialists[i].name, packet->specialist_name[i]);
+    game.rgame.specialists[i].min_size = packet->specialist_min_size[i];
+    game.rgame.specialists[i].bonus_sci = packet->specialist_bonus_sci[i];
+    game.rgame.specialists[i].bonus_tax = packet->specialist_bonus_tax[i];
+    game.rgame.specialists[i].bonus_lux = packet->specialist_bonus_lux[i];
+  }
+  tilespec_setup_citizen_types();
+
   game.rgame.changable_tax = packet->changable_tax;
   game.rgame.forced_science = packet->forced_science;
   game.rgame.forced_luxury = packet->forced_luxury;
Index: client/tilespec.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/tilespec.c,v
retrieving revision 1.170
diff -u -r1.170 tilespec.c
--- client/tilespec.c   22 May 2004 18:12:21 -0000      1.170
+++ client/tilespec.c   30 May 2004 23:21:14 -0000
@@ -838,17 +838,13 @@
 /**********************************************************************
   Returns a text name for the citizen, as used in the tileset.
 ***********************************************************************/
-static const char *get_citizen_name(enum citizen_type citizen)
+static const char *get_citizen_name(struct citizen_type citizen)
 {
   /* These strings are used in reading the tileset.  Do not
    * translate. */
-  switch (citizen) {
-  case CITIZEN_ELVIS:
-    return "entertainer";
-  case CITIZEN_SCIENTIST:
-    return "scientist";
-  case CITIZEN_TAXMAN:
-    return "tax_collector";
+  switch (citizen.type) {
+  case CITIZEN_SPECIALIST:
+    return game.rgame.specialists[citizen.spec_type].name;
   case CITIZEN_HAPPY:
     return "happy";
   case CITIZEN_CONTENT:
@@ -858,7 +854,7 @@
   case CITIZEN_ANGRY:
     return "angry";
   default:
-    die("unknown citizen type %d", (int) citizen);
+    die("unknown citizen type %d", (int) citizen.type);
   }
   return NULL;
 }
@@ -899,6 +895,50 @@
 #define SET_SPRITE_OPT(field, tag) \
   sprites.field = load_sprite(tag)
 
+void tilespec_setup_citizen_types(void)
+{
+  int i, j;
+  char buffer[512];
+
+  /* Load the citizen sprite graphics. */
+  for (i = 0; i < NUM_TILES_CITIZEN + SP_COUNT; i++) {
+    struct citizen_type c;
+
+    if (i >= NUM_TILES_CITIZEN) {
+      c.type = CITIZEN_SPECIALIST;
+      c.spec_type = i - NUM_TILES_CITIZEN;
+    } else {
+      c.type = i;
+      if (i == CITIZEN_SPECIALIST) {
+       continue;
+      }
+    }
+
+    my_snprintf(buffer, sizeof(buffer), "citizen.%s", get_citizen_name(c));
+    sprites.citizen[i].sprite[0] = load_sprite(buffer);
+    if (sprites.citizen[i].sprite[0]) {
+      /*
+       * If this form exists, use it as the only sprite.  This allows
+       * backwards compatability with tilesets that use e.g.,
+       * citizen.entertainer.
+       */
+      sprites.citizen[i].count = 1;
+      continue;
+    }
+
+    for (j = 0; j < NUM_TILES_CITIZEN; j++) {
+      my_snprintf(buffer, sizeof(buffer), "citizen.%s_%d",
+                 get_citizen_name(c), j);
+      sprites.citizen[i].sprite[j] = load_sprite(buffer);
+      if (!sprites.citizen[i].sprite[j]) {
+       break;
+      }
+    }
+    sprites.citizen[i].count = j;
+    assert(j > 0);
+  }
+}
+
 /**********************************************************************
   Initialize 'sprites' structure based on hardwired tags which
   freeciv always requires. 
@@ -907,7 +947,7 @@
 {
   char buffer[512];
   const char dir_char[] = "nsew";
-  int i, j;
+  int i;
   
   assert(sprite_hash != NULL);
 
@@ -929,31 +969,7 @@
     SET_SPRITE(dither_tile, "t.dither_tile");
   }
 
-  /* Load the citizen sprite graphics. */
-  for (i = 0; i < NUM_TILES_CITIZEN; i++) {
-    my_snprintf(buffer, sizeof(buffer), "citizen.%s", get_citizen_name(i));
-    sprites.citizen[i].sprite[0] = load_sprite(buffer);
-    if (sprites.citizen[i].sprite[0]) {
-      /*
-       * If this form exists, use it as the only sprite.  This allows
-       * backwards compatability with tilesets that use e.g.,
-       * citizen.entertainer.
-       */
-      sprites.citizen[i].count = 1;
-      continue;
-    }
-
-    for (j = 0; j < NUM_TILES_CITIZEN; j++) {
-      my_snprintf(buffer, sizeof(buffer), "citizen.%s_%d",
-                 get_citizen_name(i), j);
-      sprites.citizen[i].sprite[j] = load_sprite(buffer);
-      if (!sprites.citizen[i].sprite[j]) {
-       break;
-      }
-    }
-    sprites.citizen[i].count = j;
-    assert(j > 0);
-  }
+  tilespec_setup_citizen_types();
 
   SET_SPRITE(spaceship.solar_panels, "spaceship.solar_panels");
   SET_SPRITE(spaceship.life_support, "spaceship.life_support");
@@ -2570,12 +2586,20 @@
   value indicates there is no city; i.e., the sprite is just being
   used as a picture).
 **************************************************************************/
-struct Sprite *get_citizen_sprite(enum citizen_type type, int citizen_index,
+struct Sprite *get_citizen_sprite(struct citizen_type type,
+                                 int citizen_index,
                                  struct city *pcity)
 {
-  assert(type >= 0 && type < NUM_TILES_CITIZEN);
-  citizen_index %= sprites.citizen[type].count;
-  return sprites.citizen[type].sprite[citizen_index];
+  int i;
+
+  if (type.type == CITIZEN_SPECIALIST) {
+    i = NUM_TILES_CITIZEN + type.spec_type;
+  } else {
+    i = type.type;
+  }
+  assert(i >= 0 && i < NUM_TILES_CITIZEN + SP_COUNT);
+  citizen_index %= sprites.citizen[i].count;
+  return sprites.citizen[i].sprite[citizen_index];
 }
 
 /**************************************************************************
Index: client/tilespec.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/tilespec.h,v
retrieving revision 1.67
diff -u -r1.67 tilespec.h
--- client/tilespec.h   21 May 2004 17:59:42 -0000      1.67
+++ client/tilespec.h   30 May 2004 23:21:14 -0000
@@ -54,6 +54,7 @@
 void tilespec_reread(const char *tileset_name);
 void tilespec_reread_callback(struct client_option *option);
 
+void tilespec_setup_citizen_types(void);
 void tilespec_setup_unit_type(int id);
 void tilespec_setup_impr_type(int id);
 void tilespec_setup_tech_type(int id);
@@ -147,7 +148,7 @@
      * sprites, as defined by the tileset. */
     int count;
     struct Sprite *sprite[MAX_NUM_CITIZEN_SPRITES];
-  } citizen[NUM_TILES_CITIZEN];
+  } citizen[NUM_TILES_CITIZEN + MAX_NUM_SPECIALISTS - 1];
   struct {
     struct Sprite
       *solar_panels,
@@ -241,7 +242,7 @@
 
 extern struct named_sprites sprites;
 
-struct Sprite *get_citizen_sprite(enum citizen_type type, int citizen_index,
+struct Sprite *get_citizen_sprite(struct citizen_type type, int citizen_index,
                                  struct city *pcity);
 
 /* full pathnames: */
Index: client/agents/cma_core.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/agents/cma_core.c,v
retrieving revision 1.53
diff -u -r1.53 cma_core.c
--- client/agents/cma_core.c    29 May 2004 20:34:31 -0000      1.53
+++ client/agents/cma_core.c    30 May 2004 23:21:14 -0000
@@ -103,6 +103,18 @@
   return worker;
 }
 
+static int count_specialist(struct city *pcity,
+                           const struct cm_result *const result)
+{
+  int count = 0, sp;
+
+  for (sp = 0; sp < SP_COUNT; sp++) {
+    count += result->specialists[sp];
+  }
+
+  return count;
+}
+
 #define T(x) if (result1->x != result2->x) { \
        freelog(RESULTS_ARE_EQUAL_LOG_LEVEL, #x); \
        return FALSE; }
@@ -115,11 +127,14 @@
                             const struct cm_result *const result1,
                             const struct cm_result *const result2)
 {
+  int sp;
+
   T(disorder);
   T(happy);
-  T(specialists[SP_ELVIS]);
-  T(specialists[SP_SCIENTIST]);
-  T(specialists[SP_TAXMAN]);
+
+  for (sp = 0; sp < SP_COUNT; sp++) {
+    T(specialists[sp]);
+  }
 
   T(production[FOOD]);
   T(production[SHIELD]);
@@ -156,11 +171,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) {
@@ -188,7 +205,7 @@
 static void print_result(struct city *pcity,
                         const struct cm_result *const result)
 {
-  int y, i, worker = count_worker(pcity, result);
+  int y, i;
 
   freelog(LOG_NORMAL, "print_result(result=%p)", result);
   freelog(LOG_NORMAL,
@@ -223,10 +240,12 @@
     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,
@@ -269,7 +288,7 @@
 static void get_current_as_result(struct city *pcity,
                                  struct cm_result *result)
 {
-  int worker = 0, i;
+  int worker = 0, i, specialists = 0;
 
   memset(result->worker_positions_used, 0,
         sizeof(result->worker_positions_used));
@@ -284,11 +303,10 @@
 
   for (i = 0; i < SP_COUNT; i++) {
     result->specialists[i] = pcity->specialists[i];
+    specialists++;
   }
 
-  assert(worker + result->specialists[SP_ELVIS]
-        + result->specialists[SP_SCIENTIST]
-        + result->specialists[SP_TAXMAN] == pcity->size);
+  assert(worker + specialists == pcity->size);
 
   result->found_a_valid = TRUE;
 
@@ -352,10 +370,7 @@
 
   /* Do checks */
   worker = count_worker(pcity, result);
-  if (pcity->size !=
-      (worker + result->specialists[SP_ELVIS]
-       + result->specialists[SP_SCIENTIST]
-       + result->specialists[SP_TAXMAN])) {
+  if (pcity->size != (worker + count_specialist(pcity, result))) {
     print_city(pcity);
     print_result(pcity, result);
     assert(0);
@@ -372,11 +387,13 @@
     }
   } 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). */
+  for (sp = 0; sp < SP_COUNT; 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;
       }
@@ -386,8 +403,8 @@
   /* 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) {
@@ -399,12 +416,14 @@
     }
   } 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). */
+  for (sp = 0; sp < SP_COUNT; 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;
       }
Index: client/agents/cma_fec.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/agents/cma_fec.c,v
retrieving revision 1.20
diff -u -r1.20 cma_fec.c
--- client/agents/cma_fec.c     29 May 2004 20:34:31 -0000      1.20
+++ client/agents/cma_fec.c     30 May 2004 23:21:14 -0000
@@ -359,6 +359,7 @@
     my_snprintf(buf[5], BUFFER_SIZE, "%3d(%+3d)",
                result->production[SCIENCE], result->surplus[SCIENCE]);
 
+#if 0
     my_snprintf(buf[6], BUFFER_SIZE, "%d/%d/%d/%d%s",
                pcity->size -
                (result->specialists[SP_ELVIS]
@@ -368,6 +369,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: client/gui-gtk-2.0/citydlg.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/gui-gtk-2.0/citydlg.c,v
retrieving revision 1.88
diff -u -r1.88 citydlg.c
--- client/gui-gtk-2.0/citydlg.c        24 May 2004 13:00:51 -0000      1.88
+++ client/gui-gtk-2.0/citydlg.c        30 May 2004 23:21:14 -0000
@@ -1262,7 +1262,7 @@
 {
   int i, width;
   struct city *pcity = pdialog->pcity;
-  enum citizen_type citizens[MAX_CITY_SIZE];
+  struct citizen_type citizens[MAX_CITY_SIZE];
 
   /* If there is not enough space we stack the icons. We draw from left to */
   /* right. width is how far we go to the right for each drawn pixmap. The */
Index: client/gui-gtk-2.0/gui_main.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/gui-gtk-2.0/gui_main.c,v
retrieving revision 1.72
diff -u -r1.72 gui_main.c
--- client/gui-gtk-2.0/gui_main.c       1 May 2004 17:28:47 -0000       1.72
+++ client/gui-gtk-2.0/gui_main.c       30 May 2004 23:21:15 -0000
@@ -671,7 +671,8 @@
   int i;
   struct Sprite *sprite;
   GtkCellRenderer *rend;
-
+  struct citizen_type c;
+  
   main_tips = gtk_tooltips_new();
 
   /* the window is divided into two panes. "top" and "message window" */ 
@@ -803,7 +804,9 @@
   gtk_container_add(GTK_CONTAINER(ebox), table2);
   
   for (i = 0; i < 10; i++) {
-    enum citizen_type c = i < 5 ? CITIZEN_SCIENTIST : CITIZEN_TAXMAN;
+    c.type = CITIZEN_SPECIALIST;
+    c.spec_type = 0;
+
     ebox = gtk_event_box_new();
     gtk_widget_add_events(ebox, GDK_BUTTON_PRESS_MASK);
 
@@ -812,8 +815,12 @@
     g_signal_connect(ebox, "button_press_event",
                      G_CALLBACK(taxrates_callback), GINT_TO_POINTER(i));
 
+#if 0
     sprite = get_citizen_sprite(c, i, NULL);
     econ_label[i] = gtk_image_new_from_pixmap(sprite->pixmap, sprite->mask);
+#endif
+    /* FIXME: use different tags (not the specialists) for these graphics. */
+    econ_label[i] = gtk_image_new();
     gtk_container_add(GTK_CONTAINER(ebox), econ_label[i]);
   }
 
@@ -821,7 +828,8 @@
   bulb_label = gtk_image_new_from_pixmap(sprites.bulb[0]->pixmap, NULL);
   sun_label = gtk_image_new_from_pixmap(sprites.warming[0]->pixmap, NULL);
   flake_label = gtk_image_new_from_pixmap(sprites.cooling[0]->pixmap, NULL);
-  sprite = get_citizen_sprite(CITIZEN_UNHAPPY, 0, NULL);
+  c.type = CITIZEN_UNHAPPY;
+  sprite = get_citizen_sprite(c, 0, NULL);
   government_label = gtk_image_new_from_pixmap(sprite->pixmap, sprite->mask);
 
   for (i = 0; i < 4; i++) {
Index: client/gui-gtk-2.0/happiness.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/gui-gtk-2.0/happiness.c,v
retrieving revision 1.12
diff -u -r1.12 happiness.c
--- client/gui-gtk-2.0/happiness.c      5 May 2004 20:39:15 -0000       1.12
+++ client/gui-gtk-2.0/happiness.c      30 May 2004 23:21:15 -0000
@@ -153,7 +153,7 @@
 static void refresh_pixcomm(GtkPixcomm *dst, struct city *pcity, int index)
 {
   int i;
-  enum citizen_type citizens[MAX_CITY_SIZE];
+  struct citizen_type citizens[MAX_CITY_SIZE];
   int num_citizens = pcity->size;
   int offset = MIN(SMALL_TILE_WIDTH, PIXCOMM_WIDTH / num_citizens);
 
Index: client/gui-gtk-2.0/mapview.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/gui-gtk-2.0/mapview.c,v
retrieving revision 1.126
diff -u -r1.126 mapview.c
--- client/gui-gtk-2.0/mapview.c        24 May 2004 22:48:08 -0000      1.126
+++ client/gui-gtk-2.0/mapview.c        30 May 2004 23:21:15 -0000
@@ -123,6 +123,32 @@
 {
   int  d;
   int  sol, flake;
+  int lux_specialist = DEFAULT_SPECIALIST,
+    sci_specialist = DEFAULT_SPECIALIST,
+    tax_specialist = DEFAULT_SPECIALIST;
+  struct citizen_type c;
+
+  {
+    /* The tileset should specify different tags for science, luxury,
+     * and tax percentage sprites.  Instead we just guess the best
+     * specialist to use the sprite from. */
+    int max_lux = -1, max_sci = -1, max_tax = -1, i;
+
+    for (i = 0; i < SP_COUNT; i++) {
+      if (game.rgame.specialists[i].bonus_lux > max_lux) {
+       max_lux = game.rgame.specialists[i].bonus_lux;
+       lux_specialist = i;
+      }
+      if (game.rgame.specialists[i].bonus_sci > max_sci) {
+       max_sci = game.rgame.specialists[i].bonus_sci;
+       sci_specialist = i;
+      }
+      if (game.rgame.specialists[i].bonus_tax > max_tax) {
+       max_tax = game.rgame.specialists[i].bonus_tax;
+       tax_specialist = i;
+      }
+    }
+  }
 
   gtk_frame_set_label(GTK_FRAME(main_frame_civ_name),
                      get_nation_name(game.player_ptr->nation));
@@ -137,21 +163,25 @@
                      game.player_ptr->government);
 
   d=0;
+  c.type = CITIZEN_SPECIALIST;
+  c.spec_type = lux_specialist;
   for (; d < game.player_ptr->economic.luxury /10; d++) {
-    struct Sprite *sprite = get_citizen_sprite(CITIZEN_ELVIS, d, NULL);
+    struct Sprite *sprite = get_citizen_sprite(c, d, NULL);
     gtk_image_set_from_pixmap(GTK_IMAGE(econ_label[d]),
                              sprite->pixmap, sprite->mask);
   }
- 
+
+  c.spec_type = sci_specialist;
   for (; d < (game.player_ptr->economic.science
             + game.player_ptr->economic.luxury) / 10; d++) {
-    struct Sprite *sprite = get_citizen_sprite(CITIZEN_SCIENTIST, d, NULL);
+    struct Sprite *sprite = get_citizen_sprite(c, d, NULL);
     gtk_image_set_from_pixmap(GTK_IMAGE(econ_label[d]),
                              sprite->pixmap, sprite->mask);
   }
  
+  c.spec_type = tax_specialist;
   for (; d < 10; d++) {
-    struct Sprite *sprite = get_citizen_sprite(CITIZEN_TAXMAN, d, NULL);
+    struct Sprite *sprite = get_citizen_sprite(c, d, NULL);
     gtk_image_set_from_pixmap(GTK_IMAGE(econ_label[d]),
                              sprite->pixmap, sprite->mask);
   }
@@ -246,7 +276,8 @@
 
   if (game.government_count==0) {
     /* not sure what to do here */
-    gov_sprite = get_citizen_sprite(CITIZEN_UNHAPPY, 0, NULL);
+    struct citizen_type c = {CITIZEN_UNHAPPY};
+    gov_sprite = get_citizen_sprite(c, 0, NULL);
   } else {
     gov_sprite = get_government(gov)->sprite;
   }
Index: common/capstr.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/capstr.c,v
retrieving revision 1.166
diff -u -r1.166 capstr.c
--- common/capstr.c     21 May 2004 19:03:43 -0000      1.166
+++ common/capstr.c     30 May 2004 23:21:15 -0000
@@ -77,7 +77,7 @@
 #define CAPABILITY "+1.14.delta +last_turns_shield_surplus veteran +orders " \
                    "+starter +union +iso_maps +orders2client " \
                    "+change_production +tilespec1 +no_earth +trans " \
-                   "+want_hack invasions bombard +killstack2 spec"
+                   "+want_hack invasions bombard +killstack2 spec +spec2"
 
 /* "+1.14.delta" is the new delta protocol for 1.14.0-dev.
  *
@@ -117,6 +117,8 @@
  * "bombard" means units support the bombard ability.
  * 
  * "spec" is configurable specialists
+ *
+ * "spec2" is semi-configurable specialists in an array
  */
 
 void init_our_capability(void)
Index: common/city.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/city.c,v
retrieving revision 1.213
diff -u -r1.213 city.c
--- common/city.c       29 May 2004 20:57:16 -0000      1.213
+++ common/city.c       30 May 2004 23:21:16 -0000
@@ -1723,6 +1723,7 @@
   int sci_rest, tax_rest, lux_rest;
   struct player *pplayer = city_owner(pcity);
   int sci_rate, lux_rate, tax_rate;
+  int i;
 
   if (game.rgame.changable_tax) {
     sci_rate = pplayer->economic.science;
@@ -1817,17 +1818,18 @@
 
   assert(sci + tax + lux == pcity->trade_prod);
 
+  for (i = 0; i < game.rgame.num_specialist_types; i++) {
+    pcity->luxury_total
+      += pcity->specialists[i] * game.rgame.specialists[i].bonus_lux;
+    pcity->science_total
+      += pcity->specialists[i] * game.rgame.specialists[i].bonus_sci;
+    pcity->tax_total
+      += pcity->specialists[i] * game.rgame.specialists[i].bonus_tax;
+  }
+
   pcity->science_total = sci;
-  pcity->tax_total = tax;
+  pcity->tax_total = tax + get_city_tithes_bonus(pcity);
   pcity->luxury_total = lux;
-
-  pcity->luxury_total
-    += (pcity->specialists[SP_ELVIS] * game.rgame.base_elvis);
-  pcity->science_total
-    += (pcity->specialists[SP_SCIENTIST] * game.rgame.base_scientist);
-  pcity->tax_total
-    += ((pcity->specialists[SP_TAXMAN] * game.rgame.base_taxman) 
-       + get_city_tithes_bonus(pcity));
 }
 
 /**************************************************************************
@@ -2421,13 +2423,55 @@
 {
   int count = 0, i;
 
-  for (i = 0; i < SP_COUNT; i++) {
+  for (i = 0; i < game.rgame.num_specialist_types; i++) {
     count += pcity->specialists[i];
   }
 
   return count;
 }
 
+int best_science_specialist(void)
+{
+  int best = DEFAULT_SPECIALIST, val = -1, i;
+
+  for (i = 0; i < SP_COUNT; i++) {
+    if (game.rgame.specialists[i].bonus_sci > val) {
+      best = i;
+      val = game.rgame.specialists[i].bonus_sci;
+    }
+  }
+
+  return best;
+}
+
+int best_tax_specialist(void)
+{
+  int best = DEFAULT_SPECIALIST, val = -1, i;
+
+  for (i = 0; i < SP_COUNT; i++) {
+    if (game.rgame.specialists[i].bonus_tax > val) {
+      best = i;
+      val = game.rgame.specialists[i].bonus_tax;
+    }
+  }
+
+  return best;
+}
+
+int best_luxury_specialist(void)
+{
+  int best = DEFAULT_SPECIALIST, val = -1, i;
+
+  for (i = 0; i < SP_COUNT; i++) {
+    if (game.rgame.specialists[i].bonus_lux > val) {
+      best = i;
+      val = game.rgame.specialists[i].bonus_lux;
+    }
+  }
+
+  return best;
+}
+
 /**************************************************************************
 ...
 **************************************************************************/
@@ -2575,10 +2619,10 @@
   pcity->y = y;
   sz_strlcpy(pcity->name, name);
   pcity->size = 1;
-  for (i = 0; i < SP_COUNT; i++) {
+  for (i = 0; i < game.rgame.num_specialist_types; i++) {
     pcity->specialists[i] = 0;
   }
-  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.144
diff -u -r1.144 city.h
--- common/city.h       27 May 2004 22:14:18 -0000      1.144
+++ common/city.h       30 May 2004 23:21:16 -0000
@@ -25,10 +25,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
 };
@@ -82,6 +78,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
@@ -231,7 +230,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];
@@ -475,6 +474,9 @@
 int city_corruption(struct city *pcity, int trade);
 int city_waste(struct city *pcity, int shields);
 int city_specialists(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(struct city *pcity);
 int get_cathedral_power(struct city *pcity);
 int get_colosseum_power(struct city *pcity);
Index: common/game.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/game.c,v
retrieving revision 1.180
diff -u -r1.180 game.c
--- common/game.c       18 May 2004 16:29:30 -0000      1.180
+++ common/game.c       30 May 2004 23:21:16 -0000
@@ -73,13 +73,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.140
diff -u -r1.140 game.h
--- common/game.h       25 May 2004 00:40:20 -0000      1.140
+++ common/game.h       30 May 2004 23:21:16 -0000
@@ -186,12 +186,15 @@
 
   /* values from game.ruleset */
   struct {
-    int min_size_elvis;
-    int min_size_taxman;
-    int min_size_scientist;
-    int base_elvis;
-    int base_scientist;
-    int base_taxman;
+    struct specialist_type {
+      char name[MAX_LEN_NAME];
+      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.24
diff -u -r1.24 packets.def
--- common/packets.def  27 May 2004 22:14:18 -0000      1.24
+++ common/packets.def  30 May 2004 23:21:16 -0000
@@ -162,7 +162,7 @@
 type EVENT             = sint16(enum event_type)
 type TERRAIN           = uint8(enum tile_terrain_type)
 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)
@@ -381,7 +381,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;
@@ -956,12 +956,12 @@
 end
 
 PACKET_RULESET_GAME=97;sc,lsend
-  UINT8 min_size_elvis; add-cap(spec)
-  UINT8 min_size_taxman; add-cap(spec)
-  UINT8 min_size_scientist; add-cap(spec)
-  UINT8 base_elvis; add-cap(spec)
-  UINT8 base_scientist; add-cap(spec)
-  UINT8 base_taxman; add-cap(spec)
+  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; add-cap(spec)
   UINT8 forced_science; add-cap(spec)
   UINT8 forced_luxury; add-cap(spec)
Index: common/player.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/player.h,v
retrieving revision 1.115
diff -u -r1.115 player.h
--- common/player.h     23 Apr 2004 22:58:06 -0000      1.115
+++ common/player.h     30 May 2004 23:21:16 -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.22
diff -u -r1.22 cm.c
--- common/aicore/cm.c  29 May 2004 20:34:31 -0000      1.22
+++ common/aicore/cm.c  30 May 2004 23:21:17 -0000
@@ -147,28 +147,9 @@
 #define SHOW_TIME_STATS                                 FALSE
 #define DISABLE_CACHE3                                  FALSE
 
-#define NUM_SPECIALISTS_ROLES                          3
 #define MAX_FIELDS_USED                                        (CITY_TILES - 1)
 #define MAX_COMBINATIONS                               150
 
-/*
- * 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 production, 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.
@@ -179,7 +160,9 @@
   struct {
     struct combination {
       bool is_valid;
-      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];
 
@@ -203,7 +186,7 @@
   int queries;
   struct cache_stats {
     int hits, misses;
-  } cache1, cache2, cache3;
+  } cache1, cache3;
 } stats;
 
 /*
@@ -226,6 +209,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)
@@ -305,15 +291,20 @@
  Returns TRUE iff if the given city can use this kind of specialists.
 *****************************************************************************/
 static bool can_use_specialist(struct city *pcity,
-                              enum specialist_type specialist_type)
+                              int specialist_type)
 {
-  if (specialist_type == SP_ELVIS) {
-    return TRUE;
-  }
-  if (pcity->size >= 5) {
-    return TRUE;
+  return (pcity->size >= game.rgame.specialists[specialist_type].min_size);
+}
+
+static int get_num_specialists(const struct cm_result *const result)
+{
+  int i, count = 0;
+
+  for (i = 0; i < game.rgame.num_specialist_types; i++) {
+    count += result->specialists[i];
   }
-  return FALSE;
+
+  return count;
 }
 
 /****************************************************************************
@@ -326,8 +317,7 @@
   int i;
 
   if (!parameter->allow_specialists
-      && (result->specialists[SP_ELVIS] + result->specialists[SP_SCIENTIST]
-         + result->specialists[SP_TAXMAN]) >
+      && (get_num_specialists(result)) >
       MAX(0,cache3.pcity->size - cache3.fields_available_total)) {
     return FALSE;
   }
@@ -355,11 +345,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) {
@@ -387,7 +379,7 @@
 static void print_result(struct city *pcity,
                         const struct cm_result *const result)
 {
-  int y, i, worker = count_worker(pcity, result);
+  int y, i;
 
   freelog(LOG_NORMAL, "print_result(result=%p)", result);
   freelog(LOG_NORMAL,
@@ -422,11 +414,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,
@@ -485,108 +479,6 @@
 }
 
 /****************************************************************************
- 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->production[TRADE],
-                          result->specialists[SP_SCIENTIST],
-                          SP_SCIENTIST);
-    if (!p->is_valid) {
-      p->production = result->production[SCIENCE];
-      p->surplus = result->surplus[SCIENCE];
-      p->is_valid = TRUE;
-    } else {
-      assert(p->production == result->production[SCIENCE] &&
-            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->production[TRADE],
-                          result->specialists[SP_TAXMAN],
-                          SP_TAXMAN);
-    if (!p->is_valid && !result->disorder) {
-      p->production = result->production[GOLD];
-      p->surplus = result->surplus[GOLD];
-      p->is_valid = TRUE;
-    } else {
-      assert(p->production == result->production[GOLD] &&
-            p->surplus == result->surplus[GOLD]);
-    }
-  }
-
-  p = get_secondary_stat(result->production[TRADE],
-                        result->specialists[SP_ELVIS],
-                        SP_ELVIS);
-  if (!p->is_valid) {
-    p->production = result->production[LUXURY];
-    p->surplus = result->surplus[LUXURY];
-    p->is_valid = TRUE;
-  } else {
-    if (!result->disorder) {
-      assert(p->production == result->production[LUXURY] &&
-            p->surplus == result->surplus[LUXURY]);
-    }
-  }
-
-  q = get_city_status(result->production[LUXURY],
-                     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);
-  }
-}
-
-/****************************************************************************
 ...
 *****************************************************************************/
 static void clear_cache(void)
@@ -613,17 +505,14 @@
 static void real_fill_out_result(struct city *pcity,
                                 struct cm_result *result)
 {
-  int worker = count_worker(pcity, result);
+  int worker = count_worker(pcity, result), sp;
   struct city backup;
 
   freelog(LOG_DEBUG, "real_fill_out_result(city='%s'(%d))", pcity->name,
          pcity->id);
 
   /* Do checks */
-  if (pcity->size !=
-      (worker + result->specialists[SP_ELVIS]
-       + result->specialists[SP_SCIENTIST]
-       + result->specialists[SP_TAXMAN])) {
+  if (pcity->size != (worker + get_num_specialists(result))) {
     print_city(pcity);
     print_result(pcity, result);
     assert(0);
@@ -645,9 +534,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];
+  for (sp = 0; sp < SP_COUNT; sp++) {
+    pcity->specialists[sp] = result->specialists[sp];
+  }
 
   /* Do a local recalculation of the city */
   generic_city_refresh(pcity, FALSE, NULL);
@@ -657,6 +546,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",
          count_worker(pcity, result), result->specialists[SP_ELVIS],
@@ -666,7 +556,7 @@
          result->production[LUXURY],
          result->production[GOLD],
          result->disorder ? "yes" : "no", result->happy ? "yes" : "no");
-  update_cache2(pcity, result);
+#endif
 }
 
 /****************************************************************************
@@ -766,7 +656,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
 }
@@ -775,16 +664,34 @@
                            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];
+    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;
+  int index = combination_spec_index(base_combination, specialists);
 
   assert(base_combination->is_valid);
 
@@ -792,23 +699,25 @@
    * First try to get a filled out result from cache1 or from the
    * all_entertainer result.
    */
-  if (scientists == 0 && taxmen == 0) {
+  if (index == 0) {
     slot = &base_combination->all_entertainer;
   } else {
-    assert(scientists <= base_combination->max_scientists);
-    assert(taxmen <= base_combination->max_taxmen);
+    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 */
@@ -823,89 +732,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;
+  for (sp = 0; sp < SP_COUNT; sp++) {
+    if (sp == best_entertainer) continue;
 
+    result->specialists[sp] = specialists[sp];
+    result->specialists[best_entertainer] -= specialists[sp];
+  }
+
+#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->production[i] =
-         base_combination->all_entertainer.production[i];
-      result->surplus[i] = base_combination->all_entertainer.surplus[i];
-    }
-
-    p = get_secondary_stat(result->production[TRADE],
-                          result->specialists[SP_SCIENTIST],
-                          SP_SCIENTIST);
-    if (!p->is_valid) {
-      got_all = FALSE;
-    } else {
-      result->production[SCIENCE] = p->production;
-      result->surplus[SCIENCE] = p->surplus;
-    }
-
-    p = get_secondary_stat(result->production[TRADE],
-                          result->specialists[SP_TAXMAN],
-                          SP_TAXMAN);
-    if (!p->is_valid) {
-      got_all = FALSE;
-    } else {
-      result->production[GOLD] = p->production;
-      result->surplus[GOLD] = p->surplus;
-    }
-
-    p = get_secondary_stat(result->production[TRADE],
-                          result->specialists[SP_ELVIS],
-                          SP_ELVIS);
-    if (!p->is_valid) {
-      got_all = FALSE;
-    } else {
-      result->production[LUXURY] = p->production;
-      result->surplus[LUXURY] = p->surplus;
-    }
-
-    q = get_city_status(result->production[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
@@ -1076,86 +917,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_tax_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_tax_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_tax_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.
@@ -1234,8 +995,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);
 }
 
 /****************************************************************************
@@ -1249,28 +1008,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 (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 (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(can_use_specialist(pcity, sp));
+       continue;
+      }
+
+      if (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++) {
@@ -1280,54 +1046,74 @@
 
   best_result->found_a_valid = FALSE;
 
-  for (scientists = 0;
-       scientists <= base_combination->max_scientists; scientists++) {
-    for (taxmen = 0;
-        taxmen <= base_combination->max_scientists - scientists; taxmen++) {
-      int major_fitness, minor_fitness;
-      struct cm_result result;
-
-      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);
+  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;
 
-      fill_out_result(pcity, &result, base_combination, scientists,
-                     taxmen);
-
-      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]);
+    for (sp = 0; sp < SP_COUNT; sp++) {
+      int max = base_combination->max_specialists[sp + 1];
 
-      if (!is_valid_result(parameter, &result)) {
-       freelog(FIND_BEST_SPECIALIST_ARRANGEMENT_LOG_LEVEL,
-               "  optimize_people: doesn't have enough surplus or disorder");
+      if (sp == best_entertainer) {
+       continue;
+      }
+      if (max == 0) {
+       specialists[sp] = 0;
        continue;
       }
 
-      calc_fitness(pcity, parameter, &result, &major_fitness,
-                  &minor_fitness);
+      specialists[sp] = myindex % max;
+      myindex /= max;
+
+      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 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
+
+    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;
+    }
+  }
 }
 
 /****************************************************************************
@@ -1359,6 +1145,7 @@
          &cache3.results[fields_used].combinations[i];
       int stat, major_fitness, minor_fitness;
       struct cm_result result;
+      int specialists[SP_COUNT];
 
       if (!current->is_valid) {
        continue;
@@ -1367,7 +1154,8 @@
       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));
+      fill_out_result(pcity, &result, current, specialists);
 
       /*
        * Check. The actual production can be bigger because of city
@@ -1456,15 +1244,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();
 }
 
Index: common/aicore/cm.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/aicore/cm.h,v
retrieving revision 1.5
diff -u -r1.5 cm.h
--- common/aicore/cm.h  29 May 2004 20:34:31 -0000      1.5
+++ common/aicore/cm.h  30 May 2004 23:21:17 -0000
@@ -57,7 +57,7 @@
   int surplus[NUM_STATS];
 
   bool worker_positions_used[CITY_MAP_SIZE][CITY_MAP_SIZE];
-  int specialists[SP_COUNT];
+  int specialists[MAX_NUM_SPECIALISTS];
 };
 
 /*
Index: data/default/cities.ruleset
===================================================================
RCS file: /home/freeciv/CVS/freeciv/data/default/cities.ruleset,v
retrieving revision 1.10
diff -u -r1.10 cities.ruleset
--- data/default/cities.ruleset 21 May 2004 19:03:44 -0000      1.10
+++ data/default/cities.ruleset 30 May 2004 23:21:17 -0000
@@ -20,6 +20,17 @@
 ; fields restrict setting specialists to this type before the
 ; the city is of a certain size.
 [specialist]
+types = "elvis", "scientist", "taxman"
+
+elvis_min_size = 0
+elvis_bonus_lux = 2
+
+scientist_min_size = 5
+scientist_bonus_sci = 2
+
+taxman_min_size = 5
+taxman_bonus_tax = 2
+
 base_elvis = 2
 base_scientist = 3
 base_taxman = 3
Index: data/isotrident/small.spec
===================================================================
RCS file: /home/freeciv/CVS/freeciv/data/isotrident/small.spec,v
retrieving revision 1.1
diff -u -r1.1 small.spec
--- data/isotrident/small.spec  2 May 2002 05:46:58 -0000       1.1
+++ data/isotrident/small.spec  30 May 2004 23:21:17 -0000
@@ -70,9 +70,9 @@
 
 ; Citizen icons:
 
-  0, 23, "citizen.entertainer"
+  0, 23, "citizen.elvis"
   0, 24, "citizen.scientist"
-  0, 25, "citizen.tax_collector"
+  0, 25, "citizen.taxman"
   0, 26, "citizen.content_0"
   0, 27, "citizen.content_1"
   0, 28, "citizen.happy_0"
Index: data/misc/small.spec
===================================================================
RCS file: /home/freeciv/CVS/freeciv/data/misc/small.spec,v
retrieving revision 1.4
diff -u -r1.4 small.spec
--- data/misc/small.spec        25 Sep 2001 19:58:13 -0000      1.4
+++ data/misc/small.spec        30 May 2004 23:21:17 -0000
@@ -70,9 +70,9 @@
 
 ; Citizen icons:
 
-  0, 23, "citizen.entertainer"
+  0, 23, "citizen.elvis"
   0, 24, "citizen.scientist"
-  0, 25, "citizen.tax_collector"
+  0, 25, "citizen.taxman"
   0, 26, "citizen.content_0"
   0, 27, "citizen.content_1"
   0, 28, "citizen.happy_0"
Index: server/cityhand.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/cityhand.c,v
retrieving revision 1.130
diff -u -r1.130 cityhand.c
--- server/cityhand.c   29 May 2004 20:57:16 -0000      1.130
+++ server/cityhand.c   30 May 2004 23:21:17 -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);
 
@@ -74,9 +73,7 @@
 
   if (to < 0 || to >= SP_COUNT
       || from < 0 || from >= SP_COUNT
-      || (to == SP_ELVIS && pcity->size < game.rgame.min_size_elvis)
-      || (to == SP_TAXMAN && pcity->size < game.rgame.min_size_taxman)
-      || (to == SP_SCIENTIST && pcity->size < game.rgame.min_size_scientist)
+      || pcity->size < game.rgame.specialists[to].min_size
       || pcity->specialists[from] == 0) {
     freelog(LOG_ERROR, "Error in specialist change request from client.");
     return;
@@ -108,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.259
diff -u -r1.259 citytools.c
--- server/citytools.c  27 May 2004 22:14:19 -0000      1.259
+++ server/citytools.c  30 May 2004 23:21:17 -0000
@@ -1687,9 +1687,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];
@@ -2148,7 +2148,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.249
diff -u -r1.249 cityturn.c
--- server/cityturn.c   29 May 2004 20:34:31 -0000      1.249
+++ server/cityturn.c   30 May 2004 23:21:18 -0000
@@ -376,6 +376,8 @@
 **************************************************************************/
 bool city_reduce_size(struct city *pcity, int pop_loss)
 {
+  int i;
+
   if (pop_loss == 0) {
     return TRUE;
   }
@@ -394,16 +396,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) {
@@ -437,6 +438,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 */
@@ -497,18 +499,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.50
diff -u -r1.50 report.c
--- server/report.c     11 May 2004 17:59:34 -0000      1.50
+++ server/report.c     30 May 2004 23:21:18 -0000
@@ -496,19 +496,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)
@@ -902,9 +898,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.173
diff -u -r1.173 ruleset.c
--- server/ruleset.c    23 May 2004 17:06:42 -0000      1.173
+++ server/ruleset.c    30 May 2004 23:21:18 -0000
@@ -2490,23 +2490,36 @@
   char **styles, *replacement;
   int i, nval;
   const char *filename = secfile_filename(file);
+  char **spec_names;
 
   (void) check_ruleset_capabilities(file, "+1.9", filename);
 
   /* Specialist options */
 
-  game.rgame.min_size_elvis = 
-    secfile_lookup_int_default(file, 0, "specialist.min_size_elvis");
-  game.rgame.min_size_taxman = 
-    secfile_lookup_int_default(file, 5, "specialist.min_size_taxman");
-  game.rgame.min_size_scientist = 
-    secfile_lookup_int_default(file, 5, "specialist.min_size_scientist");
-  game.rgame.base_elvis = 
-    secfile_lookup_int_default(file, 2, "specialist.base_elvis");
-  game.rgame.base_scientist = 
-    secfile_lookup_int_default(file, 3, "specialist.base_scientist");
-  game.rgame.base_taxman = 
-    secfile_lookup_int_default(file, 3, "specialist.base_taxman");
+  spec_names = secfile_lookup_str_vec(file, &nval, "specialist.types");
+
+  assert(nval <= MAX_NUM_SPECIALISTS);
+  game.rgame.default_specialist = -1;
+  for (i = 0; i < nval; i++) {
+    const char *name = spec_names[i];
+    struct specialist_type *s = &game.rgame.specialists[i];
+
+    sz_strlcpy(s->name, name);
+    s->min_size = secfile_lookup_int(file, "specialist.%s_min_size", name);
+    s->bonus_sci = secfile_lookup_int_default(file, 0,
+                                             "specialist.%s_bonus_sci", name);
+    s->bonus_tax = secfile_lookup_int_default(file, 0,
+                                             "specialist.%s_bonus_tax", name);
+    s->bonus_lux = secfile_lookup_int_default(file, 0,
+                                             "specialist.%s_bonus_lux", name);
+
+    if (s->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");
   game.rgame.forced_science = 
@@ -2520,12 +2533,6 @@
     freelog(LOG_FATAL, "Forced taxes do not add up in ruleset!");
     exit(EXIT_FAILURE);
   }
-  if (game.rgame.min_size_elvis > 0 && game.rgame.min_size_taxman > 0
-      && game.rgame.min_size_scientist > 0) {
-    freelog(LOG_FATAL, "At least one specialist must be available without a "
-           "city size restriction!");
-    exit(EXIT_FAILURE);
-  }
 
   /* City Parameters */
 
@@ -3095,12 +3102,15 @@
   int i;
   struct packet_ruleset_game misc_p;
 
-  misc_p.min_size_elvis = game.rgame.min_size_elvis;
-  misc_p.min_size_taxman = game.rgame.min_size_taxman;
-  misc_p.min_size_scientist = game.rgame.min_size_scientist;
-  misc_p.base_elvis = game.rgame.base_elvis;
-  misc_p.base_scientist = game.rgame.base_scientist;
-  misc_p.base_taxman = game.rgame.base_taxman;
+  misc_p.num_specialist_types = game.rgame.num_specialist_types;
+  misc_p.default_specialist = game.rgame.default_specialist;
+  for (i = 0; i < T_COUNT; i++) {
+    sz_strlcpy(misc_p.specialist_name[i], game.rgame.specialists[i].name);
+    misc_p.specialist_min_size[i] = game.rgame.specialists[i].min_size;
+    misc_p.specialist_bonus_sci[i] = game.rgame.specialists[i].bonus_sci;
+    misc_p.specialist_bonus_tax[i] = game.rgame.specialists[i].bonus_tax;
+    misc_p.specialist_bonus_lux[i] = game.rgame.specialists[i].bonus_lux;
+  }
   misc_p.changable_tax = game.rgame.changable_tax;
   misc_p.forced_science = game.rgame.forced_science;
   misc_p.forced_luxury = game.rgame.forced_luxury;
Index: server/sanitycheck.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/sanitycheck.c,v
retrieving revision 1.42
diff -u -r1.42 sanitycheck.c
--- server/sanitycheck.c        28 May 2004 06:47:10 -0000      1.42
+++ server/sanitycheck.c        30 May 2004 23:21:18 -0000
@@ -227,9 +227,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.156
diff -u -r1.156 savegame.c
--- server/savegame.c   29 May 2004 20:01:45 -0000      1.156
+++ server/savegame.c   30 May 2004 23:21:19 -0000
@@ -934,12 +934,11 @@
 
     pcity->steal=secfile_lookup_int(file, "player%d.c%d.steal", plrno, i);
 
-    pcity->specialists[SP_ELVIS]
-      = secfile_lookup_int(file, "player%d.c%d.nelvis", plrno, i);
-    pcity->specialists[SP_SCIENTIST]
-      = secfile_lookup_int(file, "player%d.c%d.nscientist", plrno, i);
-    pcity->specialists[SP_TAXMAN]
-      = secfile_lookup_int(file, "player%d.c%d.ntaxman", plrno, i);
+    for (j = 0; j < SP_COUNT; i++) {
+      pcity->specialists[i]
+       = secfile_lookup_int(file, "player%d.c%d.n%s", plrno, i,
+                            game.rgame.specialists[j].name);
+    }
 
     for (j = 0; j < NUM_TRADEROUTES; j++)
       pcity->trade[j]=secfile_lookup_int(file, "player%d.c%d.traderoute%d",
@@ -1047,7 +1046,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);
@@ -1687,12 +1686,11 @@
                       plrno, i);
     secfile_insert_int(file, pcity->size, "player%d.c%d.size", plrno, i);
     secfile_insert_int(file, pcity->steal, "player%d.c%d.steal", plrno, i);
-    secfile_insert_int(file, pcity->specialists[SP_ELVIS],
-                      "player%d.c%d.nelvis", plrno, i);
-    secfile_insert_int(file, pcity->specialists[SP_SCIENTIST],
-                      "player%d.c%d.nscientist", plrno, i);
-    secfile_insert_int(file, pcity->specialists[SP_TAXMAN],
-                      "player%d.c%d.ntaxman", plrno, i);
+    for (j = 0; j < SP_COUNT; j++) {
+      secfile_insert_int(file, pcity->specialists[j],
+                        "player%d.c%d.n%s", plrno, i,
+                        game.rgame.specialists[j].name);
+    }
 
     for (j = 0; j < NUM_TRADEROUTES; j++)
       secfile_insert_int(file, pcity->trade[j], "player%d.c%d.traderoute%d", 
@@ -1938,7 +1936,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.5
diff -u -r1.5 score.c
--- server/score.c      27 May 2004 22:14:19 -0000      1.5
+++ server/score.c      30 May 2004 23:21:19 -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.299
diff -u -r1.299 unithand.c
--- server/unithand.c   27 May 2004 22:14:19 -0000      1.299
+++ server/unithand.c   30 May 2004 23:21:19 -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]
  • [Freeciv-Dev] (PR#8877) RFC: design for generalized specialists, Jason Short <=