Complete.Org: Mailing Lists: Archives: freeciv-dev: January 2006:
[Freeciv-Dev] (PR#14763) Advanced govt patch
Home

[Freeciv-Dev] (PR#14763) Advanced govt patch

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
Subject: [Freeciv-Dev] (PR#14763) Advanced govt patch
From: "Per I. Mathisen" <per@xxxxxxxxxxx>
Date: Sun, 15 Jan 2006 07:06:42 -0800
Reply-to: bugs@xxxxxxxxxxx

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

As a preface, I'd like to say that this patch really shows in places how
more generalization comes at a cost in code complexity. Take a quick look
at what had to be done to make the AI enter anarchy in order to declare
war, and meditate upon the insuffiency of the even more complicated
helptext code.

That said, the government code has been really simplified overall
with the removal of revolution and addition of entry_from and min_turns,
that give more or less the same effect.

The AI tax cheating has been removed. I can put it back in done
differently but I am not sure I want to. The less cheating the better.

The AI settler code was assuming our target government when finding
places to put its cities - this has been removed (this hack will not work
now that tile changes from governments are due to effects, not governments
directly).

EFT_MAX_RATES now handles outputtype requirements, so that we can
drop the special casing of anarchy's tax rates. This fine-grained
ontrol is not shown in the client in any way (only tells user about
overall EFT_MAX_RATES), so modpack usability at the moment is limited.
However, it is used to generalize and simplify some nasty code.

I #ifdef'ed out some report code, also not sure how to fix it. Removed
ruler titles. This does not fit into the new government scheme.
Martial_Law_Max of zero now means no limit, instead of no martial law -
uses Martial_Law_Each of zero for no martial law instead.

The patch is still quite unfinished, but it works for the default
(current) ruleset, and it got so huge that I think it needs to go in as
separate pieces. It lacks a good GUI for selecting governments, it lacks
some needed effects, and needs tons of tweaking of the new government
ruleset. The "steps" have not been coded yet.

My plan is to get this beast into svn after the branching for 2.1. I could
try to split out smaller parts to commit first, although I am not sure how
much could be committed this way. I would like to commit the patch without
the new ruleset first, then add new effects, GUI and steps code later.

  - Per

Index: server/srv_main.c
===================================================================
--- server/srv_main.c   (revision 11446)
+++ server/srv_main.c   (working copy)
@@ -455,6 +455,25 @@
 }
 
 /**************************************************************************
+  Check for min_turn requirements running out in governments.  If so,
+  be helpful and notify player.
+**************************************************************************/
+static void update_governments(void)
+{
+  players_iterate(plr) {
+    gov_category_iterate(pcat) {
+      struct government *pgov = plr->govs[pcat->index].current_government;
+      if (pgov->min_turns != 0
+          && plr->govs[pcat->index].turn_changed + pgov->min_turns 
+             == game.info.turn) {
+          notify_player(plr, NULL, E_NEW_GOVERNMENT,
+                        _("You may now change your government."));
+      }
+    } gov_category_iterate_end;
+  } players_iterate_end;
+}
+
+/**************************************************************************
   Check for cease-fires and armistices running out; update cancelling 
   reasons and contact information.
 **************************************************************************/
@@ -621,10 +640,6 @@
   flush_packets();  /* to curb major city spam */
   conn_list_do_unbuffer(game.est_connections);
 
-  phase_players_iterate(pplayer) {
-    update_revolution(pplayer);
-  } phase_players_iterate_end;
-
   if (is_new_phase) {
     /* Try to avoid hiding events under a diplomacy dialog */
     phase_players_iterate(pplayer) {
@@ -739,6 +754,7 @@
                             &game.info.nuclearwinter, &game.info.coolinglevel,
                             nuclear_winter);
   update_diplomatics();
+  update_governments();
   make_history_report();
   stdinhand_turn();
   send_player_turn_notifications(NULL);
@@ -1714,6 +1730,7 @@
   if (!with_ggz) {
     server_open_socket();
   }
+  init_game_seed(); /* for access to myrand in pregame */
 
   /* load a saved game */
   if (srvarg.load_filename[0] != '\0') {
@@ -1787,14 +1804,21 @@
 {
   players_iterate(pplayer) {
     struct nation_type *pnation = get_nation_by_plr(pplayer);
+    struct government *init_gov = pnation->init_government;
 
-    pplayer->government = pnation->init_government;
+    gov_category_iterate(pcat) {
+      struct plr_gov *plrgov = &pplayer->govs[pcat->index];
 
-    if (pnation->init_government == game.government_when_anarchy) {
-      /* If we do not do this, an assert will trigger. This enables us to
-       * select a valid government on game start. */
-      pplayer->revolution_finishes = 0;
+      plrgov->current_government = pcat->anarchy_government;
+      plrgov->turn_changed = game.info.turn;
+      plrgov->current_step = 0;
+    } gov_category_iterate_end;
+    if (init_gov) {
+      struct plr_gov *plrgov = &pplayer->govs[init_gov->category->index];
+
+      plrgov->current_government = init_gov;
     }
+    player_limit_to_government_rates(pplayer);
   } players_iterate_end;
 }
 
@@ -1916,7 +1940,7 @@
     } players_iterate_end;
   } else {
     players_iterate(pplayer) {
-      ai_data_init(pplayer); /* Initialize this at last moment */
+      ai_data_init(pplayer); /* Initialize this again to be sure */
     } players_iterate_end;
   }
 
Index: server/settings.c
===================================================================
--- server/settings.c   (revision 11446)
+++ server/settings.c   (working copy)
@@ -731,16 +731,6 @@
          GAME_MIN_ONSETBARBARIAN, GAME_MAX_ONSETBARBARIAN, 
          GAME_DEFAULT_ONSETBARBARIAN)
 
-  GEN_INT("revolen", game.info.revolution_length,
-         SSET_RULES_FLEXIBLE, SSET_SOCIOLOGY, SSET_RARE, SSET_TO_CLIENT,
-         N_("Length in turns of revolution"),
-         N_("When changing governments, a period of anarchy lasting this "
-            "many turns will occur. "
-             "Setting this value to 0 will give a random "
-             "length of 1-6 turns."), NULL, 
-         GAME_MIN_REVOLUTION_LENGTH, GAME_MAX_REVOLUTION_LENGTH, 
-         GAME_DEFAULT_REVOLUTION_LENGTH)
-
   GEN_BOOL("fogofwar", game.info.fogofwar,
           SSET_RULES_FLEXIBLE, SSET_MILITARY, SSET_RARE, SSET_TO_CLIENT,
           N_("Whether to enable fog of war"),
Index: server/settlers.c
===================================================================
--- server/settlers.c   (revision 11446)
+++ server/settlers.c   (working copy)
@@ -790,8 +790,7 @@
 static int unit_food_upkeep(struct unit *punit)
 {
   struct player *pplayer = unit_owner(punit);
-  int upkeep = utype_upkeep_cost(unit_type(punit), pplayer,
-                                get_gov_pplayer(pplayer), O_FOOD);
+  int upkeep = utype_upkeep_cost(unit_type(punit), pplayer, O_FOOD);
   if (punit->id != 0 && punit->homecity == 0)
     upkeep = 0; /* thanks, Peter */
 
Index: server/report.c
===================================================================
--- server/report.c     (revision 11446)
+++ server/report.c     (working copy)
@@ -450,17 +450,17 @@
 
 static int get_taxrate(struct player *pplayer)
 {
-  return pplayer->economic.tax;
+  return pplayer->economic.rates[O_GOLD];
 }
 
 static int get_scirate(struct player *pplayer)
 {
-  return pplayer->economic.science;
+  return pplayer->economic.rates[O_SCIENCE];
 }
 
 static int get_luxrate(struct player *pplayer)
 {
-  return pplayer->economic.luxury;
+  return pplayer->economic.rates[O_LUXURY];
 }
 
 static int get_riots(struct player *pplayer)
@@ -504,7 +504,11 @@
 
 static int get_gov(struct player *pplayer)
 {
+#if 0
+/* I have no idea how to fix this. - Per */
   return pplayer->government->index;
+#endif
+  return 0;
 }
 
 static int get_corruption(struct player *pplayer)
@@ -737,8 +741,7 @@
     return;
   }
 
-  my_snprintf(civbuf, sizeof(civbuf), _("The %s of the %s (%s)"),
-             get_government_name(pplayer->government),
+  my_snprintf(civbuf, sizeof(civbuf), _("The civilization of the %s (%s)"),
              get_nation_name_plural(pplayer->nation),
              textyear(game.info.year));
 
Index: server/barbarian.c
===================================================================
--- server/barbarian.c  (revision 11446)
+++ server/barbarian.c  (working copy)
@@ -156,12 +156,10 @@
 
   sz_strlcpy(barbarians->username, ANON_USER_NAME);
   barbarians->is_connected = FALSE;
-  barbarians->government = nation->init_government;
-  assert(barbarians->revolution_finishes < 0);
   barbarians->capital = FALSE;
   barbarians->economic.gold = 100;
-
   barbarians->phase_done = TRUE;
+  handle_player_change_government(barbarians, nation->init_government->index);
 
   /* Do the ai */
   barbarians->ai.control = TRUE;
Index: server/cityturn.c
===================================================================
--- server/cityturn.c   (revision 11446)
+++ server/cityturn.c   (working copy)
@@ -990,11 +990,9 @@
 static bool city_distribute_surplus_shields(struct player *pplayer,
                                            struct city *pcity)
 {
-  struct government *g = get_gov_pplayer(pplayer);
-
   if (pcity->surplus[O_SHIELD] < 0) {
     unit_list_iterate_safe(pcity->units_supported, punit) {
-      if (utype_upkeep_cost(unit_type(punit), pplayer, g, O_SHIELD) > 0
+      if (utype_upkeep_cost(unit_type(punit), pplayer, O_SHIELD) > 0
          && pcity->surplus[O_SHIELD] < 0
           && !unit_flag(punit, F_UNDISBANDABLE)) {
        notify_player(pplayer, pcity->tile, E_UNIT_LOST,
@@ -1012,7 +1010,7 @@
      * it! If we make it here all normal units are already disbanded, so only
      * undisbandable ones remain. */
     unit_list_iterate_safe(pcity->units_supported, punit) {
-      int upkeep = utype_upkeep_cost(unit_type(punit), pplayer, g, O_SHIELD);
+      int upkeep = utype_upkeep_cost(unit_type(punit), pplayer, O_SHIELD);
 
       if (upkeep > 0 && pcity->surplus[O_SHIELD] < 0) {
        assert(unit_flag(punit, F_UNDISBANDABLE));
@@ -1261,14 +1259,15 @@
 }
 
 /**************************************************************************
-...
+  Pay for upkeep costs for all buildings, or sell them.
 **************************************************************************/
 static void pay_for_buildings(struct player *pplayer, struct city *pcity)
 {
   built_impr_iterate(pcity, i) {
-    if (can_city_sell_building(pcity, i)
-       && pplayer->government != game.government_when_anarchy) {
-      if (pplayer->economic.gold - improvement_upkeep(pcity, i) < 0) {
+    if (can_city_sell_building(pcity, i)) {
+      int upkeep = improvement_upkeep(pcity, i);
+
+      if (pplayer->economic.gold - upkeep < 0) {
        notify_player(pplayer, pcity->tile, E_IMP_AUCTIONED,
                         _("Can't afford to maintain %s in %s, "
                           "building sold!"),
@@ -1276,7 +1275,7 @@
        do_sell_building(pplayer, pcity, i);
        city_refresh(pcity);
       } else
-       pplayer->economic.gold -= improvement_upkeep(pcity, i);
+        pplayer->economic.gold -= upkeep;
     }
   } built_impr_iterate_end;
 }
@@ -1323,7 +1322,6 @@
 **************************************************************************/
 int city_incite_cost(struct player *pplayer, struct city *pcity)
 {
-  struct government *g = get_gov_pcity(pcity);
   struct city *capital;
   int dist, size, cost;
 
@@ -1345,14 +1343,12 @@
   } built_impr_iterate_end;
 
   /* Stability bonuses */
-  if (g != game.government_when_anarchy) {
-    if (!city_unhappy(pcity)) {
-      cost *= 2;
-    }
-    if (city_celebrating(pcity)) {
-      cost *= 2;
-    }
+  if (!city_unhappy(pcity)) {
+    cost *= 2;
   }
+  if (city_celebrating(pcity)) {
+    cost *= 2;
+  }
 
   /* City is empty */
   if (unit_list_size(pcity->tile->units) == 0) {
@@ -1441,8 +1437,6 @@
 **************************************************************************/
 static void update_city_activity(struct player *pplayer, struct city *pcity)
 {
-  struct government *g = get_gov_pcity(pcity);
-
   city_refresh(pcity);
 
   /* reporting of celebrations rewritten, copying the treatment of disorder 
below,
@@ -1450,20 +1444,16 @@
   if (city_build_stuff(pplayer, pcity)) {
     if (city_celebrating(pcity)) {
       pcity->rapture++;
-      if (pcity->rapture == 1)
-       notify_player(pplayer, pcity->tile, E_CITY_LOVE,
-                        _("We Love The %s Day celebrated in %s."), 
-                        get_ruler_title(pplayer->government, pplayer->is_male,
-                                        pplayer->nation),
-                        pcity->name);
-    }
-    else {
-      if (pcity->rapture != 0)
-       notify_player(pplayer, pcity->tile, E_CITY_NORMAL,
-                        _("We Love The %s Day canceled in %s."),
-                        get_ruler_title(pplayer->government, pplayer->is_male,
-                                        pplayer->nation),
-                        pcity->name);
+      if (pcity->rapture == 1) {
+        notify_player(pplayer, pcity->tile, E_CITY_LOVE,
+                      _("Wild celebrations in your honour in %s."),
+                      pcity->name);
+      }
+    } else {
+      if (pcity->rapture != 0) {
+        notify_player(pplayer, pcity->tile, E_CITY_NORMAL,
+                      _("Celebrations cancelled in %s."), pcity->name);
+      }
       pcity->rapture=0;
     }
     pcity->was_happy=city_happy(pcity);
@@ -1508,10 +1498,15 @@
     if (pcity->anarchy>2 
         && get_player_bonus(pplayer, EFT_REVOLUTION_WHEN_UNHAPPY) > 0) {
       notify_player(pplayer, pcity->tile, E_ANARCHY,
-                      _("The people have overthrown your %s, "
-                        "your country is in turmoil."),
-                      get_government_name(g));
-      handle_player_change_government(pplayer, g->index);
+                    _("The people have overthrown your government, "
+                      "your country is in turmoil."));
+      gov_category_iterate(pcat) {
+        struct government *gov = pcat->anarchy_government;
+
+        if (gov) {
+          handle_player_change_government(pplayer, gov->index);
+        }
+      } gov_category_iterate_end;
     }
     sanity_check_city(pcity);
   }
Index: server/ruleset.c
===================================================================
--- server/ruleset.c    (revision 11446)
+++ server/ruleset.c    (working copy)
@@ -1664,9 +1664,9 @@
 **************************************************************************/
 static void load_government_names(struct section_file *file)
 {
-  int nval;
-  char **sec;
+  char **sec, **slist;
   const char *filename = secfile_filename(file);
+  int j, nval;
 
   (void) section_file_lookup(file, "datafile.description");    /* unused */
 
@@ -1693,6 +1693,47 @@
     gov->name = gov->name_orig;
   } government_iterate_end;
   free(sec);
+
+  slist = secfile_lookup_str_vec(file, &nval, "governments.categories");
+  gov_categories_alloc(nval);
+  for (j = 0; j < nval; j++) {
+    char *sval = slist[j];
+    struct gov_category *pcat;
+
+    if (strcmp(sval, "") == 0) {
+      continue;
+    }
+    if (j == MAX_NUM_CATEGORIES) {
+      freelog(LOG_FATAL, "Too many government categories (max %d)",
+              MAX_NUM_CATEGORIES);
+      exit(EXIT_FAILURE);
+    }
+    pcat = &gov_categories[j];
+    name_strlcpy(pcat->name_orig, sval);
+    sz_strlcpy(pcat->name, sval);
+  }
+  free(slist);
+
+  slist = secfile_lookup_str_vec(file, &nval, "governments.when_anarchy");
+  if (nval != game.control.gov_categories_count) {
+    freelog(LOG_FATAL, "Missing governments.when_anarchy entries");
+    exit(EXIT_FAILURE);
+  }
+  for (j = 0; j < nval; j++) {
+    char *sval = slist[j];
+    struct gov_category *pcat = &gov_categories[j];
+
+    if (strcmp(sval, "") == 0) {
+      continue;
+    }
+    pcat->anarchy_government = find_government_by_name(sval);
+    if (!pcat->anarchy_government) {
+      freelog(LOG_FATAL, "%s: in anarchy list, %s couldn't match any "
+              "government", filename, sval);
+      exit(EXIT_FAILURE);
+    }
+  }
+  free(slist);
 }
 
 /**************************************************************************
@@ -1708,15 +1749,21 @@
 
   sec = secfile_get_secnames_prefix(file, "government_", &nval);
 
-  game.government_when_anarchy
-    = lookup_government(file, "governments.when_anarchy", filename);
-  game.info.government_when_anarchy_id = game.government_when_anarchy->index;
-
   /* easy ones: */
   government_iterate(g) {
     int i = g->index;
     struct requirement_vector *reqs = lookup_req_list(file, sec[i], "reqs");
+    char *tmp = NULL;
 
+    g->min_turns = secfile_lookup_int_default(file, 0, "%s.min_turns", 
+                                              sec[i]);
+    tmp = secfile_lookup_str(file, "%s.category", sec[i]);
+    g->category = find_gov_category_by_name_orig(tmp);
+    if (g->category == NULL) {
+      freelog(LOG_FATAL, "%s: Could not find category %s for government %s",
+              filename, tmp, g->name);
+      exit(EXIT_FAILURE);
+    }
     if (section_file_lookup(file, "%s.ai_better", sec[i])) {
       char entry[100];
 
@@ -1725,6 +1772,14 @@
     } else {
       g->ai.better = NULL;
     }
+    if (section_file_lookup(file, "%s.entry_from", sec[i])) {
+      char entry[100];
+
+      my_snprintf(entry, sizeof(entry), "%s.entry_from", sec[i]);
+      g->entry_from = lookup_government(file, entry, filename);
+    } else {
+      g->entry_from = NULL;
+    }
     requirement_vector_copy(&g->reqs, reqs);
     
     sz_strlcpy(g->graphic_str,
@@ -1736,24 +1791,6 @@
   } government_iterate_end;
 
   
-  /* titles */
-  government_iterate(g) {
-    int i = g->index;
-    struct ruler_title *title;
-
-    g->num_ruler_titles = 1;
-    g->ruler_titles = fc_calloc(1, sizeof(*g->ruler_titles));
-    title = &(g->ruler_titles[0]);
-
-    title->nation = DEFAULT_TITLE;
-    sz_strlcpy(title->male_title_orig,
-              secfile_lookup_str(file, "%s.ruler_male_title", sec[i]));
-    title->male_title = title->male_title_orig;
-    sz_strlcpy(title->female_title_orig,
-              secfile_lookup_str(file, "%s.ruler_female_title", sec[i]));
-    title->female_title = title->female_title_orig;
-  } government_iterate_end;
-
   free(sec);
   section_file_check_unused(file, filename);
   section_file_free(file);
@@ -1995,9 +2032,8 @@
 **************************************************************************/
 static void load_ruleset_nations(struct section_file *file)
 {
-  char *bad_leader, *g;
+  char *bad_leader;
   struct nation_type *pl;
-  struct government *gov;
   int dim, i, j, k, nval, numgroups;
   char temp_name[MAX_LEN_NAME];
   char **leaders, **sec, **civilwar_nations, **groups, **conflicts;
@@ -2018,8 +2054,6 @@
   sec = secfile_get_secnames_prefix(file, "nation", &nval);
 
   for (i = 0; i < game.control.nation_count; i++) {
-    char tmp[200] = "\0";
-
     pl = get_nation_by_idx(i);
     
     groups = secfile_lookup_str_vec(file, &dim, "%s.groups", sec[i]);
@@ -2108,34 +2142,6 @@
     sz_strlcpy(pl->flag_graphic_alt,
               secfile_lookup_str(file, "%s.flag_alt", sec[i]));
 
-    /* Ruler titles */
-
-    j = -1;
-    while ((g = secfile_lookup_str_default(file, NULL,
-                                          "%s.ruler_titles%d.government",
-                                          sec[i], ++j))) {
-      char *male_name;
-      char *female_name;
-      
-      male_name = secfile_lookup_str(file, "%s.ruler_titles%d.male_title",
-                                    sec[i], j);
-      female_name = secfile_lookup_str(file, "%s.ruler_titles%d.female_title",
-                                      sec[i], j);
-
-      gov = find_government_by_name(g);
-      if (gov) {
-       check_name(male_name);
-       check_name(female_name);
-       /* Truncation is handled by set_ruler_title(). */
-       set_ruler_title(gov, pl, male_name, female_name);
-      } else {
-       /* LOG_VERBOSE rather than LOG_ERROR so that can use single nation
-          ruleset file with variety of government ruleset files: */
-        freelog(LOG_VERBOSE,
-               "Nation %s: government %s not found", pl->name, g);
-      }
-    }
-
     /* City styles */
 
     sz_strlcpy(temp_name,
@@ -2200,10 +2206,21 @@
                         filename);
     lookup_unit_list(file, sec[i], "init_units", pl->init_units, filename,
                      FALSE);
-    mystrlcat(tmp, sec[i], 200);
-    mystrlcat(tmp, ".init_government", 200);
-    pl->init_government = lookup_government(file, tmp, filename);
+    if (section_file_lookup(file, "%s.init_government", sec[i])) {
+      /* Accept wrong governments in init_government so that we can
+       * reuse the nations as much as possible. Yes I know - yuck. */
+      char *sval;
+      struct government *gov;
 
+      sval = secfile_lookup_str(file, "%s.init_government", sec[i]);
+      gov = find_government_by_name(sval);
+      if (gov) {
+        pl->init_government = gov;
+      } else {
+        pl->init_government = NULL;
+      }
+    }
+
     /* read "normal" city names */
 
     pl->city_names = load_city_name_list(file, sec[i], ".cities");
@@ -2847,13 +2864,13 @@
 
 /**************************************************************************
   Send the government ruleset information to the specified connections.
-  One packet per government type, and for each type one per ruler title.
+  One packet per government type.
 **************************************************************************/
 static void send_ruleset_governments(struct conn_list *dest)
 {
   struct packet_ruleset_government gov;
-  struct packet_ruleset_government_ruler_title title;
-  struct ruler_title *p_title;
+  struct packet_ruleset_gov_category packcat;
+  struct gov_category *pcat;
   int j;
 
   government_iterate(g) {
@@ -2866,7 +2883,13 @@
     } requirement_vector_iterate_end;
     gov.reqs_count = j;
 
-    gov.num_ruler_titles = g->num_ruler_titles;
+    gov.min_turns = g->min_turns;
+    gov.category = g->category->index;
+    if (g->entry_from) {
+      gov.entry_from = g->entry_from->index;
+    } else {
+      gov.entry_from = -1;
+    }
 
     sz_strlcpy(gov.name, g->name_orig);
     sz_strlcpy(gov.graphic_str, g->graphic_str);
@@ -2879,20 +2902,25 @@
     }
       
     lsend_packet_ruleset_government(dest, &gov);
-    
-    /* send one packet_government_ruler_title per ruler title */
-    for(j=0; j<g->num_ruler_titles; j++) {
-      p_title = &g->ruler_titles[j];
+  } government_iterate_end;
 
-      title.gov = g->index;
-      title.id = j;
-      title.nation = p_title->nation ? p_title->nation->index : -1;
-      sz_strlcpy(title.male_title, p_title->male_title);
-      sz_strlcpy(title.female_title, p_title->female_title);
-    
-      lsend_packet_ruleset_government_ruler_title(dest, &title);
+  for (j = 0; j < game.control.gov_categories_count; j++) {
+    int k;
+
+    pcat = &gov_categories[j];
+    packcat.index = j;
+    packcat.colour = pcat->background_colour;
+    if (pcat->anarchy_government != NULL) {
+      packcat.anarchy_government = pcat->anarchy_government->index;
+    } else {
+      packcat.anarchy_government = -1;
     }
-  } government_iterate_end;
+    sz_strlcpy(packcat.name, pcat->name);
+    for (k = 0; k < MAX_NUM_GOV_STEPS; k++) {
+      packcat.steps[k] = pcat->steps[k];
+    }
+    lsend_packet_ruleset_gov_category(dest, &packcat);
+  }
 }
 
 /**************************************************************************
@@ -2929,7 +2957,11 @@
            sizeof(packet.init_buildings));
     memcpy(packet.init_units, n->init_units, 
            sizeof(packet.init_units));
-    packet.init_government = n->init_government->index;
+    if (n->init_government) {
+      packet.init_government = n->init_government->index;
+    } else {
+      packet.init_government = -1;
+    }
 
     sz_strlcpy(packet.legend, n->legend);
 
Index: server/sanitycheck.c
===================================================================
--- server/sanitycheck.c        (revision 11446)
+++ server/sanitycheck.c        (working copy)
@@ -129,10 +129,6 @@
                   <= plr_tile->own_seen[V_MAIN]);
     } players_iterate_end;
   } whole_map_iterate_end;
-
-  SANITY_CHECK(game.government_when_anarchy != NULL);
-  SANITY_CHECK(game.government_when_anarchy
-              == get_government(game.info.government_when_anarchy_id));
 }
 
 /**************************************************************************
@@ -474,19 +470,6 @@
                      != DIPL_ALLIANCE_PROBLEM);
       }
     } players_iterate_end;
-
-    if (pplayer->revolution_finishes == -1) {
-      if (pplayer->government == game.government_when_anarchy) {
-        freelog(LOG_FATAL, "%s's government is anarchy but does not finish",
-                pplayer->name);
-      }
-      SANITY_CHECK(pplayer->government != game.government_when_anarchy);
-    } else if (pplayer->revolution_finishes > game.info.turn) {
-      SANITY_CHECK(pplayer->government == game.government_when_anarchy);
-    } else {
-      /* Things may vary in this case depending on when the sanity_check
-       * call is made.  No better check is possible. */
-    }
   } players_iterate_end;
 
   /* Sanity checks on living and dead players. */
Index: server/savegame.c
===================================================================
--- server/savegame.c   (revision 11446)
+++ server/savegame.c   (working copy)
@@ -200,7 +200,7 @@
 " diplchance_percent map_editor known32fix turn " \
 "attributes watchtower rulesetdir client_worklists orders " \
 "startunits turn_last_built improvement_order technology_order embassies " \
-"new_owner_map resources"
+"new_owner_map resources new_owner_map advgovt"
 
 static const char hex_chars[] = "0123456789abcdef";
 
@@ -1242,19 +1242,6 @@
   "Writing"
 };
 
-/* old (~1.14.1) government order in default, civ1, and history rulesets */
-const char* old_default_governments[] = 
-{
-  "Anarchy", "Despotism", "Monarchy", "Communism", "Republic", "Democracy"
-};
-
-/* old (~1.14.1) government order in the civ2 ruleset */
-const char* old_civ2_governments[] =
-{
-  "Anarchy", "Despotism", "Monarchy", "Communism", "Fundamentalism",
-  "Republic", "Democracy"
-};
-
 /****************************************************************************
   Nowadays unit types are saved by name, but old servers need the
   unit_type_id.  This function tries to find the correct _old_ id for the
@@ -1560,61 +1547,6 @@
 }
 
 /****************************************************************************
-  Nowadays governments are saved by name, but old servers need the
-  index.  This function tries to find the correct _old_ id for the
-  government. It is used when the government is saved.
-****************************************************************************/
-static int old_government_id(struct government *gov)
-{
-  const char** names;
-  int num_names, i;
-
-  if (strcmp(game.rulesetdir, "civ2") == 0) {
-    names = old_civ2_governments;
-    num_names = ARRAY_SIZE(old_civ2_governments);
-  } else {
-    names = old_default_governments;
-    num_names = ARRAY_SIZE(old_default_governments);
-  }
-
-  for (i = 0; i < num_names; i++) {
-    if (mystrcasecmp(gov->name_orig, names[i]) == 0) {
-      return i;
-    }
-  }
-
-  /* It's a new government. Savegame cannot be forward compatible so we can
-   * return anything */
-  return gov->index;
-}
-
-/****************************************************************************
-  Convert an old-style government index into a government name.
-****************************************************************************/
-static const char* old_government_name(int id)
-{
-  /* before 1.15.0 governments used to be saved by index */
-  if (id < 0) {
-    freelog(LOG_ERROR, _("Wrong government type id value (%d)"), id);
-    exit(EXIT_FAILURE);
-  }
-  /* Different rulesets had different governments. */
-  if (strcmp(game.rulesetdir, "civ2") == 0) {
-    if (id >= ARRAY_SIZE(old_civ2_governments)) {
-      freelog(LOG_ERROR, _("Wrong government type id value (%d)"), id);
-      exit(EXIT_FAILURE);
-    }
-    return old_civ2_governments[id];
-  } else {
-    if (id >= ARRAY_SIZE(old_default_governments)) {
-      freelog(LOG_ERROR, _("Wrong government type id value (%d)"), id);
-      exit(EXIT_FAILURE);
-    }
-    return old_default_governments[id];
-  }
-}
-
-/****************************************************************************
   Loads the units for the given player.
 ****************************************************************************/
 static void load_player_units(struct player *plr, int plrno,
@@ -1846,7 +1778,6 @@
   const char *name;
   char *savefile_options = secfile_lookup_str(file, "savefile.options");
   struct ai_data *ai;
-  struct government *gov;
   int id;
   struct team *pteam;
   struct player_research *research;
@@ -1920,36 +1851,31 @@
     plr->nation = pick_barbarian_nation();
   }
 
-  /* government */
-  name = secfile_lookup_str_default(file, NULL, "player%d.government_name",
-                                    plrno);
-  if (!name) {
-    /* old servers used to save government by id */
-    id = secfile_lookup_int(file, "player%d.government", plrno);
-    name = old_government_name(id);
-  }
-  gov = find_government_by_name_orig(name);
-  if (gov == NULL) {
-    freelog(LOG_ERROR, _("Unsupported government found (%s)"), name);
-    exit(EXIT_FAILURE);
-  }
-  plr->government = gov;
+  if (has_capability("advgovt", savefile_options)) {
+    gov_category_iterate(pcat) {
+      /* Load government info */
+      struct government *gov;
+      int i;
 
-  /* Target government */
-  name = secfile_lookup_str_default(file, NULL,
-                                   "player%d.target_government_name",
-                                   plrno);
-  if (name) {
-    gov = find_government_by_name_orig(name);
+      name = secfile_lookup_str(file, "player%d.%s.current_government",
+                                plrno, pcat->name);
+      gov = find_government_by_name_orig(name);
+      plr->govs[pcat->index].current_government = gov;
+      i = secfile_lookup_int(file, "player%d.%s.turn_changed", plrno, 
+                             pcat->name);
+      plr->govs[pcat->index].turn_changed = i;
+      i = secfile_lookup_int(file, "player%d.%s.current_step", plrno,
+                             pcat->name);
+      plr->govs[pcat->index].current_step = i;
+    } gov_category_iterate_end;
   } else {
-    gov = NULL;
+    /* Start out in anarchy */
+    gov_category_iterate(pcat) {
+      plr->govs[pcat->index].current_government = pcat->anarchy_government;
+      plr->govs[pcat->index].turn_changed = game.info.turn;
+      plr->govs[pcat->index].current_step = 0;
+    } gov_category_iterate_end;
   }
-  if (gov) {
-    plr->target_government = gov;
-  } else {
-    /* Old servers didn't have this value. */
-    plr->target_government = plr->government;
-  }
 
   BV_CLR_ALL(plr->embassy);
   if (has_capability("embassies", savefile_options)) {
@@ -2044,9 +1970,9 @@
   }
 
   plr->economic.gold=secfile_lookup_int(file, "player%d.gold", plrno);
-  plr->economic.tax=secfile_lookup_int(file, "player%d.tax", plrno);
-  plr->economic.science=secfile_lookup_int(file, "player%d.science", plrno);
-  plr->economic.luxury=secfile_lookup_int(file, "player%d.luxury", plrno);
+  plr->economic.rates[O_GOLD] = secfile_lookup_int(file, "player%d.tax", 
plrno);
+  plr->economic.rates[O_SCIENCE] = secfile_lookup_int(file, 
"player%d.science", plrno);
+  plr->economic.rates[O_LUXURY] = secfile_lookup_int(file, "player%d.luxury", 
plrno);
   
   plr->bulbs_last_turn =
     secfile_lookup_int_default(file, 0,
@@ -2107,31 +2033,6 @@
   }
     
   plr->capital = secfile_lookup_bool(file, "player%d.capital", plrno);
-
-  {
-    /* The old-style "revolution" value indicates the number of turns until
-     * the revolution is complete, or 0 if there is no revolution.  The
-     * new-style "revolution_finishes" value indicates the turn in which
-     * the revolution will complete (which may be less than the current
-     * turn) or -1 if there is no revolution. */
-    int revolution = secfile_lookup_int_default(file, 0, "player%d.revolution",
-                                               plrno);
-
-    if (revolution == 0) {
-      if (plr->government != game.government_when_anarchy) {
-        revolution = -1;
-      } else {
-        /* some old savegames may be buggy */
-        revolution = game.info.turn + 1;
-      }
-    } else {
-      revolution = game.info.turn + revolution;
-    }
-    plr->revolution_finishes
-      = secfile_lookup_int_default(file, revolution,
-                                  "player%d.revolution_finishes", plrno);
-  }
-
   update_research(plr);
 
   for (i = 0; i < game.info.nplayers; i++) {
@@ -2706,18 +2607,16 @@
   secfile_insert_int(file, plr->team ? plr->team->index : -1,
                     "player%d.team_no", plrno);
 
-  secfile_insert_str(file, plr->government->name_orig,
-                    "player%d.government_name", plrno);
-  /* 1.15 and later won't use "government" field; it's kept for forward 
-   * compatibility */
-  secfile_insert_int(file, old_government_id(plr->government),
-                     "player%d.government", plrno);
+  gov_category_iterate(pcat) {
+    /* Save government info */
+    secfile_insert_str(file, plr->govs[pcat->index].current_government->name,
+                       "player%d.%s.current_government", plrno, pcat->name);
+    secfile_insert_int(file, plr->govs[pcat->index].current_step,
+                       "player%d.%s.current_step", plrno, pcat->name);
+    secfile_insert_int(file, plr->govs[pcat->index].turn_changed,
+                       "player%d.%s.turn_changed", plrno, pcat->name);
+  } gov_category_iterate_end;
 
-  if (plr->target_government) {
-    secfile_insert_str(file, plr->target_government->name_orig,
-                      "player%d.target_government_name", plrno);
-  }
-
   players_iterate(pother) {
     secfile_insert_bool(file, BV_ISSET(plr->embassy, pother->player_no),
                        "player%d.embassy%d", plrno, pother->player_no);
@@ -2763,9 +2662,9 @@
                     "player%d.ai.skill_level", plrno);
   secfile_insert_int(file, plr->ai.barbarian_type, "player%d.ai.is_barbarian", 
plrno);
   secfile_insert_int(file, plr->economic.gold, "player%d.gold", plrno);
-  secfile_insert_int(file, plr->economic.tax, "player%d.tax", plrno);
-  secfile_insert_int(file, plr->economic.science, "player%d.science", plrno);
-  secfile_insert_int(file, plr->economic.luxury, "player%d.luxury", plrno);
+  secfile_insert_int(file, plr->economic.rates[O_GOLD], "player%d.tax", plrno);
+  secfile_insert_int(file, plr->economic.rates[O_SCIENCE], "player%d.science", 
plrno);
+  secfile_insert_int(file, plr->economic.rates[O_LUXURY], "player%d.luxury", 
plrno);
   
   secfile_insert_int(file, plr->bulbs_last_turn, "player%d.bulbs_last_turn",
                      plrno);
@@ -2788,24 +2687,6 @@
 
   secfile_insert_bool(file, plr->capital, "player%d.capital", plrno);
 
-  secfile_insert_int(file, plr->revolution_finishes,
-                    "player%d.revolution_finishes", plrno);
-  {
-    /* Insert the old-style "revolution" value, for forward compatibility.
-     * See the loading code for more explanation. */
-    int revolution;
-
-    if (plr->revolution_finishes < 0) {
-      /* No revolution. */
-      revolution = 0;
-    } else if (plr->revolution_finishes <= game.info.turn) {
-      revolution = 1; /* Approximation. */
-    } else {
-      revolution = plr->revolution_finishes - game.info.turn;
-    }
-    secfile_insert_int(file, revolution, "player%d.revolution", plrno);
-  }
-
   /* 1.14 servers depend on technology order in ruleset. Here we are trying
    * to simulate 1.14.1 default order */
   init_old_technology_bitvector(invs);
@@ -3600,9 +3481,6 @@
                                                    "game.barbarians");
     game.info.onsetbarbarian = secfile_lookup_int_default(file, 
game.info.onsetbarbarian,
                                                     "game.onsetbarbs");
-    game.info.revolution_length
-      = secfile_lookup_int_default(file, game.info.revolution_length,
-                                  "game.revolen");
     game.info.nbarbarians = 0; /* counted in player_load for compatibility 
with 
                             1.10.0 */
     game.info.occupychance = secfile_lookup_int_default(file, 
game.info.occupychance,
@@ -4129,7 +4007,6 @@
   secfile_insert_bool(file, game.info.fixedlength, "game.fixedlength");
   secfile_insert_int(file, game.info.barbarianrate, "game.barbarians");
   secfile_insert_int(file, game.info.onsetbarbarian, "game.onsetbarbs");
-  secfile_insert_int(file, game.info.revolution_length, "game.revolen");
   secfile_insert_int(file, game.info.occupychance, "game.occupychance");
   secfile_insert_bool(file, game.info.autoattack, "game.autoattack");
   secfile_insert_str(file, game.demography, "game.demography");
Index: server/plrhand.c
===================================================================
--- server/plrhand.c    (revision 11446)
+++ server/plrhand.c    (working copy)
@@ -122,7 +122,8 @@
 /**************************************************************************
   Murder a player in cold blood.
 **************************************************************************/
-void kill_player(struct player *pplayer) {
+void kill_player(struct player *pplayer)
+{
   bool palace;
 
   pplayer->is_dying = FALSE; /* Can't get more dead than this. */
@@ -214,14 +215,12 @@
     } else {
       rtype = _("Science");
     }
-
-    notify_player(pplayer, NULL, E_BAD_COMMAND,
-                 _("%s rate exceeds the max rate for %s."),
-                  rtype, get_government_name(pplayer->government));
+    notify_player(pplayer, NULL, E_BAD_COMMAND, 
+                  _("%s rate exceeds the max rate limits."), rtype);
   } else {
-    pplayer->economic.tax = tax;
-    pplayer->economic.luxury = luxury;
-    pplayer->economic.science = science;
+    pplayer->economic.rates[O_GOLD] = tax;
+    pplayer->economic.rates[O_LUXURY] = luxury;
+    pplayer->economic.rates[O_SCIENCE] = science;
     conn_list_do_buffer(pplayer->connections);
     global_city_refresh(pplayer);
     send_player_info(pplayer, pplayer);
@@ -230,107 +229,29 @@
 }
 
 /**************************************************************************
-  Finish the revolution and set the player's government.  Call this as soon
-  as the player has set a target_government and the revolution_finishes
-  turn has arrived.
-**************************************************************************/
-static void finish_revolution(struct player *pplayer)
-{
-  struct government *government = pplayer->target_government;
-
-  if (pplayer->target_government == game.government_when_anarchy) {
-    assert(0);
-    return;
-  }
-  if (pplayer->revolution_finishes > game.info.turn) {
-    assert(0);
-    return;
-  }
-
-  pplayer->government = government;
-  pplayer->target_government = NULL;
-
-  freelog(LOG_DEBUG,
-         "Revolution finished for %s.  Government is %s.  Revofin %d (%d).",
-         pplayer->name, get_government_name(government),
-         pplayer->revolution_finishes, game.info.turn);
-  notify_player(pplayer, NULL, E_REVOLT_DONE,
-                  _("%s now governs the %s as a %s."), 
-                  pplayer->name, 
-                  get_nation_name_plural(pplayer->nation),
-                  get_government_name(government));
-
-  if (!pplayer->ai.control) {
-    /* Keep luxuries if we have any.  Try to max out science. -GJW */
-    int max = get_player_bonus(pplayer, EFT_MAX_RATES);
-
-    pplayer->economic.science
-      = MIN(100 - pplayer->economic.luxury, max);
-    pplayer->economic.tax
-      = MIN(100 - pplayer->economic.luxury - pplayer->economic.science, max);
-    pplayer->economic.luxury
-      = 100 - pplayer->economic.science - pplayer->economic.tax;
-  }
-
-  check_player_government_rates(pplayer);
-  global_city_refresh(pplayer);
-  send_player_info(pplayer, pplayer);
-}
-
-/**************************************************************************
   Called by the client or AI to change government.
 **************************************************************************/
 void handle_player_change_government(struct player *pplayer, int government)
 {
-  int turns;
   struct government *gov = get_government(government);
+  struct plr_gov *plrgov;
+  bool nochange = FALSE;
 
   if (!gov || !can_change_to_government(pplayer, gov)) {
     return;
   }
-
-  freelog(LOG_DEBUG,
-         "Government changed for %s.  Target government is %s; "
-         "old %s.  Revofin %d, Turn %d.",
-         pplayer->name,
-         get_government_name(gov),
-         get_government_name(pplayer->government),
-         pplayer->revolution_finishes, game.info.turn);
-
-  /* Set revolution_finishes value. */
-  if (pplayer->revolution_finishes > 0) {
-    /* Player already has an active revolution.  Note that the finish time
-     * may be in the future (we're waiting for it to finish), the current
-     * turn (it just finished - but isn't reset until the end of the turn)
-     * or even in the past (if the player is in anarchy and hasn't chosen
-     * a government). */
-    turns = pplayer->revolution_finishes - game.info.turn;
-  } else if ((pplayer->ai.control && !ai_handicap(pplayer, H_REVOLUTION))
-            || get_player_bonus(pplayer, EFT_NO_ANARCHY)) {
-    /* AI players without the H_REVOLUTION handicap can skip anarchy */
-    turns = 0;
-  } else if (game.info.revolution_length == 0) {
-    turns = myrand(5) + 1;
-  } else {
-    turns = game.info.revolution_length;
-  }
-
-  pplayer->government = game.government_when_anarchy;
-  pplayer->target_government = gov;
-  pplayer->revolution_finishes = game.info.turn + turns;
-
-  freelog(LOG_DEBUG,
-         "Revolution started for %s.  Target government is %s.  "
-         "Revofin %d (%d).",
-         pplayer->name, get_government_name(pplayer->target_government),
-         pplayer->revolution_finishes, game.info.turn);
-
-  /* Now see if the revolution is instantaneous. */
-  if (turns <= 0
-      && pplayer->target_government != game.government_when_anarchy) {
-    finish_revolution(pplayer);
+  plrgov = &pplayer->govs[gov->category->index];
+  if (gov->entry_from && !player_has_government(pplayer, gov->entry_from)) {
+    notify_player(pplayer, NULL, E_NEW_GOVERNMENT, _("You cannot change "
+                  "to %s from %s directly, you must go by way of %s."),
+                  get_government_name(gov),
+                  get_government_name(plrgov->current_government),
+                  get_government_name(gov->entry_from));
     return;
-  } else if (turns > 0) {
+  }
+  /* You can go to "anarchy" govt even though you still have min_turns */
+  if (gov == gov->category->anarchy_government
+      || gov == plrgov->current_government) {
     notify_player(pplayer, NULL, E_REVOLT_START,
                     /* TRANS: this is a message event so don't make it
                      * too long. */
@@ -339,115 +260,80 @@
                         "Target government is %s.",
                         "The %s have incited a revolt! "
                         "%d turns of anarchy will ensue! "
-                        "Target government is %s.",
-                        turns),
-                    get_nation_name_plural(pplayer->nation), turns,
-                    get_government_name(pplayer->target_government));
-  } else {
-    assert(pplayer->target_government == game.government_when_anarchy);
-    notify_player(pplayer, NULL, E_REVOLT_START,
-                    _("Revolution: returning to anarchy."));
+                        "Target government is %s.", gov->min_turns),
+                    get_nation_name_plural(pplayer->nation), gov->min_turns,
+                    get_government_name(gov));
+    gov = gov->category->anarchy_government;
+    nochange = TRUE;
   }
+  if (game.info.turn < plrgov->turn_changed + gov->min_turns
+      && (pplayer->ai.control && !ai_handicap(pplayer, H_REVOLUTION))
+      && !get_player_bonus(pplayer, EFT_NO_ANARCHY)
+      && !nochange) {
+    /* AI players without the H_REVOLUTION handicap can skip anarchy */
+    notify_player(pplayer, NULL, E_NEW_GOVERNMENT, _("You cannot change "
+                  "from %s yet, you must wait for %d more turns first."),
+                  get_government_name(gov), 
+                  plrgov->turn_changed + gov->min_turns - game.info.turn);
+    return;
+  }
 
+  plrgov->current_government = gov;
+  plrgov->turn_changed = game.info.turn;
+  freelog(LOG_DEBUG, "Government changed for %s from %s to %s.",
+          pplayer->name, 
+          get_government_name(plrgov->current_government),
+          get_government_name(gov));
+  notify_player(pplayer, NULL, E_REVOLT_DONE,
+                _("%s now governs the %s as a %s."),
+                pplayer->name,
+                get_nation_name_plural(pplayer->nation),
+                get_government_name(gov));
+
+  if (!pplayer->ai.control) {
+    /* Keep luxuries if we have any.  Try to max out science. -GJW */
+    int max = get_player_bonus(pplayer, EFT_MAX_RATES);
+
+    pplayer->economic.rates[O_SCIENCE]
+      = MIN(100 - pplayer->economic.rates[O_LUXURY], max);
+    pplayer->economic.rates[O_GOLD]
+      = MIN(100 - pplayer->economic.rates[O_LUXURY] 
+            - pplayer->economic.rates[O_SCIENCE], max);
+    pplayer->economic.rates[O_LUXURY]
+      = 100 - pplayer->economic.rates[O_SCIENCE] 
+        - pplayer->economic.rates[O_GOLD];
+  }
+
   check_player_government_rates(pplayer);
   global_city_refresh(pplayer);
   send_player_info(pplayer, pplayer);
-
-  freelog(LOG_DEBUG,
-         "Government change complete for %s.  Target government is %s; "
-         "now %s.  Turn %d; revofin %d.",
-         pplayer->name,
-         get_government_name(pplayer->target_government),
-         get_government_name(pplayer->government),
-         game.info.turn, pplayer->revolution_finishes);
 }
 
 /**************************************************************************
-  See if the player has finished their revolution.  This function should
-  be called at the beginning of a player's phase.
-**************************************************************************/
-void update_revolution(struct player *pplayer)
-{
-  /* The player's revolution counter is stored in the revolution_finishes
-   * field.  This value has the following meanings:
-   *   - If negative (-1), then the player is not in a revolution.  In this
-   *     case the player should never be in anarchy.
-   *   - If positive, the player is in the middle of a revolution.  In this
-   *     case the value indicates the turn in which the revolution finishes.
-   *     * If this value is > than the current turn, then the revolution is
-   *       in progress.  In this case the player should always be in anarchy.
-   *     * If the value is == to the current turn, then the revolution is
-   *       finished.  The player may now choose a government.  However the
-   *       value isn't reset until the end of the turn.  If the player has
-   *       chosen a government by the end of the turn, then the revolution is
-   *       over and the value is reset to -1.
-   *     * If the player doesn't pick a government then the revolution
-   *       continues.  At this point the value is <= to the current turn,
-   *       and the player can leave the revolution at any time.  The value
-   *       is reset at the end of any turn when a non-anarchy government is
-   *       chosen.
-   */
-  freelog(LOG_DEBUG, "Update revolution for %s.  Current government %s, "
-         "target %s, revofin %d, turn %d.",
-         pplayer->name, get_government_name(pplayer->government),
-         pplayer->target_government
-         ? get_government_name(pplayer->target_government)
-         : "(none)",
-         pplayer->revolution_finishes, game.info.turn);
-  if (pplayer->government == game.government_when_anarchy
-      && pplayer->revolution_finishes <= game.info.turn) {
-    if (pplayer->target_government != game.government_when_anarchy) {
-      /* If the revolution is over and a target government is set, go into
-       * the new government. */
-      freelog(LOG_DEBUG, "Update: finishing revolution for %s.",
-             pplayer->name);
-      finish_revolution(pplayer);
-    } else {
-      /* If the revolution is over but there's no target government set,
-       * alert the player. */
-      notify_player(pplayer, NULL, E_REVOLT_DONE,
-                      _("You should choose a new government from the "
-                        "government menu."));
-    }
-  } else if (pplayer->government != game.government_when_anarchy
-            && pplayer->revolution_finishes < game.info.turn) {
-    /* Reset the revolution counter.  If the player has another revolution
-     * they'll have to re-enter anarchy. */
-    freelog(LOG_DEBUG, "Update: resetting revofin for %s.",
-           pplayer->name);
-    pplayer->revolution_finishes = -1;
-    send_player_info(pplayer, pplayer);
-  }
-}
+  The following checks that tax rates are acceptable. Has to be called 
+  when switching governments or when toggling from AI to human, or 
+  whenever changing the EFT_MAX_RATES effect.
 
-/**************************************************************************
-The following checks that government rates are acceptable for the present
-form of government. Has to be called when switching governments or when
-toggling from AI to human.
+  FIXME: Should automatically be called when EFT_MAX_RATES change 
+  somehow. As of now it is only called when governments change.
 **************************************************************************/
 void check_player_government_rates(struct player *pplayer)
 {
   struct player_economic old_econ = pplayer->economic;
   bool changed = FALSE;
+
   player_limit_to_government_rates(pplayer);
-  if (pplayer->economic.tax != old_econ.tax) {
-    changed = TRUE;
-    notify_player(pplayer, NULL, E_NEW_GOVERNMENT,
-                 _("Tax rate exceeded the max rate for %s; adjusted."), 
-                 get_government_name(pplayer->government));
+  output_type_iterate(o) {
+    if (pplayer->economic.rates[o] != old_econ.rates[o]) {
+      changed = TRUE;
+      break;
+    }
+  } output_type_iterate_end;
+
+  if (changed) {
+    notify_player(pplayer, NULL, E_PLAYER_SETTINGS,
+                  _("Tax rate exceeded the max rates; adjusted."));
   }
-  if (pplayer->economic.science != old_econ.science) {
-    changed = TRUE;
-    notify_player(pplayer, NULL, E_NEW_GOVERNMENT,
-                 _("Science rate exceeded the max rate for %s; adjusted."), 
-                 get_government_name(pplayer->government));
-  }
-  if (pplayer->economic.luxury != old_econ.luxury) {
-    changed = TRUE;
-    notify_player(pplayer, NULL, E_NEW_GOVERNMENT,
-                 _("Luxury rate exceeded the max rate for %s; adjusted."), 
-                 get_government_name(pplayer->government));
-  }
 }
 
 /**************************************************************************
@@ -876,7 +762,11 @@
   packet->science_cost = plr->ai.science_cost;
 
   packet->gold = plr->economic.gold;
-  packet->government = plr->government ? plr->government->index : -1;
+  gov_category_iterate(pcat) {
+    packet->current_government[pcat->index]
+        = plr->govs[pcat->index].current_government 
+          ? plr->govs[pcat->index].current_government->index : -1;
+  } gov_category_iterate_end;
 }
 
 /**************************************************************************
@@ -916,8 +806,6 @@
   if (info_level >= INFO_EMBASSY
       || (receiver
          && receiver->diplstates[plr->player_no].contact_turns_left > 0)) {
-    packet->target_government
-      = plr->target_government ? plr->target_government->index : -1;
     memset(&packet->embassy, 0, sizeof(packet->embassy));
     players_iterate(pother) {
       packet->embassy[pother->player_no]
@@ -932,7 +820,6 @@
       packet->diplstates[i].has_reason_to_cancel = 
plr->diplstates[i].has_reason_to_cancel;
     }
   } else {
-    packet->target_government = packet->government;
     memset(&packet->embassy, 0, sizeof(packet->embassy));
     if (receiver && player_has_embassy(plr, receiver)) {
       packet->embassy[receiver->player_no] = TRUE;
@@ -979,14 +866,21 @@
         research->inventions[i].state + '0';
     }
     packet->inventions[i]   = '\0';
-    packet->tax             = plr->economic.tax;
-    packet->science         = plr->economic.science;
-    packet->luxury          = plr->economic.luxury;
+    packet->tax             = plr->economic.rates[O_GOLD];
+    packet->science         = plr->economic.rates[O_SCIENCE];
+    packet->luxury          = plr->economic.rates[O_LUXURY];
     packet->bulbs_researched = research->bulbs_researched;
     packet->techs_researched = research->techs_researched;
     packet->researching = research->researching;
     packet->future_tech = research->future_tech;
-    packet->revolution_finishes = plr->revolution_finishes;
+    gov_category_iterate(pcat) {
+      packet->turn_changed[pcat->index]
+        = plr->govs[pcat->index].turn_changed;
+      packet->current_government[pcat->index]
+        = plr->govs[pcat->index].current_government->index;
+      packet->current_step[pcat->index]
+        = plr->govs[pcat->index].current_step;
+    } gov_category_iterate_end;
   } else {
     for (i = A_FIRST; i < game.control.num_tech_types; i++) {
       packet->inventions[i] = '0';
@@ -999,7 +893,11 @@
     packet->techs_researched= 0;
     packet->researching     = A_NOINFO;
     packet->future_tech     = 0;
-    packet->revolution_finishes = -1;
+    gov_category_iterate(pcat) {
+      packet->current_government[pcat->index] = -1;
+      packet->turn_changed[pcat->index]  = 0;
+      packet->current_step[pcat->index]  = 0;
+    } gov_category_iterate_end;
   }
 
   /* We have to inform the client that the other players also know
@@ -1433,8 +1331,11 @@
 
   sz_strlcpy(cplayer->username, ANON_USER_NAME);
   cplayer->is_connected = FALSE;
-  cplayer->government = cplayer->nation->init_government;
-  assert(cplayer->revolution_finishes < 0);
+  if (cplayer->nation->init_government) {
+    int gov = cplayer->nation->init_government->index;
+
+    handle_player_change_government(cplayer, gov);
+  }
   cplayer->capital = TRUE;
 
   /* cplayer is not yet part of players_iterate which goes only
@@ -1468,7 +1369,6 @@
   game.info.max_players = game.info.nplayers;
 
   /* Split the resources */
-  
   cplayer->economic.gold = pplayer->economic.gold;
   cplayer->economic.gold /= 2;
   pplayer->economic.gold -= cplayer->economic.gold;
@@ -1503,11 +1403,15 @@
     cplayer->ai.tech_want[i] = pplayer->ai.tech_want[i];
   } tech_type_iterate_end;
   
-  /* change the original player */
-  if (pplayer->government != game.government_when_anarchy) {
-    pplayer->government = game.government_when_anarchy;
-    pplayer->revolution_finishes = game.info.turn + 1;
-  }
+  /* throw the original player into anarchy */
+  gov_category_iterate(pcat) {
+    struct government *gov = pcat->anarchy_government;
+
+    if (gov && gov != pplayer->govs[pcat->index].current_government) {
+      handle_player_change_government(pplayer, gov->index);
+    }
+  } gov_category_iterate_end;
+
   get_player_research(pplayer)->bulbs_researched = 0;
   BV_CLR_ALL(pplayer->embassy);   /* all embassies destroyed */
 
Index: server/plrhand.h
===================================================================
--- server/plrhand.h    (revision 11446)
+++ server/plrhand.h    (working copy)
@@ -34,7 +34,6 @@
 void server_remove_player(struct player *pplayer);
 void kill_player(struct player *pplayer);
 void kill_dying_players(void);
-void update_revolution(struct player *pplayer);
 
 struct nation_type *pick_a_nation(struct nation_type **choices,
                                   bool ignore_conflicts,
Index: data/stdsounds.soundspec
===================================================================
--- data/stdsounds.soundspec    (revision 11446)
+++ data/stdsounds.soundspec    (working copy)
@@ -258,5 +258,6 @@
 ;e_wonder_started = ""
 ;e_wonder_stopped = ""
 ;e_worklist = ""
+;e_player_settings = ""
 
 ;music_start = ""
Index: data/default/effects.ruleset
===================================================================
--- data/default/effects.ruleset        (revision 11446)
+++ data/default/effects.ruleset        (working copy)
@@ -93,6 +93,14 @@
       "UnitClass", "Sea", "Local"
     }
 
+[effect_anarchy_upkeep]
+name   = "Upkeep_Free"
+value  = 99
+reqs   =
+    { "type", "name", "range"
+      "Gov", "Anarchy", "Player"
+    }
+
 [effect_republic]
 name   = "Make_Content_Mil"
 value  = 1
@@ -391,6 +399,7 @@
 reqs   =
     { "type",       "name",      "range"
       "Gov", "Anarchy",   "Player"
+      "OutputType", "Luxury", "Local"
     }
 
 [effect_max_rates_1]
Index: data/default/governments.ruleset
===================================================================
--- data/default/governments.ruleset    (revision 11446)
+++ data/default/governments.ruleset    (working copy)
@@ -11,11 +11,16 @@
 ; than minor changes.
 
 [datafile]
-description="Default governments data for Freeciv (as Civ2, minus 
fundamentalism)"
+description="Default governments data for Freeciv"
 options="1.9"
 
 [governments]
-when_anarchy="Anarchy"
+; The "anarchy" government must be able to declare war, as the AI
+; is depending on this.
+categories   = "Governments"
+when_anarchy = "Anarchy"
+;colours      = TODO
+;steps        = TODO
 
 ; Below: The individual government types, one per section.
 ;
@@ -47,11 +52,13 @@
 
 ;------------------------------------------------------------------------
 [government_anarchy]
-
+category    = _("Governments")
 name        = _("Anarchy")
 ; No reqs
 graphic     = "gov.anarchy"
 graphic_alt = "-"
+min_turns   = 5
+ai_better   = "Despotism"
 
 ruler_male_title = _("Usurper")
 ruler_female_title = _("?female:Usurper")
@@ -68,12 +75,13 @@
 
 ;------------------------------------------------------------------------
 [government_despotism]
-
+category    = _("Governments")
 name        = _("Despotism")
 ; No reqs
 graphic     = "gov.despotism"
 graphic_alt = "-"
 ai_better   = "Monarchy"
+entry_from  = "Anarchy"
 
 ruler_male_title = _("Dictator")
 ruler_female_title = _("Dictatress")
@@ -88,14 +96,15 @@
 
 ;------------------------------------------------------------------------
 [government_monarchy]
-
+category    = _("Governments")
 name        = _("Monarchy")
 reqs = { "type", "name", "range"
          "tech", "Monarchy", "Player"
        }
 graphic     = "gov.monarchy"
 graphic_alt = "-"
-ai_better   = "Communism"
+ai_better   = "Republic" ; since Republic is much earlier
+entry_from  = "Anarchy"
 
 ruler_male_title = _("King")
 ruler_female_title = _("Queen")
@@ -110,13 +119,14 @@
 
 ;------------------------------------------------------------------------
 [government_communism]
-
+category    = _("Governments")
 name        = _("Communism")
 reqs = { "type", "name", "range"
          "tech", "Communism", "Player"
        }
 graphic     = "gov.communism"
 graphic_alt = "-"
+entry_from  = "Anarchy"
 
 ruler_male_title = _("Comrade")
 ruler_female_title = _("?female:Comrade")
@@ -134,13 +144,14 @@
 
 ;------------------------------------------------------------------------
 [government_republic]
-
+category    = _("Governments")
 name        = _("Republic")
 reqs = { "type", "name", "range"
          "tech", "The Republic", "Player"
        }
 graphic     = "gov.republic"
 graphic_alt = "-"
+entry_from  = "Anarchy"
 
 ruler_male_title = _("Consul")
 ruler_female_title = _("?female:Consul")
@@ -156,13 +167,14 @@
 
 ;------------------------------------------------------------------------
 [government_democracy]
-
+category    = _("Governments")
 name        = _("Democracy")
 reqs = { "type", "name", "range"
          "tech", "Democracy", "Player"
        }
 graphic     = "gov.democracy"
 graphic_alt = "-"
+entry_from  = "Anarchy"
 
 ruler_male_title = _("President")
 ruler_female_title = _("?female:President")
Index: common/specialist.c
===================================================================
--- common/specialist.c (revision 11446)
+++ common/specialist.c (working copy)
@@ -68,7 +68,7 @@
 Specialist_type_id find_specialist_by_name(const char *name)
 {
   specialist_type_iterate(sp) {
-    if (strcmp(specialists[sp].name, name) == 0) {
+    if (mystrcasecmp(specialists[sp].name, name) == 0) {
       return sp;
     }
   } specialist_type_iterate_end;
Index: common/unittype.c
===================================================================
--- common/unittype.c   (revision 11446)
+++ common/unittype.c   (working copy)
@@ -101,7 +101,7 @@
   Returns the upkeep of a unit of this type under the given government.
 **************************************************************************/
 int utype_upkeep_cost(const struct unit_type *ut, struct player *pplayer,
-                     const struct government *g, Output_type_id otype)
+                      Output_type_id otype)
 {
   int val = ut->upkeep[otype];
 
@@ -440,7 +440,7 @@
     return FALSE;
   }
   if (punittype->gov_requirement
-      && punittype->gov_requirement != p->government) {
+      && !player_has_government(p, punittype->gov_requirement)) {
     return FALSE;
   }
   if (get_invention(p,punittype->tech_requirement) != TECH_KNOWN) {
Index: common/unittype.h
===================================================================
--- common/unittype.h   (revision 11446)
+++ common/unittype.h   (working copy)
@@ -219,7 +219,7 @@
 const char *get_units_with_flag_string(int flag);
 
 int utype_upkeep_cost(const struct unit_type *ut, struct player *pplayer,
-                      const struct government *g, Output_type_id otype);
+                      Output_type_id otype);
 int utype_happy_cost(const struct unit_type *ut, const struct player *pplayer);
 
 struct unit_type *can_upgrade_unittype(const struct player *pplayer,
Index: common/events.c
===================================================================
--- common/events.c     (revision 11446)
+++ common/events.c     (working copy)
@@ -139,6 +139,7 @@
   GEN_EV(N_("Chat error messages"), E_CHAT_ERROR),
   GEN_EV(N_("Connect/disconnect messages"), E_CONNECTION),
   GEN_EV(N_("AI Debug messages"), E_AI_DEBUG),
+  GEN_EV(N_("Player settings"), E_PLAYER_SETTINGS),
   GEN_EV_TERMINATOR
 };
 
Index: common/packets.def
===================================================================
--- common/packets.def  (revision 11446)
+++ common/packets.def  (working copy)
@@ -209,7 +209,7 @@
 type TECH              = UINT8
 type UNIT_TYPE         = uint8(Unit_type_id)
 type NATION            = sint16(Nation_type_id)
-type GOVERNMENT                = UINT8
+type GOVERNMENT                = SINT8
 type CONNECTION                = UINT8
 type TEAM              = UINT8
 type CONTINENT          = sint16(Continent_id)
@@ -438,8 +438,6 @@
   UINT16 incite_improvement_factor;
   UINT16 incite_unit_factor;
   UINT16 incite_total_factor;
-  GOVERNMENT government_when_anarchy_id;
-  UINT8 revolution_length;
   SINT16 base_pollution;
   UINT8 happy_cost;
   UINT8 food_cost;
@@ -643,8 +641,6 @@
 
   UINT32 score;
   BOOL is_male;
-  GOVERNMENT government;
-  GOVERNMENT target_government;
   BOOL embassy[MAX_NUM_PLAYERS + MAX_NUM_BARBARIANS];
   UINT8 city_style;
   NATION nation;
@@ -665,10 +661,13 @@
   UINT8 researching;
   UINT16 science_cost;
 
+  TURN turn_changed[MAX_NUM_CATEGORIES];
+  TURN current_step[MAX_NUM_CATEGORIES];
+  GOVERNMENT current_government[MAX_NUM_CATEGORIES];
+
   UINT16 future_tech;
   UINT8 tech_goal;
   BOOL is_connected;
-  TURN revolution_finishes;
   BOOL ai;
   UINT8 ai_skill_level;
   UINT8 barbarian_type;
@@ -1099,14 +1098,6 @@
   REQUIREMENT reqs[MAX_NUM_REQS:reqs_count];
 end
 
-PACKET_RULESET_GOVERNMENT_RULER_TITLE=98;sc,lsend
-  GOVERNMENT gov;
-  UINT8 id;
-  NATION nation;
-  STRING male_title[MAX_LEN_NAME];
-  STRING female_title[MAX_LEN_NAME];
-end
-
 PACKET_RULESET_TECH=99;sc,lsend
   TECH id;
   TECH req[2];
@@ -1124,8 +1115,10 @@
   UINT8 reqs_count;
   REQUIREMENT reqs[MAX_NUM_REQS:reqs_count];
 
-  UINT8 num_ruler_titles;
-       
+  UINT8 min_turns;
+  UINT8 category;
+  GOVERNMENT entry_from;
+
   STRING name[MAX_LEN_NAME];
   STRING graphic_str[MAX_LEN_NAME];
   STRING graphic_alt[MAX_LEN_NAME];
@@ -1260,6 +1253,7 @@
   UINT8 num_impr_types;
   UINT8 num_tech_types;
   UINT8 government_count;
+  UINT8 gov_categories_count;
   UINT8 nation_count;
   UINT8 styles_count;
   UINT8 terrain_count;
@@ -1349,3 +1343,11 @@
   STRING graphic_str[MAX_LEN_NAME];
   STRING graphic_alt[MAX_LEN_NAME];
 end
+
+PACKET_RULESET_GOV_CATEGORY=125;sc,lsend
+  UINT8 index;
+  STRING name[MAX_LEN_NAME];
+  GOVERNMENT anarchy_government;
+  UINT32 colour;
+  TURN steps[MAX_NUM_GOV_STEPS];
+end
Index: common/city.c
===================================================================
--- common/city.c       (revision 11446)
+++ common/city.c       (working copy)
@@ -1471,8 +1471,8 @@
   int rates[3], result[3];
 
   if (game.info.changable_tax) {
-    rates[SCIENCE] = pplayer->economic.science;
-    rates[LUXURY] = pplayer->economic.luxury;
+    rates[SCIENCE] = pplayer->economic.rates[O_SCIENCE];
+    rates[LUXURY] = pplayer->economic.rates[O_LUXURY];
     rates[TAX] = 100 - rates[SCIENCE] - rates[LUXURY];
   } else {
     rates[SCIENCE] = game.info.forced_science;
@@ -1480,13 +1480,6 @@
     rates[TAX] = game.info.forced_gold;
   }
   
-  /* ANARCHY */
-  if (get_gov_pplayer(pplayer) == game.government_when_anarchy) {
-    rates[SCIENCE] = 0;
-    rates[LUXURY] = 100;
-    rates[TAX] = 0;
-  }
-
   distribute(trade, 3, rates, result);
 
   output[O_SCIENCE] += result[SCIENCE];
@@ -1953,7 +1946,6 @@
                                                        struct unit *punit))
 {
   struct player *plr = city_owner(pcity);
-  struct government *g = get_gov_pcity(pcity);
   int free_upkeep[O_COUNT];
   int free_happy = get_city_bonus(pcity, EFT_MAKE_CONTENT_MIL);
 
@@ -1983,9 +1975,11 @@
   /* military units in this city (need _not_ be home city) can make
      unhappy citizens content
    */
-  if (get_city_bonus(pcity, EFT_MARTIAL_LAW_MAX) > 0) {
+  if (get_city_bonus(pcity, EFT_MARTIAL_LAW_EACH) > 0) {
+    int max = get_city_bonus(pcity, EFT_MARTIAL_LAW_MAX);
+
     unit_list_iterate(pcity->tile->units, punit) {
-      if (pcity->martial_law < get_city_bonus(pcity, EFT_MARTIAL_LAW_MAX)
+      if ((pcity->martial_law < max || max == 0)
          && is_military_unit(punit)
          && punit->owner == pcity->owner) {
        pcity->martial_law++;
@@ -2006,7 +2000,7 @@
     int old_unhappiness = this_unit->unhappiness;
 
     output_type_iterate(o) {
-      upkeep_cost[o] = utype_upkeep_cost(ut, plr, g, o);
+      upkeep_cost[o] = utype_upkeep_cost(ut, plr, o);
       old_upkeep[o] = this_unit->upkeep[o];
     } output_type_iterate_end;
 
Index: common/events.h
===================================================================
--- common/events.h     (revision 11446)
+++ common/events.h     (working copy)
@@ -116,9 +116,10 @@
   E_CHAT_ERROR, /* Chatline errors (bad syntax, etc.) */
   E_CONNECTION, /* Messages about acquired or lost connections */
   E_AI_DEBUG, /* AI debugging messages */
+  E_PLAYER_SETTINGS, /* taxes etc */
   /* 
    * Note: If you add a new event, make sure you make a similar change
-   * to the events array in client/options.c using GEN_EV and to
+   * to the events array in common/events.c using GEN_EV and to
    * data/stdsounds.spec.
    */
   E_LAST
Index: common/aicore/cm.c
===================================================================
--- common/aicore/cm.c  (revision 11446)
+++ common/aicore/cm.c  (working copy)
@@ -1584,11 +1584,11 @@
 
   /* sci/lux/gold get benefit from the tax rates (in percentage) */
   estimates[O_SCIENCE]
-    += pplayer->economic.science * estimates[O_TRADE] / 100.0;
+    += pplayer->economic.rates[O_SCIENCE] * estimates[O_TRADE] / 100.0;
   estimates[O_LUXURY]
-    += pplayer->economic.luxury * estimates[O_TRADE] / 100.0;
+    += pplayer->economic.rates[O_LUXURY] * estimates[O_TRADE] / 100.0;
   estimates[O_GOLD]
-    += pplayer->economic.tax * estimates[O_TRADE] / 100.0;
+    += pplayer->economic.rates[O_LUXURY] * estimates[O_TRADE] / 100.0;
 
   /* now add in the bonuses from building effects (in percentage) */
   output_type_iterate(stat) {
Index: common/effects.c
===================================================================
--- common/effects.c    (revision 11446)
+++ common/effects.c    (working copy)
@@ -844,6 +844,10 @@
                             const struct output_type *poutput,
                             enum effect_type effect_type)
 {
+  if (!initialized) {
+    return 0;
+  }
+
   assert(pplayer != NULL);
   assert(poutput != NULL);
   assert(effect_type != EFT_LAST);
@@ -858,6 +862,10 @@
                           const struct output_type *poutput,
                           enum effect_type effect_type)
 {
+  if (!initialized) {
+    return 0;
+  }
+
   assert(pcity != NULL);
   assert(poutput != NULL);
   assert(effect_type != EFT_LAST);
@@ -871,6 +879,10 @@
 int get_building_bonus(const struct city *pcity, Impr_type_id id,
                       enum effect_type effect_type)
 {
+  if (!initialized) {
+    return 0;
+  }
+
   assert(pcity != NULL && id != B_LAST);
   return get_target_bonus_effects(NULL,
                                  city_owner(pcity), pcity,
@@ -892,6 +904,10 @@
                       const struct unit_type *punittype,
                       enum effect_type effect_type)
 {
+  if (!initialized) {
+    return 0;
+  }
+
   assert(pplayer != NULL && ptile != NULL && punittype != NULL);
   return get_target_bonus_effects(NULL,
                                  pplayer, ptile->city, NULL, ptile,
@@ -903,6 +919,10 @@
 **************************************************************************/
 int get_unit_bonus(const struct unit *punit, enum effect_type effect_type)
 {
+  if (!initialized) {
+    return 0;
+  }
+
   assert(punit != NULL);
   return get_target_bonus_effects(NULL,
                                  unit_owner(punit),
@@ -922,6 +942,10 @@
                             const struct player *pplayer,
                             enum effect_type effect_type)
 {
+  if (!initialized) {
+    return 0;
+  }
+
   assert(pplayer != NULL);
   return get_target_bonus_effects(plist,
                                  pplayer, NULL, NULL,
@@ -940,6 +964,10 @@
                           const struct output_type *poutput,
                           enum effect_type effect_type)
 {
+  if (!initialized) {
+    return 0;
+  }
+
   assert(pcity != NULL);
   return get_target_bonus_effects(plist,
                                  city_owner(pcity), pcity, NULL,
@@ -956,6 +984,10 @@
 int get_current_construction_bonus(const struct city *pcity,
                                   enum effect_type effect_type)
 {
+  if (!initialized) {
+    return 0;
+  }
+
   if (!pcity->production.is_unit) {
     Impr_type_id id = pcity->production.value;
     int power = 0;
Index: common/fc_types.h
===================================================================
--- common/fc_types.h   (revision 11446)
+++ common/fc_types.h   (working copy)
@@ -37,6 +37,10 @@
 #define MAX_LEN_VET_SHORT_NAME 8
 #define MAX_VET_LEVELS 10
 
+/* Government stuff */
+#define MAX_NUM_CATEGORIES 3
+#define MAX_NUM_GOV_STEPS 10
+
 /* Changing these will probably break network compatability. */
 #define MAX_LEN_NAME     32
 #define MAX_LEN_DEMOGRAPHY 16
Index: common/government.c
===================================================================
--- common/government.c (revision 11446)
+++ common/government.c (working copy)
@@ -18,7 +18,6 @@
 #include <assert.h>
 
 #include "game.h"
-#include "log.h"
 #include "mem.h"
 #include "player.h"
 #include "shared.h"
@@ -28,10 +27,49 @@
 #include "government.h"
 
 struct government *governments = NULL;
+struct gov_category *gov_categories = NULL;
 
 #define CHECK_GOVERNMENT(gov) assert(&governments[(gov)->index] == (gov))
 
 /****************************************************************************
+  Does a linear search of the government categories to find the one that 
+  matches the given (translated) name.  Returns NULL if none match.
+****************************************************************************/
+struct gov_category *find_gov_category_by_name(const char *name)
+{
+  int i;
+
+  for (i = 0; i < game.control.gov_categories_count; i++) {
+    struct gov_category *pcat = &gov_categories[i];
+
+    if (mystrcasecmp(pcat->name, name) == 0) {
+      return pcat;
+    }
+  }
+
+  return NULL;
+}
+
+/****************************************************************************
+  Does a linear search of the government categories to find the one that 
+  matches the given (translated) name.  Returns NULL if none match.
+****************************************************************************/
+struct gov_category *find_gov_category_by_name_orig(const char *name)
+{
+  int i;
+
+  for (i = 0; i < game.control.gov_categories_count; i++) {
+    struct gov_category *pcat = &gov_categories[i];
+
+    if (mystrcasecmp(pcat->name_orig, name) == 0) {
+      return pcat;
+    }
+  }
+
+  return NULL;
+}
+
+/****************************************************************************
   Does a linear search of the governments to find the one that matches the
   given (translated) name.  Returns NULL if none match.
 ****************************************************************************/
@@ -72,60 +110,9 @@
   return &governments[gov];
 }
 
-/****************************************************************************
-  Return this player's government.
-****************************************************************************/
-struct government *get_gov_pplayer(const struct player *pplayer)
-{
-  assert(pplayer != NULL);
-  return pplayer->government;
-}
-
-/****************************************************************************
-  Return the government of the player who owns the city.
-****************************************************************************/
-struct government *get_gov_pcity(const struct city *pcity)
-{
-  assert(pcity != NULL);
-  return get_gov_pplayer(city_owner(pcity));
-}
-
-
 /***************************************************************
 ...
 ***************************************************************/
-const char *get_ruler_title(const struct government *g, bool male,
-                           const struct nation_type *nation)
-{
-  struct ruler_title *best_match = NULL;
-  int i;
-
-  CHECK_GOVERNMENT(g);
-
-  for(i=0; i<g->num_ruler_titles; i++) {
-    struct ruler_title *title = &g->ruler_titles[i];
-
-    if (title->nation == DEFAULT_TITLE && !best_match) {
-      best_match = title;
-    } else if (title->nation == nation) {
-      best_match = title;
-      break;
-    }
-  }
-
-  if (best_match) {
-    return male ? best_match->male_title : best_match->female_title;
-  } else {
-    freelog(LOG_ERROR,
-           "get_ruler_title: found no title for government %d (%s) nation %d",
-           g->index, g->name, nation->index);
-    return male ? "Mr." : "Ms.";
-  }
-}
-
-/***************************************************************
-...
-***************************************************************/
 const char *get_government_name(const struct government *gov)
 {
   CHECK_GOVERNMENT(gov);
@@ -153,48 +140,52 @@
     return TRUE;
   }
 
+  if (gov->entry_from && !player_has_government(pplayer, gov->entry_from)) {
+    /* Can only enter this government from the specified government */
+    return FALSE;
+  }
+
   return are_reqs_active(pplayer, NULL, NULL, NULL, NULL, NULL, NULL,
                         &gov->reqs);
 }
 
 /***************************************************************
-...
+ Allocate space for the given number of governments.
 ***************************************************************/
-void set_ruler_title(struct government *gov, struct nation_type *pnation,
-                     const char *male, const char *female)
+void governments_alloc(int num)
 {
-  struct ruler_title *title;
+  int index;
 
-  gov->num_ruler_titles++;
-  gov->ruler_titles
-    = fc_realloc(gov->ruler_titles,
-                gov->num_ruler_titles * sizeof(*gov->ruler_titles));
-  title = &(gov->ruler_titles[gov->num_ruler_titles-1]);
+  governments = fc_calloc(num, sizeof(*governments));
+  game.control.government_count = num;
 
-  title->nation = pnation; /* A valid nation or DEFAULT_NATION */
+  for (index = 0; index < num; index++) {
+    struct government *gov = &governments[index];
 
-  sz_strlcpy(title->male_title_orig, male);
-  title->male_title = title->male_title_orig;
-
-  sz_strlcpy(title->female_title_orig, female);
-  title->female_title = title->female_title_orig;
+    gov->index = index;
+    gov->category = NULL;
+    gov->entry_from = NULL;
+    gov->helptext = NULL;
+    gov->ai.better = NULL;
+    requirement_vector_init(&gov->reqs);
+  }
 }
 
 /***************************************************************
- Allocate space for the given number of governments.
+ Allocate space for the given number of government categories.
 ***************************************************************/
-void governments_alloc(int num)
+void gov_categories_alloc(int num)
 {
   int index;
 
-  governments = fc_calloc(num, sizeof(*governments));
-  game.control.government_count = num;
+  gov_categories = fc_calloc(num, sizeof(*gov_categories));
+  game.control.gov_categories_count = num;
 
   for (index = 0; index < num; index++) {
-    struct government *gov = &governments[index];
+    struct gov_category *pcat = &gov_categories[index];
 
-    gov->index = index;
-    requirement_vector_init(&gov->reqs);
+    pcat->index = index;
+    pcat->anarchy_government = NULL;
   }
 }
 
@@ -203,9 +194,6 @@
 ***************************************************************/
 static void government_free(struct government *gov)
 {
-  free(gov->ruler_titles);
-  gov->ruler_titles = NULL;
-
   free(gov->helptext);
   gov->helptext = NULL;
 
@@ -224,3 +212,28 @@
   governments = NULL;
   game.control.government_count = 0;
 }
+
+/***************************************************************
+  De-allocate the currently allocated government categories.
+***************************************************************/
+void gov_categories_free(void)
+{
+  free(gov_categories);
+  governments = NULL;
+  game.control.gov_categories_count = 0;
+}
+
+/**************************************************************************
+  Return TRUE if we are using this government.
+**************************************************************************/
+bool player_has_government(const struct player *pplayer,
+                           const struct government *gov)
+{
+  gov_category_iterate(pcat) {
+    if (pplayer->govs[pcat->index].current_government == gov) {
+      return TRUE;
+    }
+  } gov_category_iterate_end;
+  return FALSE;
+}
+
Index: common/government.h
===================================================================
--- common/government.h (revision 11446)
+++ common/government.h (working copy)
@@ -23,20 +23,13 @@
 /* special values for free_* fields -- SKi */
 #define G_CITY_SIZE_FREE          G_MAGIC
 
-/* each government has a list of ruler titles, where at least
- * one entry should have nation=DEFAULT_TITLE.
- */
-#define DEFAULT_TITLE NULL
-
-struct ruler_title
-{
-  struct nation_type *nation;
-  const char *male_title; /* Translated string - doesn't need freeing. */
-  const char *female_title; /* Translated string - doesn't need freeing. */
-  
-  /* untranslated copies: */
-  char male_title_orig[MAX_LEN_NAME];    
-  char female_title_orig[MAX_LEN_NAME];
+struct gov_category {
+  int index;
+  char name[MAX_LEN_NAME];
+  char name_orig[MAX_LEN_NAME];
+  int background_colour;
+  struct government *anarchy_government;
+  int steps[MAX_NUM_GOV_STEPS];
 };
 
 /* This is struct government itself.  All information about
@@ -50,10 +43,10 @@
   char  graphic_str[MAX_LEN_NAME];
   char  graphic_alt[MAX_LEN_NAME];
   struct requirement_vector reqs;
+  int min_turns; /* for revolution */
+  struct gov_category *category;
+  struct government *entry_from;
 
-  struct ruler_title *ruler_titles;
-  int   num_ruler_titles;
-
   char *helptext;
 
   /* AI cached data for this government. */
@@ -63,23 +56,25 @@
 };
 
 extern struct government *governments;
+extern struct gov_category *gov_categories;
 
+struct gov_category *find_gov_category_by_name_orig(const char *name);
+struct gov_category *find_gov_category_by_name(const char *name);
+void gov_categories_alloc(int num);
+void gov_categories_free(void);
+
 struct government *get_government(int government_id);
-struct government *get_gov_pplayer(const struct player *pplayer);
-struct government *get_gov_pcity(const struct city *pcity);
+bool player_has_government(const struct player *pplayer, 
+                           const struct government *gov);
 
 struct government *find_government_by_name(const char *name);
 struct government *find_government_by_name_orig(const char *name);
 
 const char *get_government_name(const struct government *gov);
-const char *get_ruler_title(const struct government *gov, bool male,
-                           const struct nation_type *pnation);
 
 bool can_change_to_government(struct player *pplayer,
                              const struct government *gov);
 
-void set_ruler_title(struct government *gov, struct nation_type *pnation,
-                     const char *male, const char *female);
 void governments_alloc(int num);
 void governments_free(void);
 
@@ -96,4 +91,17 @@
   }                                                                         \
 }
 
+#define gov_category_iterate(cat)                                           \
+{                                                                           \
+  int GCII;                                                                 \
+                                                                            \
+  for (GCII = 0; GCII < game.control.gov_categories_count; GCII++) {        \
+    struct gov_category *cat = &gov_categories[GCII];                       \
+    {
+
+#define gov_category_iterate_end                                            \
+    }                                                                       \
+  }                                                                         \
+}
+
 #endif  /* FC__GOVERNMENT_H */
Index: common/player.c
===================================================================
--- common/player.c     (revision 11446)
+++ common/player.c     (working copy)
@@ -180,12 +180,14 @@
   sz_strlcpy(plr->ranked_username, ANON_USER_NAME);
   plr->user_turns = 0;
   plr->is_male = TRUE;
-  plr->government = NULL;
-  plr->target_government = NULL;
+  for (i = 0; i < MAX_NUM_CATEGORIES; i++) {
+    plr->govs[i].current_government = NULL;
+    plr->govs[i].current_step = 0;
+    plr->govs[i].turn_changed = 0;
+  }
   plr->nation = NO_NATION_SELECTED;
   plr->team = NULL;
   plr->is_ready = FALSE;
-  plr->revolution_finishes = -1;
   plr->capital = FALSE;
   plr->units = unit_list_new();
   plr->cities = city_list_new();
@@ -209,9 +211,15 @@
   plr->ai.fuzzy = 0;
   plr->ai.expand = 100;
   plr->ai.barbarian_type = NOT_A_BARBARIAN;
-  plr->economic.tax=PLAYER_DEFAULT_TAX_RATE;
-  plr->economic.science=PLAYER_DEFAULT_SCIENCE_RATE;
-  plr->economic.luxury=PLAYER_DEFAULT_LUXURY_RATE;
+  output_type_iterate(o) {
+    plr->economic.rates[o] = 100;
+  } output_type_iterate_end;
+  output_type_iterate(o) {
+    plr->economic.rates[o] = 100;
+  } output_type_iterate_end;
+  plr->economic.rates[O_GOLD] = PLAYER_DEFAULT_TAX_RATE;
+  plr->economic.rates[O_SCIENCE] = PLAYER_DEFAULT_SCIENCE_RATE;
+  plr->economic.rates[O_LUXURY] = PLAYER_DEFAULT_LUXURY_RATE;
 
   player_limit_to_government_rates(plr);
   spaceship_init(&plr->spaceship);
@@ -519,42 +527,30 @@
 **************************************************************************/
 void player_limit_to_government_rates(struct player *pplayer)
 {
-  int maxrate, surplus;
+  int maxrate[O_MAX], surplus = 0, sum = 0;
 
-  /* ai players allowed to cheat */
-  if (pplayer->ai.control) {
-    return;
-  }
+  output_type_iterate(o) {
+    maxrate[o] = MIN(get_player_output_bonus(pplayer, get_output_type(o), 
+                                             EFT_MAX_RATES), 100);
+    sum += pplayer->economic.rates[o];
+    if (pplayer->economic.rates[o] > maxrate[o]) {
+      surplus += pplayer->economic.rates[o] - maxrate[o];
+      pplayer->economic.rates[o] = maxrate[o];
+    }
+  } output_type_iterate_end;
 
-  maxrate = get_player_bonus(pplayer, EFT_MAX_RATES);
-  if (maxrate == 0) {
-    maxrate = 100; /* effects not initialized yet */
-  }
-  surplus = 0;
-  if (pplayer->economic.luxury > maxrate) {
-    surplus += pplayer->economic.luxury - maxrate;
-    pplayer->economic.luxury = maxrate;
-  }
-  if (pplayer->economic.tax > maxrate) {
-    surplus += pplayer->economic.tax - maxrate;
-    pplayer->economic.tax = maxrate;
-  }
-  if (pplayer->economic.science > maxrate) {
-    surplus += pplayer->economic.science - maxrate;
-    pplayer->economic.science = maxrate;
-  }
+  /* Adjust to make rates equal to 100 in sum if they are not already */
+  surplus += 100 - sum;
 
   assert(surplus % 10 == 0);
   while (surplus > 0) {
-    if (pplayer->economic.science < maxrate) {
-      pplayer->economic.science += 10;
-    } else if (pplayer->economic.tax < maxrate) {
-      pplayer->economic.tax += 10;
-    } else if (pplayer->economic.luxury < maxrate) {
-      pplayer->economic.luxury += 10;
-    } else {
-      die("byebye");
-    }
+    if (pplayer->economic.rates[O_SCIENCE] < maxrate[O_SCIENCE]) {
+      pplayer->economic.rates[O_SCIENCE] += 10;
+    } else if (pplayer->economic.rates[O_GOLD] < maxrate[O_GOLD]) {
+      pplayer->economic.rates[O_GOLD] += 10;
+    } else if (pplayer->economic.rates[O_LUXURY] < maxrate[O_LUXURY]) {
+      pplayer->economic.rates[O_LUXURY] += 10;
+    } /* else case is not always a bug - during initialization eg */
     surplus -= 10;
   }
 
Index: common/player.h
===================================================================
--- common/player.h     (revision 11446)
+++ common/player.h     (working copy)
@@ -62,9 +62,7 @@
 
 struct player_economic {
   int gold;
-  int tax;
-  int science;
-  int luxury;
+  int rates[O_MAX];
 };
 
 struct player_score {
@@ -152,6 +150,12 @@
   PLAYER_DEBUG_DIPLOMACY, PLAYER_DEBUG_TECH, PLAYER_DEBUG_LAST
 };
 
+struct plr_gov {
+  struct government *current_government;
+  int turn_changed;
+  int current_step;
+};
+
 BV_DEFINE(bv_debug, PLAYER_DEBUG_LAST);
 struct player {
   int player_no;
@@ -160,8 +164,7 @@
   char ranked_username[MAX_LEN_NAME]; /* the user who will be ranked */
   int user_turns;            /* number of turns this user has played */
   bool is_male;
-  struct government *government; /* may be NULL in pregame */
-  struct government *target_government; /* may be NULL */
+  struct plr_gov govs[MAX_NUM_CATEGORIES];
   struct nation_type *nation;
   struct team *team;
   bool is_ready; /* Did the player click "start" yet? */
@@ -170,10 +173,6 @@
   bool is_alive;
   bool is_dying; /* set once the player is in the process of dying */
   bool surrendered; /* has indicated willingness to surrender */
-
-  /* Turn in which the player's revolution is over; see update_revolution. */
-  int revolution_finishes;
-
   bool capital; /* used to give player init_buildings in first city. */
   bv_player embassy;
   struct player_diplstate diplstates[MAX_NUM_PLAYERS + MAX_NUM_BARBARIANS];
Index: common/game.c
===================================================================
--- common/game.c       (revision 11446)
+++ common/game.c       (working copy)
@@ -236,7 +236,6 @@
   game.info.nbarbarians   = 0;
   game.info.occupychance  = GAME_DEFAULT_OCCUPYCHANCE;
   game.info.autoattack    = GAME_DEFAULT_AUTOATTACK;
-  game.info.revolution_length = GAME_DEFAULT_REVOLUTION_LENGTH;
   game.info.heating       = 0;
   game.info.cooling       = 0;
   game.info.allowed_city_names = GAME_DEFAULT_ALLOWED_CITY_NAMES;
@@ -246,7 +245,6 @@
 #else
   game.info.save_compress_level = GAME_NO_COMPRESS_LEVEL;
 #endif
-  game.info.government_when_anarchy_id = G_MAGIC;   /* flag */
 
   game.info.is_new_game   = TRUE;
   game.simultaneous_phases_stored = GAME_DEFAULT_SIMULTANEOUS_PHASES;
@@ -613,15 +611,7 @@
 
 
   government_iterate(tthis) {
-    int j;
-
     tthis->name = Q_(tthis->name_orig);
-    for(j=0; j<tthis->num_ruler_titles; j++) {
-      struct ruler_title *that = &tthis->ruler_titles[j];
-
-      that->male_title = Q_(that->male_title_orig);
-      that->female_title = Q_(that->female_title_orig);
-    }
   } government_iterate_end;
   for (i = 0; i < game.control.nation_count; i++) {
     struct nation_type *tthis = get_nation_by_idx(i);
Index: common/game.h
===================================================================
--- common/game.h       (revision 11446)
+++ common/game.h       (working copy)
@@ -56,7 +56,6 @@
 
 struct civ_game {
   struct packet_game_info info;
-  struct government *government_when_anarchy;
 
   struct packet_ruleset_control control;
   int version;
@@ -359,10 +358,6 @@
 #define GAME_MIN_ALLOWED_CITY_NAMES 0
 #define GAME_MAX_ALLOWED_CITY_NAMES 3
 
-#define GAME_DEFAULT_REVOLUTION_LENGTH 0
-#define GAME_MIN_REVOLUTION_LENGTH 0
-#define GAME_MAX_REVOLUTION_LENGTH 10
-
 #define GAME_START_YEAR -4000
 
 #endif  /* FC__GAME_H */
Index: common/requirements.c
===================================================================
--- common/requirements.c       (revision 11446)
+++ common/requirements.c       (working copy)
@@ -809,7 +809,7 @@
   case REQ_GOV:
     /* The requirement is filled if the player is using the government. */
     eval = (target_player
-           && (target_player->government == req->source.value.gov));
+            && player_has_government(target_player, req->source.value.gov));
     break;
   case REQ_BUILDING:
     /* The requirement is filled if there's at least one of the building
Index: ai/advdomestic.c
===================================================================
--- ai/advdomestic.c    (revision 11446)
+++ ai/advdomestic.c    (working copy)
@@ -139,8 +139,6 @@
                                   struct ai_choice *choice)
 {
   struct ai_data *ai = ai_data_get(pplayer);
-  /* Government of the player */
-  struct government *gov = get_gov_pplayer(pplayer);
   /* Unit type with certain role */
   struct unit_type *unit_type;
 
@@ -152,7 +150,7 @@
   if (unit_type
       && (pcity->id != ai->wonder_city || unit_type->pop_cost == 0)
       && pcity->surplus[O_FOOD] > utype_upkeep_cost(unit_type,
-                                                   pplayer, gov, O_FOOD)) {
+                                                    pplayer, O_FOOD)) {
     /* The worker want is calculated in settlers.c called from
      * ai_manage_cities.  The expand value is the % that the AI should
      * value expansion (basically to handicap easier difficutly levels)
@@ -181,7 +179,7 @@
       && (pcity->id != ai->wonder_city
           || unit_type->pop_cost == 0)
       && pcity->surplus[O_FOOD] >= utype_upkeep_cost(unit_type,
-                                                    pplayer, gov, O_FOOD)) {
+                                                     pplayer, O_FOOD)) {
     /* founder_want calculated in aisettlers.c */
     int want = pcity->ai.founder_want;
 
Index: ai/aitools.c
===================================================================
--- ai/aitools.c        (revision 11446)
+++ ai/aitools.c        (working copy)
@@ -1094,7 +1094,7 @@
 **************************************************************************/
 void ai_government_change(struct player *pplayer, struct government *gov)
 {
-  if (gov == pplayer->government) {
+  if (player_has_government(pplayer, gov)) {
     return;
   }
 
@@ -1205,8 +1205,7 @@
   sure whether it is fully general for all possible parameters/
   combinations." --dwp
 **********************************************************************/
-bool ai_assess_military_unhappiness(struct city *pcity,
-                                    struct government *g)
+bool ai_assess_military_unhappiness(struct city *pcity)
 {
   int free_happy;
   int unhap = 0;
Index: ai/advdiplomacy.c
===================================================================
--- ai/advdiplomacy.c   (revision 11446)
+++ ai/advdiplomacy.c   (working copy)
@@ -24,6 +24,7 @@
 #include "events.h"
 #include "fcintl.h"
 #include "game.h"
+#include "government.h"
 #include "log.h"
 #include "mem.h"
 #include "packets.h"
@@ -1121,10 +1122,35 @@
     remove_shared_vision(pplayer, target);
   }
 
-  /* Check for Senate obstruction.  If so, dissolve it. */
+  /* Check for Senate obstruction.  If so, dissolve it. 
+   *
+   * This is a useful example of how complex code becomes when we make
+   * the rules generalized. */
   if (pplayer_can_cancel_treaty(pplayer, target) == DIPL_SENATE_BLOCKING) {
-    handle_player_change_government(pplayer, 
-                                    game.info.government_when_anarchy_id);
+    government_iterate(gov) {
+      struct req_source source = {
+        .type = REQ_GOV,
+        .value = {.gov = gov }
+      };
+
+      effect_list_iterate(get_req_source_effects(&source), peffect) {
+        if (peffect->type == EFT_HAS_SENATE) {
+          requirement_list_iterate(peffect->reqs, preq) {
+            if (preq->source.type == REQ_GOV) {
+              struct government *gov = preq->source.value.gov;
+              struct government *anarchy = gov->category->anarchy_government;
+
+              if (anarchy) {
+                handle_player_change_government(pplayer, anarchy->index);
+              } else {
+                DIPLO_LOG(LOG_ERROR, pplayer, target, "Could not change "
+                          "government to anarchy.");
+              }
+            }
+          } requirement_list_iterate_end;
+        }
+      } effect_list_iterate_end;
+    } government_iterate_end;
   }
 
   /* This will take us straight to war. */
Index: ai/aitools.h
===================================================================
--- ai/aitools.h        (revision 11446)
+++ ai/aitools.h        (working copy)
@@ -96,7 +96,7 @@
 void ai_choose_role_unit(struct player *pplayer, struct city *pcity,
                          struct ai_choice *choice, int role, int want);
 void ai_advisor_choose_building(struct city *pcity, struct ai_choice *choice);
-bool ai_assess_military_unhappiness(struct city *pcity, struct government *g);
+bool ai_assess_military_unhappiness(struct city *pcity);
 
 bool ai_wants_no_science(struct player *pplayer);
 
Index: ai/aicity.c
===================================================================
--- ai/aicity.c (revision 11446)
+++ ai/aicity.c (working copy)
@@ -469,11 +469,15 @@
          v += c + MIN(ai->stats.units.land, 13);
          break;
        case EFT_ANY_GOVERNMENT:
-         if (!can_change_to_government(pplayer, ai->goal.govt.gov)) {
-           v += MIN(MIN(ai->goal.govt.val, 65),
-               num_unknown_techs_for_goal(pplayer, ai->goal.govt.req) * 10);
-         }
-         break;
+    gov_category_iterate(pcat) {
+      int gov = pcat->index;
+      int num = num_unknown_techs_for_goal(pplayer, ai->goal.govt[gov].req);
+
+      if (!can_change_to_government(pplayer, ai->goal.govt[gov].gov)) {
+        v += MIN(MIN(ai->goal.govt[gov].val, 65), num * 10);
+      }
+    } gov_category_iterate_end;
+    break;
        case EFT_ENABLE_NUKE:
          /* Treat nuke as a Cruise Missile upgrade */
          v += 20 + ai->stats.units.missiles * 5;
Index: ai/aidata.c
===================================================================
--- ai/aidata.c (revision 11446)
+++ ai/aidata.c (working copy)
@@ -512,11 +512,6 @@
   count_my_units(pplayer);
 
   TIMING_LOG(AIT_AIDATA, TIMER_STOP);
-
-  /* Government */
-  TIMING_LOG(AIT_GOVERNMENT, TIMER_START);
-  ai_best_government(pplayer);
-  TIMING_LOG(AIT_GOVERNMENT, TIMER_STOP);
 }
 
 /**************************************************************************
@@ -573,20 +568,15 @@
 }
 
 /**************************************************************************
-  Initialize with sane values.
+  Initialize with sane values.  Warning: Sometimes called twice on the
+  same player (when created, and when starting the game).  May not
+  have correct rulesets loaded when running the first time.
 **************************************************************************/
 void ai_data_init(struct player *pplayer)
 {
   struct ai_data *ai = &aidata[pplayer->player_no];
   int i;
 
-  ai->govt_reeval = 0;
-  ai->government_want = fc_realloc(ai->government_want,
-                                  ((game.control.government_count + 1)
-                                   * sizeof(*ai->government_want)));
-  memset(ai->government_want, 0,
-        (game.control.government_count + 1) * sizeof(*ai->government_want));
-
   ai->wonder_city = 0;
   ai->diplomacy.strategy = WIN_OPEN;
   ai->diplomacy.timer = 0;
@@ -609,4 +599,6 @@
   }
   ai->wants_no_science = FALSE;
   ai->max_num_cities = 10000;
+
+  ai_find_government_personality(pplayer);
 }
Index: ai/aihand.c
===================================================================
--- ai/aihand.c (revision 11446)
+++ ai/aihand.c (working copy)
@@ -26,6 +26,7 @@
 #include "nation.h"
 #include "packets.h"
 #include "player.h"
+#include "rand.h"
 #include "shared.h"
 #include "unit.h"
 #include "timing.h"
@@ -96,20 +97,21 @@
 **************************************************************************/
 static void ai_manage_taxes(struct player *pplayer) 
 {
-  int maxrate = (ai_handicap(pplayer, H_RATES) 
-                 ? get_player_bonus(pplayer, EFT_MAX_RATES) : 100);
+  int maxrate[O_MAX];
   bool celebrate = TRUE;
   int can_celebrate = 0, total_cities = 0;
   int trade = 0; /* total amount of trade generated */
   int expenses = 0; /* total amount of gold upkeep */
+  struct player_economic *economic = &pplayer->economic;
 
   if (!game.info.changable_tax) {
     return; /* This ruleset does not support changing tax rates. */
   }
 
-  if (get_gov_pplayer(pplayer) == game.government_when_anarchy) {
-    return; /* This government does not support changing tax rates. */
-  }
+  output_type_iterate(o) {
+    maxrate[o] = MIN(get_player_output_bonus(pplayer, get_output_type(o),
+                                             EFT_MAX_RATES), 100);
+  } output_type_iterate_end;
 
   /* Find total trade surplus and gold expenses */
   city_list_iterate(pplayer->cities, pcity) {
@@ -119,45 +121,43 @@
 
   /* Find minimum tax rate which gives us a positive balance. We assume
    * that we want science most and luxuries least here, and reverse or 
-   * modify this assumption later. on */
+   * modify this assumption later on. */
+  economic->rates[O_SCIENCE] = maxrate[O_SCIENCE];
+  economic->rates[O_GOLD] = MAX(0, 100 - maxrate[O_GOLD] * 2);
+  economic->rates[O_LUXURY] = (100 - economic->rates[O_SCIENCE]
+                               - economic->rates[O_GOLD]); /* Spillover */
 
-  /* First set tax to the minimal available number */
-  pplayer->economic.science = maxrate; /* Assume we want science here */
-  pplayer->economic.tax = MAX(0, 100 - maxrate * 2); /* If maxrate < 50% */
-  pplayer->economic.luxury = (100 - pplayer->economic.science
-                             - pplayer->economic.tax); /* Spillover */
-
   /* Now find the minimum tax with positive balance */
-  while(pplayer->economic.tax < maxrate
-        && (pplayer->economic.science > 0
-            || pplayer->economic.luxury > 0)) {
+  while (economic->rates[O_GOLD] < maxrate[O_GOLD]
+        && (economic->rates[O_SCIENCE] > 0
+            || economic->rates[O_LUXURY] > 0)) {
     int rates[3], result[3];
     const int SCIENCE = 0, TAX = 1, LUXURY = 2;
 
     /* Assume our entire civilization is one big city, and 
      * distribute total income accordingly. This is a 
      * simplification that speeds up the code significantly. */
-    rates[SCIENCE] = pplayer->economic.science;
-    rates[LUXURY] = pplayer->economic.luxury;
+    rates[SCIENCE] = economic->rates[O_SCIENCE];
+    rates[LUXURY] = economic->rates[O_LUXURY];
     rates[TAX] = 100 - rates[SCIENCE] - rates[LUXURY];
     distribute(trade, 3, rates, result);
 
     if (expenses - result[TAX] > 0) {
-      pplayer->economic.tax += 10;
-      if (pplayer->economic.luxury > 0) {
-        pplayer->economic.luxury -= 10;
+      economic->rates[O_GOLD] += 10;
+      if (economic->rates[O_LUXURY] > 0) {
+        economic->rates[O_LUXURY] -= 10;
       } else {
-        pplayer->economic.science -= 10;
+        economic->rates[O_SCIENCE] -= 10;
       }
     } else {
       /* Ok, got positive balance */
-      if (pplayer->economic.gold < ai_gold_reserve(pplayer)) {
+      if (economic->gold < ai_gold_reserve(pplayer)) {
         /* Need to refill coffers, increase tax a bit */
-        pplayer->economic.tax += 10;
-        if (pplayer->economic.luxury > 0) {
-          pplayer->economic.luxury -= 10;
+        economic->rates[O_GOLD] += 10;
+        if (economic->rates[O_LUXURY] > 0) {
+          economic->rates[O_LUXURY] -= 10;
         } else {
-          pplayer->economic.science -= 10;
+          economic->rates[O_SCIENCE] -= 10;
         }
       }
       /* Done! Break the while loop */
@@ -172,15 +172,15 @@
   /* TODO: Allow celebrate individual cities? No modpacks use this yet. */
   if (get_player_bonus(pplayer, EFT_RAPTURE_GROW) > 0
       && !ai_handicap(pplayer, H_AWAY)) {
-    int luxrate = pplayer->economic.luxury;
-    int scirate = pplayer->economic.science;
+    int luxrate = economic->rates[O_LUXURY];
+    int scirate = economic->rates[O_SCIENCE];
     struct cm_parameter cmp;
     struct cm_result cmr;
 
-    while (pplayer->economic.luxury < maxrate
-           && pplayer->economic.science > 0) {
-      pplayer->economic.luxury += 10;
-      pplayer->economic.science -= 10;
+    while (economic->rates[O_LUXURY] < maxrate[O_LUXURY]
+           && economic->rates[O_SCIENCE] > 0) {
+      economic->rates[O_LUXURY] += 10;
+      economic->rates[O_SCIENCE] -= 10;
     }
 
     cm_init_parameter(&cmp);
@@ -224,8 +224,8 @@
         }
       } city_list_iterate_end;
     } else {
-      pplayer->economic.luxury = luxrate;
-      pplayer->economic.science = scirate;
+      economic->rates[O_LUXURY] = luxrate;
+      economic->rates[O_SCIENCE] = scirate;
       city_list_iterate(pplayer->cities, pcity) {
         /* KLUDGE: Must refresh to restore the original values which 
          * were clobbered in cm_query_result, after the tax rates 
@@ -236,140 +236,68 @@
     }
   }
 
-  if (!celebrate && pplayer->economic.luxury < maxrate) {
+  if (!celebrate && economic->rates[O_LUXURY] < maxrate[O_LUXURY]) {
     /* TODO: Add general luxury code here. */
   }
 
   /* Ok, we now have the desired tax and luxury rates. Do we really want
    * science? If not, swap it with tax if it is bigger. */
   if ((ai_wants_no_science(pplayer) || ai_on_war_footing(pplayer))
-      && pplayer->economic.science > pplayer->economic.tax) {
-    int science = pplayer->economic.science;
+      && economic->rates[O_SCIENCE] > economic->rates[O_GOLD]) {
+    int science = economic->rates[O_SCIENCE];
     /* Swap science and tax */
-    pplayer->economic.science = pplayer->economic.tax;
-    pplayer->economic.tax = science;
+    economic->rates[O_SCIENCE] = economic->rates[O_GOLD];
+    economic->rates[O_GOLD] = science;
   }
 
-  assert(pplayer->economic.tax + pplayer->economic.luxury 
-         + pplayer->economic.science == 100);
+  assert(economic->rates[O_GOLD] + economic->rates[O_LUXURY]
+         + economic->rates[O_SCIENCE] == 100);
   freelog(LOGLEVEL_TAX, "%s rates: Sci=%d Lux=%d Tax=%d trade=%d expenses=%d"
-          " celeb=(%d/%d)", pplayer->name, pplayer->economic.science,
-          pplayer->economic.luxury, pplayer->economic.tax, trade, expenses,
+          " celeb=(%d/%d)", pplayer->name, economic->rates[O_SCIENCE],
+          economic->rates[O_LUXURY], economic->rates[O_GOLD], trade, expenses,
           can_celebrate, total_cities);
   send_player_info(pplayer, pplayer);
 }
 
 /**************************************************************************
-  Find best government to aim for.
-  We do it by setting our government to all possible values and calculating
-  our GDP (total ai_eval_calc_city) under this government.  If the very
-  best of the governments is not available to us (it is not yet discovered),
-  we record it in the goal.gov structure with the aim of wanting the
-  necessary tech more.  The best of the available governments is recorded 
-  in goal.revolution.  We record the want of each government, and only
-  recalculate this data every ai->govt_reeval_turns turns.
-
-  Note: Call this _before_ doing taxes!
+  Find a personality for this AI.  Entirely random.
 **************************************************************************/
-void ai_best_government(struct player *pplayer)
+void ai_find_government_personality(struct player *pplayer)
 {
   struct ai_data *ai = ai_data_get(pplayer);
-  int best_val = 0;
-  int bonus = 0; /* in percentage */
-  struct government *current_gov = pplayer->government;
+  int count = 0, choice;
 
-  ai->goal.govt.gov = pplayer->government;
-  ai->goal.govt.val = 0;
-  ai->goal.govt.req = A_UNSET;
-  ai->goal.revolution = pplayer->government;
+  gov_category_iterate(cat) {
+    ai->goal.govt[cat->index].gov = NULL;
+    ai->goal.govt[cat->index].val = 0;
+    ai->goal.govt[cat->index].req = A_UNSET;
 
-  if (ai_handicap(pplayer, H_AWAY) || !pplayer->is_alive) {
-    return;
-  }
-
-  if (ai->govt_reeval == 0) {
+    count = 0;
     government_iterate(gov) {
-      int val = 0;
-      int dist;
-
-      if (gov == game.government_when_anarchy) {
-        continue; /* pointless */
+      if (gov->category == cat && !gov->ai.better) {
+        count++; /* number of relevant governments in this category */
       }
-      if (gov->ai.better
-          && can_change_to_government(pplayer, gov->ai.better)) {
-        continue; /* we have better governments available */
+    } government_iterate_end;
+    choice = myrand(count);
+    count = 0;
+    government_iterate(gov) {
+      if (gov->category == cat && !gov->ai.better) {
+        if (count == choice) {
+          ai->goal.govt[cat->index].gov = gov;
+          ai->goal.govt[cat->index].val = 10000; /* FIXME: WAG */
+          /* FIXME: assumes only one requirement which is a tech */
+          requirement_vector_iterate(&gov->reqs, preq) {
+            if (preq->source.type == REQ_TECH) {
+              ai->goal.govt[cat->index].req = preq->source.value.tech;
+            }
+          } requirement_vector_iterate_end;
+          break;
+        }
+        count++;
       }
-      pplayer->government = gov;
-      /* Ideally we should change tax rates here, but since
-       * this is a rather big CPU operation, we'd rather not. */
-      check_player_government_rates(pplayer);
-      city_list_iterate(pplayer->cities, acity) {
-        auto_arrange_workers(acity);
-      } city_list_iterate_end;
-      city_list_iterate(pplayer->cities, pcity) {
-        val += ai_eval_calc_city(pcity, ai);
-      } city_list_iterate_end;
-
-      /* Bonuses for non-economic abilities. We increase val by
-       * a very small amount here to choose govt in cases where
-       * we have no cities yet. */
-      bonus += get_player_bonus(pplayer, EFT_VETERAN_BUILD) ? 3 : 0;
-      bonus -= get_player_bonus(pplayer, EFT_REVOLUTION_WHEN_UNHAPPY) ? 3 : 0;
-      bonus += get_player_bonus(pplayer, EFT_NO_INCITE) ? 4 : 0;
-      bonus += get_player_bonus(pplayer, EFT_UNBRIBABLE_UNITS) ? 2 : 0;
-      bonus += get_player_bonus(pplayer, EFT_INSPIRE_PARTISANS) ? 3 : 0;
-      bonus += get_player_bonus(pplayer, EFT_RAPTURE_GROW) ? 2 : 0;
-      bonus += get_player_bonus(pplayer, EFT_FANATICS) ? 3 : 0;
-      bonus += get_player_bonus(pplayer, EFT_OUTPUT_INC_TILE) * 8;
-
-      val += (val * bonus) / 100;
-
-      /* FIXME: handle reqs other than technologies. */
-      dist = 0;
-      requirement_vector_iterate(&gov->reqs, preq) {
-       if (preq->source.type == REQ_TECH) {
-         dist += MAX(1, num_unknown_techs_for_goal(pplayer,
-                                                   preq->source.value.tech));
-       }
-      } requirement_vector_iterate_end;
-      val = amortize(val, dist);
-      ai->government_want[gov->index] = val; /* Save want */
     } government_iterate_end;
-    /* Now reset our gov to it's real state. */
-    pplayer->government = current_gov;
-    city_list_iterate(pplayer->cities, acity) {
-      /* This isn't strictly necessary since it's done in aaw. */
-      generic_city_refresh(acity, TRUE, NULL);
-      auto_arrange_workers(acity);
-    } city_list_iterate_end;
-    ai->govt_reeval = CLIP(5, city_list_size(pplayer->cities), 20);
-  }
-  ai->govt_reeval--;
-
-  /* Figure out which government is the best for us this turn. */
-  government_iterate(gov) {
-    if (ai->government_want[gov->index] > best_val 
-        && can_change_to_government(pplayer, gov)) {
-      best_val = ai->government_want[gov->index];
-      ai->goal.revolution = gov;
-    }
-    if (ai->government_want[gov->index] > ai->goal.govt.val) {
-      ai->goal.govt.gov = gov;
-      ai->goal.govt.val = ai->government_want[gov->index];
-
-      /* FIXME: handle reqs other than technologies. */
-      ai->goal.govt.req = A_NONE;
-      requirement_vector_iterate(&gov->reqs, preq) {
-       if (preq->source.type == REQ_TECH) {
-         ai->goal.govt.req = preq->source.value.tech;
-         break;
-       }
-      } requirement_vector_iterate_end;
-    }
-  } government_iterate_end;
-  /* Goodness of the ideal gov is calculated relative to the goodness of the
-   * best of the available ones. */
-  ai->goal.govt.val -= best_val;
+    assert(ai->goal.govt[cat->index].gov != NULL);
+  } gov_category_iterate_end;
 }
 
 /**************************************************************************
@@ -383,30 +311,39 @@
     return;
   }
 
-  if (ai->goal.revolution != pplayer->government) {
-    ai_government_change(pplayer, ai->goal.revolution); /* change */
-  }
+  gov_category_iterate(cat) {
+    int idx = cat->index;
+    struct government *gov = pplayer->govs[idx].current_government;
 
-  /* Crank up tech want */
-  if (ai->goal.govt.req == A_UNSET
-      || get_invention(pplayer, ai->goal.govt.req) == TECH_KNOWN) {
-    return; /* already got it! */
-  } else if (ai->goal.govt.val > 0) {
-    /* We have few cities in the beginning, compensate for this to ensure
-     * that we are sufficiently forward-looking. */
-    int want = MAX(ai->goal.govt.val, 100);
-    struct nation_type *pnation = get_nation_by_plr(pplayer);
+    if (can_change_to_government(pplayer, ai->goal.govt[idx].gov)) {
+      ai_government_change(pplayer, ai->goal.govt[idx].gov);
+      continue;
+    }
+    if (gov->ai.better
+         && can_change_to_government(pplayer, gov->ai.better)) {
+      ai_government_change(pplayer, gov->ai.better);
+      continue;
+    }
 
-    if (pplayer->government == pnation->init_government) {
-      /* Default government is the crappy one we start in (like Despotism).
-       * We want something better pretty soon! */
-      want += 25 * game.info.turn;
+    /* Crank up tech want */
+    if (ai->goal.govt[idx].req != A_UNSET
+        && get_invention(pplayer, ai->goal.govt[idx].req) != TECH_KNOWN) {
+      /* We have few cities in the beginning, compensate for this to ensure
+       * that we are sufficiently forward-looking. */
+      int want = MAX(ai->goal.govt[idx].val, 100);
+      struct nation_type *pnation = get_nation_by_plr(pplayer);
+
+      if (gov == pnation->init_government) {
+        /* Default government is the default one (like Despotism).
+         * We probably want something better pretty soon! */
+        want += 25 * game.info.turn;
+      }
+      pplayer->ai.tech_want[ai->goal.govt[idx].req] += want;
+      TECH_LOG(LOG_DEBUG, pplayer, ai->goal.govt[idx].req, "+ %d for %s in "
+               "ai_manage_government", want,
+               get_government_name(ai->goal.govt[idx].gov));
     }
-    pplayer->ai.tech_want[ai->goal.govt.req] += want;
-    TECH_LOG(LOG_DEBUG, pplayer, ai->goal.govt.req, "+ %d for %s in "
-             "ai_manage_government", want,
-             get_government_name(ai->goal.govt.gov));
-  }
+  } gov_category_iterate_end;
 }
 
 /**************************************************************************
Index: ai/advmilitary.c
===================================================================
--- ai/advmilitary.c    (revision 11446)
+++ ai/advmilitary.c    (working copy)
@@ -801,8 +801,7 @@
   int orig_move_type = get_unit_type(best_choice->choice)->move_type;
   int victim_count = 1;
   int needferry = 0;
-  bool unhap = ai_assess_military_unhappiness(pcity,
-                                              get_gov_pplayer(pplayer));
+  bool unhap = ai_assess_military_unhappiness(pcity);
 
   assert(orig_move_type == SEA_MOVING || orig_move_type == LAND_MOVING);
 
Index: ai/aidata.h
===================================================================
--- ai/aidata.h (revision 11446)
+++ ai/aidata.h (working copy)
@@ -140,18 +140,13 @@
   int angry_priority;
   int pollution_priority;
 
-  /* Government data */
-  int *government_want;
-  short govt_reeval;
-
   /* Goals */
   struct {
     struct {
       struct government *gov;        /* The ideal government */
       int val;        /* Its value (relative to the current gov) */
       int req;        /* The tech requirement for the ideal gov */
-    } govt;
-    struct government *revolution;   /* The best gov of the now available */
+    } govt[MAX_NUM_CATEGORIES];
   } goal;
   
   /* If the ai doesn't want/need any research */
Index: ai/aihand.h
===================================================================
--- ai/aihand.h (revision 11446)
+++ ai/aihand.h (working copy)
@@ -21,6 +21,6 @@
 
 bool is_unit_choice_type(enum choice_type type);
 
-void ai_best_government(struct player *pplayer);
+void ai_find_government_personality(struct player *pplayer);
 
 #endif  /* FC__AIHAND_H */
Index: ai/aiunit.c
===================================================================
--- ai/aiunit.c (revision 11446)
+++ ai/aiunit.c (working copy)
@@ -1298,7 +1298,7 @@
   if (pcity && (punit->id == 0 || pcity->id == punit->homecity)) {
     /* I would have thought unhappiness should be taken into account 
      * irrespectfully the city in which it will surface...  GB */ 
-    unhap = ai_assess_military_unhappiness(pcity, get_gov_pplayer(pplayer));
+    unhap = ai_assess_military_unhappiness(pcity);
   }
 
   move_rate = unit_move_rate(punit);
Index: ai/aisettler.c
===================================================================
--- ai/aisettler.c      (revision 11446)
+++ ai/aisettler.c      (working copy)
@@ -21,7 +21,6 @@
 
 #include "city.h"
 #include "game.h"
-#include "government.h"
 #include "map.h"
 #include "mem.h"
 #include "movement.h"
@@ -49,26 +48,6 @@
 
 #include "aisettler.h"
 
-/* COMMENTS */
-/* 
-   This code tries hard to do the right thing, including looking
-   into the future (wrt to government), and also doing this in a
-   modpack friendly manner. However, there are some pieces missing.
-
-   A tighter integration into the city management code would 
-   give more optimal city placements, since existing cities could
-   move their workers around to give a new city better placement.
-   Occasionally you will see cities being placed sub-optimally
-   because the best city center tile is taken when another tile
-   could have been worked instead by the city that took it.
-
-   The code is not generic enough. It assumes smallpox too much,
-   and should calculate with a future city of a bigger size.
-
-   We need to stop the build code from running this code all the
-   time and instead try to complete what it has started.
-*/
-
 /* A big WAG to save a big amount of CPU: */
 #define RESULT_IS_ENOUGH 250
 
@@ -105,8 +84,6 @@
   Fill cityresult struct with useful info about the city spot. It must 
   contain valid x, y coordinates and total should be zero.
 
-  We assume whatever best government we are aiming for.
-
   We always return valid other_x and other_y if total > 0.
 **************************************************************************/
 void cityresult_fill(struct player *pplayer,
@@ -116,11 +93,8 @@
   struct city *pcity = tile_get_city(result->tile);
   int sum = 0;
   bool virtual_city = FALSE;
-  struct government *curr_govt = pplayer->government;
   bool handicap = ai_handicap(pplayer, H_MAP);
 
-  pplayer->government = ai->goal.govt.gov;
-
   result->best_other = 0;
   result->other_tile = NULL;
   result->o_x = -1; /* as other_x but city-relative */
@@ -259,7 +233,6 @@
   result->total -= result->waste;
   result->total = MAX(0, result->total);
 
-  pplayer->government = curr_govt;
   if (virtual_city) {
     remove_city_virtual(pcity);
   }
Index: client/gui-gtk-2.0/mapview.c
===================================================================
--- client/gui-gtk-2.0/mapview.c        (revision 11446)
+++ client/gui-gtk-2.0/mapview.c        (working copy)
@@ -117,15 +117,15 @@
   if (game.player_ptr) {
     int d = 0;
 
-    for (; d < game.player_ptr->economic.luxury /10; d++) {
+    for (; d < game.player_ptr->economic.rates[O_LUXURY] /10; d++) {
       struct sprite *sprite = get_tax_sprite(tileset, O_LUXURY);
 
       gtk_image_set_from_pixbuf(GTK_IMAGE(econ_label[d]),
                                sprite_get_pixbuf(sprite));
     }
  
-    for (; d < (game.player_ptr->economic.science
-               + game.player_ptr->economic.luxury) / 10; d++) {
+    for (; d < (game.player_ptr->economic.rates[O_SCIENCE]
+         + game.player_ptr->economic.rates[O_LUXURY]) / 10; d++) {
       struct sprite *sprite = get_tax_sprite(tileset, O_SCIENCE);
 
       gtk_image_set_from_pixbuf(GTK_IMAGE(econ_label[d]),
Index: client/gui-gtk-2.0/menu.c
===================================================================
--- client/gui-gtk-2.0/menu.c   (revision 11446)
+++ client/gui-gtk-2.0/menu.c   (working copy)
@@ -738,11 +738,6 @@
        NULL,                   0,                                      
"<Separator>"   },
   { "/" N_("Government") "/" N_("_Change Government"),           NULL,
        NULL,                   0,                                      
"<Branch>"      },
-  { "/" N_("Government") "/" N_("_Change Government") "/" N_("_Revolution..."),
-                                                        "<shift>r",
-       government_menu_callback,       MENU_GOVERNMENT_REVOLUTION              
        },
-  { "/" N_("_Government") "/" N_("_Change Government") "/sep1", NULL,
-       NULL,                   0,                                      
"<Separator>"   },
   /* View menu ... */
   { "/" N_("_View"),                                   NULL,
        NULL,                   0,                                      
"<Branch>"      },
@@ -1217,22 +1212,38 @@
 
     if (parent) {
       GList *list, *iter;
+      int num_cats = 0;
 
       /* remove previous government entries. */
       list = gtk_container_get_children(GTK_CONTAINER(parent));
-      for (iter = g_list_nth(list, 2); iter; iter = g_list_next(iter)) {
+      for (iter = g_list_nth(list, 0); iter; iter = g_list_next(iter)) {
        gtk_widget_destroy(GTK_WIDGET(iter->data));
       }
       g_list_free(list);
 
+      /* Count number of categories to not show any in simple gov case */
+      gov_category_iterate(c) {
+        if (c) { /* quelch unused warning */
+          num_cats++;
+        }
+      } gov_category_iterate_end;
+
       /* add new government entries. */
-      government_iterate(g) {
-        if (g != game.government_when_anarchy) {
+      gov_category_iterate(c) {
+        government_iterate(g) {
           GtkWidget *item, *image;
           struct sprite *gsprite;
          char buf[256];
 
-         my_snprintf(buf, sizeof(buf), _("%s..."), g->name);
+          if (g->category != c) {
+            continue;
+          }
+
+          if (num_cats == 1) {
+            my_snprintf(buf, sizeof(buf), _("%s..."), g->name);
+          } else {
+            my_snprintf(buf, sizeof(buf), _("%s (%s)..."), g->name, c->name);
+          }
           item = gtk_image_menu_item_new_with_label(buf);
 
          if ((gsprite = get_government_sprite(tileset, g))) {
@@ -1250,8 +1261,8 @@
 
           gtk_menu_shell_append(GTK_MENU_SHELL(parent), item);
           gtk_widget_show(item);
-        }
-      } government_iterate_end;
+        } government_iterate_end;
+      } gov_category_iterate_end;
     }
 
     menus_set_sensitive("<main>/_Reports", TRUE);
Index: client/gui-gtk-2.0/dialogs.c
===================================================================
--- client/gui-gtk-2.0/dialogs.c        (revision 11446)
+++ client/gui-gtk-2.0/dialogs.c        (working copy)
@@ -228,11 +228,7 @@
   struct government *government = data;
 
   if (response == GTK_RESPONSE_YES) {
-    if (!government) {
-      start_revolution();
-    } else {
-      set_government_choice(government);
-    }
+    set_government_choice(government);
   }
   if (w) {
     gtk_widget_destroy(w);
@@ -246,29 +242,20 @@
 {
   static GtkWidget *shell = NULL;
 
-  if (game.player_ptr->revolution_finishes < 0) {
-    if (!shell) {
-      shell = gtk_message_dialog_new(NULL,
-         0,
-         GTK_MESSAGE_WARNING,
-         GTK_BUTTONS_YES_NO,
-         _("You say you wanna revolution?"));
-      gtk_window_set_title(GTK_WINDOW(shell), _("Revolution!"));
-      setup_dialog(shell, toplevel);
-
-      g_signal_connect(shell, "destroy",
-         G_CALLBACK(gtk_widget_destroyed), &shell);
-    }
-    g_signal_connect(shell, "response",
-       G_CALLBACK(revolution_response), government);
-
-    gtk_window_present(GTK_WINDOW(shell));
-  } else {
-    revolution_response(shell, GTK_RESPONSE_YES, government);
+  if (!shell) {
+    shell = gtk_message_dialog_new(NULL, 0, GTK_MESSAGE_WARNING,
+                                    GTK_BUTTONS_YES_NO, 
+                                   _("You say you wanna revolution?"));
+    gtk_window_set_title(GTK_WINDOW(shell), _("Revolution!"));
+    setup_dialog(shell, toplevel);
+    g_signal_connect(shell, "destroy", G_CALLBACK(gtk_widget_destroyed), 
+                     &shell);
   }
+  g_signal_connect(shell, "response", G_CALLBACK(revolution_response), 
+                   government);
+  gtk_window_present(GTK_WINDOW(shell));
 }
 
-
 /****************************************************************
 ...
 *****************************************************************/
Index: client/gui-gtk-2.0/diplodlg.c
===================================================================
--- client/gui-gtk-2.0/diplodlg.c       (revision 11446)
+++ client/gui-gtk-2.0/diplodlg.c       (working copy)
@@ -544,8 +544,7 @@
   label = gtk_label_new(NULL);
   gtk_misc_set_alignment(GTK_MISC(label), 0.5, 0.5);
   my_snprintf(buf, sizeof(buf),
-             "<span size=\"large\" weight=\"bold\">%s %s</span>",
-             get_ruler_title(plr0->government, plr0->is_male, plr0->nation), 
plr0->name);
+             "<span size=\"large\" weight=\"bold\">%s</span>", plr0->name);
   gtk_label_set_markup(GTK_LABEL(label), buf);
   gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
 
@@ -617,8 +616,7 @@
   label = gtk_label_new(NULL);
   gtk_misc_set_alignment(GTK_MISC(label), 0.5, 0.5);
   my_snprintf(buf, sizeof(buf),
-             "<span size=\"large\" weight=\"bold\">%s %s</span>",
-             get_ruler_title(plr1->government, plr1->is_male, plr1->nation), 
plr1->name);
+             "<span size=\"large\" weight=\"bold\">%s</span>", plr1->name);
   gtk_label_set_markup(GTK_LABEL(label), buf);
   gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
 
Index: client/gui-gtk-2.0/inteldlg.c
===================================================================
--- client/gui-gtk-2.0/inteldlg.c       (revision 11446)
+++ client/gui-gtk-2.0/inteldlg.c       (working copy)
@@ -353,11 +353,10 @@
 
        switch (i) {
          case LABEL_RULER:
-           my_snprintf(buf, sizeof(buf), "%s %s", 
-               get_ruler_title(p->government, p->is_male, p->nation), p->name);
+           my_snprintf(buf, sizeof(buf), "%s", p->name);
            break;
          case LABEL_GOVERNMENT:
-           sz_strlcpy(buf, get_government_name(p->government));
+           sz_strlcpy(buf, p->govs[0].current_government->name);
            break;
          case LABEL_CAPITAL:
            pcity = find_palace(p);
@@ -367,13 +366,13 @@
            my_snprintf(buf, sizeof(buf), "%d", p->economic.gold);
            break;
          case LABEL_TAX:
-           my_snprintf(buf, sizeof(buf), "%d%%", p->economic.tax);
+           my_snprintf(buf, sizeof(buf), "%d%%", p->economic.rates[O_GOLD]);
            break;
          case LABEL_SCIENCE:
-           my_snprintf(buf, sizeof(buf), "%d%%", p->economic.science);
+           my_snprintf(buf, sizeof(buf), "%d%%", p->economic.rates[O_SCIENCE]);
            break;
          case LABEL_LUXURY:
-           my_snprintf(buf, sizeof(buf), "%d%%", p->economic.luxury);
+           my_snprintf(buf, sizeof(buf), "%d%%", p->economic.rates[O_LUXURY]);
            break;
          case LABEL_RESEARCHING: {
            struct player_research* research = get_player_research(p);
Index: client/gui-gtk-2.0/gamedlgs.c
===================================================================
--- client/gui-gtk-2.0/gamedlgs.c       (revision 11446)
+++ client/gui-gtk-2.0/gamedlgs.c       (working copy)
@@ -324,9 +324,9 @@
     g_signal_connect_after(rates_sci_adj, "value_changed",
                           G_CALLBACK(rates_changed_callback), NULL);
 
-  rates_set_values( game.player_ptr->economic.tax, 0,
-                   game.player_ptr->economic.luxury, 0,
-                   game.player_ptr->economic.science, 0 );
+  rates_set_values(game.player_ptr->economic.rates[O_GOLD], 0,
+                   game.player_ptr->economic.rates[O_LUXURY], 0,
+                   game.player_ptr->economic.rates[O_SCIENCE], 0);
   return shell;
 }
 
@@ -351,9 +351,8 @@
     return;
   }
 
-  my_snprintf(buf, sizeof(buf), _("%s max rate: %d%%"),
-      get_government_name(game.player_ptr->government),
-      get_player_bonus(game.player_ptr, EFT_MAX_RATES));
+  my_snprintf(buf, sizeof(buf), _("Your max rate: %d%%"),
+              get_player_bonus(game.player_ptr, EFT_MAX_RATES));
   gtk_label_set_text(GTK_LABEL(rates_gov_label), buf);
   
   gtk_window_present(GTK_WINDOW(rates_dialog_shell));
Index: client/text.c
===================================================================
--- client/text.c       (revision 11446)
+++ client/text.c       (working copy)
@@ -574,9 +574,9 @@
     astr_add_line(&str, _("Gold: %d (%+d)"), game.player_ptr->economic.gold,
                  player_get_expected_income(game.player_ptr));
     astr_add_line(&str, _("Tax: %d Lux: %d Sci: %d"),
-                 game.player_ptr->economic.tax,
-                 game.player_ptr->economic.luxury,
-                 game.player_ptr->economic.science);
+                 game.player_ptr->economic.rates[O_GOLD],
+                 game.player_ptr->economic.rates[O_LUXURY],
+                 game.player_ptr->economic.rates[O_SCIENCE]);
   }
   if (!game.info.simultaneous_phases) {
     astr_add_line(&str, _("Moving: %s"), get_player(game.info.phase)->name);
@@ -609,9 +609,9 @@
                  player_get_expected_income(game.player_ptr));
     /* TRANS: Gold, luxury, and science rates are in percentage values. */
     astr_add_line(&str, _("Tax rates: Gold:%d%% Luxury:%d%% Science:%d%%"),
-                 game.player_ptr->economic.tax,
-                 game.player_ptr->economic.luxury,
-                 game.player_ptr->economic.science);
+                 game.player_ptr->economic.rates[O_GOLD],
+                 game.player_ptr->economic.rates[O_LUXURY],
+                 game.player_ptr->economic.rates[O_SCIENCE]);
     astr_add_line(&str, _("Researching %s: %s"),
                  get_tech_name(game.player_ptr, research->researching),
                  get_science_target_text(NULL));
@@ -627,8 +627,9 @@
                DIVIDE(game.info.cooling - game.info.coolinglevel + 1, 2));
 
   if (game.player_ptr) {
+    /* FIXME: Only shows first government */
     astr_add_line(&str, _("Government: %s"),
-                 get_government_name(game.player_ptr->government));
+                 game.player_ptr->govs[0].current_government->name);
   }
 
   return str.str;
@@ -874,7 +875,8 @@
 
   astr_add_line(&str, _("Shows your current government:"));
   if (game.player_ptr) {
-    astr_add_line(&str, get_government_name(game.player_ptr->government));
+    /* FIXME: Only shows first goverment */
+    astr_add_line(&str, game.player_ptr->govs[0].current_government->name);
   }
   return str.str;
 }
@@ -1043,13 +1045,10 @@
   if (game.player_ptr) {
     /* TRANS: "Republic of the Polish" */
     astr_add_line(&str, _("%s of the %s"),
-                 get_government_name(game.player_ptr->government),
+                 
get_government_name(game.player_ptr->govs[0].current_government),
                  get_nation_name_plural(game.player_ptr->nation));
 
-    astr_add_line(&str, "%s %s: %s",
-                 get_ruler_title(game.player_ptr->government,
-                                 game.player_ptr->is_male,
-                                 game.player_ptr->nation),
+    astr_add_line(&str, "%s: %s",
                  game.player_ptr->name,
                  textyear(game.info.year));
   } else {
Index: client/repodlgs_common.c
===================================================================
--- client/repodlgs_common.c    (revision 11446)
+++ client/repodlgs_common.c    (working copy)
@@ -73,13 +73,6 @@
       entries[*num_entries_used].total_cost = cost;
       entries[*num_entries_used].cost = cost / count;
       (*num_entries_used)++;
-
-      /* Currently there is no building expense under anarchy.  It's
-       * not a good idea to hard-code this in the client, but what
-       * else can we do? */
-      if (game.player_ptr->government != game.government_when_anarchy) {
-        *total_cost += cost;
-      }
     }
   } impr_type_iterate_end;
 
@@ -111,8 +104,7 @@
   }
 
   unit_type_iterate(unittype) {
-    cost = utype_upkeep_cost(unittype, game.player_ptr,
-                             get_gov_pplayer(game.player_ptr), O_GOLD);
+    cost = utype_upkeep_cost(unittype, game.player_ptr, O_GOLD);
 
     if (cost == 0) {
       /* Short-circuit all of the following checks. */
Index: client/packhand.c
===================================================================
--- client/packhand.c   (revision 11446)
+++ client/packhand.c   (working copy)
@@ -1379,8 +1379,6 @@
   
   game.info = *pinfo;
 
-  game.government_when_anarchy
-    = get_government(game.info.government_when_anarchy_id);
   game.player_ptr = get_player(game.info.player_idx);
   if (get_client_state() == CLIENT_PRE_GAME_STATE) {
     popdown_races_dialog();
@@ -1428,29 +1426,19 @@
 }
 
 /**************************************************************************
-  Sets the target government.  This will automatically start a revolution
-  if the target government differs from the current one.
+  Sets the target government.
 **************************************************************************/
 void set_government_choice(struct government *government)
 {
+  assert(government != NULL);
   if (can_client_issue_orders()
       && game.player_ptr
-      && government != game.player_ptr->government) {
+      && !player_has_government(game.player_ptr, government)) {
     dsend_packet_player_change_government(&aconnection, government->index);
   }
 }
 
 /**************************************************************************
-  Begin a revolution by telling the server to start it.  This also clears
-  the current government choice.
-**************************************************************************/
-void start_revolution(void)
-{
-  dsend_packet_player_change_government(&aconnection,
-                                       game.info.government_when_anarchy_id);
-}
-
-/**************************************************************************
 ...
 **************************************************************************/
 void handle_player_info(struct packet_player_info *pinfo)
@@ -1470,11 +1458,19 @@
   pplayer->score.game = pinfo->score;
 
   pplayer->economic.gold=pinfo->gold;
-  pplayer->economic.tax=pinfo->tax;
-  pplayer->economic.science=pinfo->science;
-  pplayer->economic.luxury=pinfo->luxury;
-  pplayer->government = get_government(pinfo->government);
-  pplayer->target_government = get_government(pinfo->target_government);
+  pplayer->economic.rates[O_GOLD] = pinfo->tax;
+  pplayer->economic.rates[O_SCIENCE] = pinfo->science;
+  pplayer->economic.rates[O_LUXURY] = pinfo->luxury;
+
+  gov_category_iterate(pcat) {
+    int i = pcat->index;
+
+    pplayer->govs[i].current_government 
+        = get_government(pinfo->current_government[i]);
+    pplayer->govs[i].current_step = pinfo->current_step[i];
+    pplayer->govs[i].turn_changed = pinfo->turn_changed[i];
+  } gov_category_iterate_end;
+
   BV_CLR_ALL(pplayer->embassy);
   players_iterate(pother) {
     if (pinfo->embassy[pother->player_no]) {
@@ -1590,7 +1586,6 @@
   pplayer->is_alive=pinfo->is_alive;
 
   pplayer->ai.barbarian_type = pinfo->barbarian_type;
-  pplayer->revolution_finishes = pinfo->revolution_finishes;
   pplayer->ai.skill_level = pinfo->ai_skill_level;
 
   update_players_dialog();
@@ -2088,6 +2083,7 @@
 
   game.control = *packet;
   governments_alloc(packet->government_count);
+  gov_categories_alloc(packet->gov_categories_count);
   nations_alloc(packet->nation_count);
   city_styles_alloc(packet->styles_count);
 }
@@ -2241,6 +2237,28 @@
 }
 
 /**************************************************************************
+  Receive government categories.
+**************************************************************************/
+void handle_ruleset_gov_category(struct packet_ruleset_gov_category *p)
+{
+  struct gov_category *pcat = &gov_categories[p->index];
+  int i;
+
+  pcat->index = p->index;
+  sz_strlcpy(pcat->name, p->name);
+  for (i = 0; i < MAX_NUM_GOV_STEPS; i++) {
+    pcat->steps[i] = p->steps[i];
+  }
+  pcat->background_colour = p->colour;
+  if (p->anarchy_government != -1) {
+    /* This is safe to do because we send governments first. */
+    pcat->anarchy_government = get_government(p->anarchy_government);
+  } else {
+    pcat->anarchy_government = NULL;
+  }
+}
+
+/**************************************************************************
 ...
 **************************************************************************/
 void handle_ruleset_government(struct packet_ruleset_government *p)
@@ -2263,47 +2281,22 @@
   }
   assert(gov->reqs.size == p->reqs_count);
 
-  gov->num_ruler_titles    = p->num_ruler_titles;
-    
+  gov->min_turns = p->min_turns;
+  gov->category = &gov_categories[p->category];
   sz_strlcpy(gov->name_orig, p->name);
   gov->name = Q_(gov->name_orig); /* See translate_data_names */
   sz_strlcpy(gov->graphic_str, p->graphic_str);
   sz_strlcpy(gov->graphic_alt, p->graphic_alt);
-
-  gov->ruler_titles = fc_calloc(gov->num_ruler_titles,
-                               sizeof(struct ruler_title));
-
+  if (p->entry_from != -1) {
+    gov->entry_from = &governments[p->entry_from];
+  } else {
+    gov->entry_from = NULL;
+  }
   gov->helptext = mystrdup(p->helptext);
   
   tileset_setup_government(tileset, p->id);
 }
 
-void handle_ruleset_government_ruler_title
-  (struct packet_ruleset_government_ruler_title *p)
-{
-  struct government *gov;
-
-  if(p->gov < 0 || p->gov >= game.control.government_count) {
-    freelog(LOG_ERROR, "Received bad government num %d for title", p->gov);
-    return;
-  }
-  gov = &governments[p->gov];
-  if(p->id < 0 || p->id >= gov->num_ruler_titles) {
-    freelog(LOG_ERROR, "Received bad ruler title num %d for %s title",
-           p->id, gov->name);
-    return;
-  }
-  gov->ruler_titles[p->id].nation = get_nation_by_idx(p->nation);
-  sz_strlcpy(gov->ruler_titles[p->id].male_title_orig, p->male_title);
-  /* See translate_data_names */
-  gov->ruler_titles[p->id].male_title
-    = Q_(gov->ruler_titles[p->id].male_title_orig);
-  sz_strlcpy(gov->ruler_titles[p->id].female_title_orig, p->female_title);
-  /* See translate_data_names */
-  gov->ruler_titles[p->id].female_title
-    = Q_(gov->ruler_titles[p->id].female_title_orig);
-}
-
 /**************************************************************************
 ...
 **************************************************************************/
@@ -2437,7 +2430,11 @@
          sizeof(pl->init_buildings));
   memcpy(pl->init_units, p->init_units, 
          sizeof(pl->init_units));
-  pl->init_government = get_government(p->init_government);
+  if (p->init_government >= 0) {
+    pl->init_government = get_government(p->init_government);
+  } else {
+    pl->init_government = NULL;
+  }
 
   if (p->legend[0] != '\0') {
     pl->legend = mystrdup(_(p->legend));
Index: client/packhand.h
===================================================================
--- client/packhand.h   (revision 11446)
+++ client/packhand.h   (working copy)
@@ -27,6 +27,5 @@
 void play_sound_for_event(enum event_type type);
 void target_government_init(void);
 void set_government_choice(struct government *government);
-void start_revolution(void);
 
 #endif /* FC__PACKHAND_H */
Index: client/helpdata.c
===================================================================
--- client/helpdata.c   (revision 11446)
+++ client/helpdata.c   (working copy)
@@ -155,7 +155,7 @@
     "Requires the Communism technology.\n\n"
 *****************************************************************/
 static void insert_requirement(struct requirement *req,
-                              char *buf, size_t bufsz)
+                              char *buf, size_t bufsz, bool gov)
 {
   switch (req->source.type) {
   case REQ_NONE:
@@ -163,51 +163,59 @@
   case REQ_LAST:
     break;
   case REQ_TECH:
-    cat_snprintf(buf, bufsz, _("Requires the %s technology.\n\n"),
+    cat_snprintf(buf, bufsz, _("Requires the %s technology."),
                 get_tech_name(game.player_ptr, req->source.value.tech));
     return;
   case REQ_GOV:
-    cat_snprintf(buf, bufsz, _("Requires the %s government.\n\n"),
-                get_government_name(req->source.value.gov));
+    if (!gov) {
+      cat_snprintf(buf, bufsz, _("Requires the %s government."),
+                   get_government_name(req->source.value.gov));
+    }
     return;
   case REQ_BUILDING:
-    cat_snprintf(buf, bufsz, _("Requires the %s building.\n\n"),
+    cat_snprintf(buf, bufsz, _("Requires the %s building."),
                 get_improvement_name(req->source.value.building));
     return;
   case REQ_SPECIAL:
-    cat_snprintf(buf, bufsz, _("Requires the %s terrain special.\n\n"),
+    cat_snprintf(buf, bufsz, _("Requires the %s terrain special."),
                 get_special_name(req->source.value.special));
     return;
   case REQ_TERRAIN:
-    cat_snprintf(buf, bufsz, _("Requires the %s terrain.\n\n"),
+    cat_snprintf(buf, bufsz, _("Requires the %s terrain."),
                 get_name(req->source.value.terrain));
     return;
   case REQ_NATION:
-    cat_snprintf(buf, bufsz, _("Requires the %s nation.\n\n"),
+    cat_snprintf(buf, bufsz, _("Requires the %s nation."),
                 get_nation_name(req->source.value.nation));
     return;
   case REQ_UNITTYPE:
-    cat_snprintf(buf, bufsz, _("Only applies to %s units.\n\n"),
+    cat_snprintf(buf, bufsz, _("Only applies to %s units."),
                 unit_name(req->source.value.unittype));
     return;
   case REQ_UNITFLAG:
-    cat_snprintf(buf, bufsz, _("Only applies to %s units.\n\n"),
-                get_unit_flag_name(req->source.value.unitflag));
+    if (!gov) {
+      cat_snprintf(buf, bufsz, _("Only applies to %s units."),
+                   get_unit_flag_name(req->source.value.unitflag));
+    }
     return;
   case REQ_UNITCLASS:
-    cat_snprintf(buf, bufsz, _("Only applies to %s units.\n\n"),
+    cat_snprintf(buf, bufsz, _("Only applies to %s units."),
                 unit_class_name(req->source.value.unitclass));
     return;
   case REQ_OUTPUTTYPE:
-    cat_snprintf(buf, bufsz, _("Applies only to %s.\n\n"),
-                get_output_name(req->source.value.outputtype));
+    if (!gov) {
+      cat_snprintf(buf, bufsz, _("Applies only to %s."),
+                   get_output_name(req->source.value.outputtype));
+    }
     return;
   case REQ_SPECIALIST:
-    cat_snprintf(buf, bufsz, _("Applies only to %s.\n\n"),
-                _(get_specialist(req->source.value.specialist)->name));
+    if (!gov) {
+      cat_snprintf(buf, bufsz, _("Applies only to %s."),
+                   _(get_specialist(req->source.value.specialist)->name));
+    }
     return;
   case REQ_MINSIZE:
-    cat_snprintf(buf, bufsz, _("Requires a minimum size of %d.\n\n"),
+    cat_snprintf(buf, bufsz, _("Requires a minimum size of %d."),
                 req->source.value.minsize);
     return;
   }
@@ -1250,6 +1258,7 @@
     .type = REQ_GOV,
     .value = {.gov = gov }
   };
+  bool req_added = FALSE;
 
   buf[0] = '\0';
 
@@ -1259,8 +1268,13 @@
 
   /* Add requirement text for government itself */
   requirement_vector_iterate(&gov->reqs, preq) {
-    insert_requirement(preq, buf, bufsz);
+    insert_requirement(preq, buf, bufsz, FALSE);
+    sprintf(buf + strlen(buf), "\n");
+    req_added = TRUE;
   } requirement_vector_iterate_end;
+  if (req_added) {
+    sprintf(buf + strlen(buf), "\n");
+  }
 
   /* Effects */
   sprintf(buf + strlen(buf), _("Features:\n"));
@@ -1268,7 +1282,8 @@
     Output_type_id output_type = O_LAST;
     struct unit_class *unitclass = NULL;
     enum unit_flag_id unitflag = F_LAST;
-    const char *output = "All";
+    const char *output = _("Output");
+    struct specialist *specialist = NULL;
 
     /* Grab output type, if there is one */
     requirement_list_iterate(peffect->reqs, preq) {
@@ -1279,6 +1294,8 @@
         unitclass = preq->source.value.unitclass;
       } else if (preq->source.type == REQ_UNITFLAG) {
         unitflag = preq->source.value.unitflag;
+      } else if (preq->source.type == REQ_SPECIALIST) {
+        specialist = get_specialist(preq->source.value.specialist);
       }
     } requirement_list_iterate_end;
 
@@ -1286,166 +1303,191 @@
       case EFT_UNHAPPY_FACTOR:
         if (peffect->value == 1) {
           sprintf(buf + strlen(buf), _("* Military units away from home and "
-                  "field units will cause one citizen to become unhappy.\n"));
+                  "field units will cause one citizen to become unhappy."));
         } else {
           sprintf(buf + strlen(buf), _("* Military units away from home and "
-                  "field units will cause %d citizens to become unhappy.\n"),
+                  "field units will cause %d citizens to become unhappy."),
                   peffect->value);
         }
         break;
       case EFT_MAKE_CONTENT:
       case EFT_FORCE_CONTENT:
         sprintf(buf + strlen(buf), _("* Each of your cities will avoid "
-                "%d unhappiness that would otherwise be caused by units.\n"),
+                "%d unhappiness that would otherwise be caused by units."),
                 peffect->value);
         break;
       case EFT_UPKEEP_FACTOR:
-        if (peffect->value > 1 && output_type != O_LAST) {
-          sprintf(buf + strlen(buf), _("* You pay %d times normal %s "
-                  "upkeep for your units.\n"), peffect->value, output);
-        } else if (peffect->value > 1) {
-          sprintf(buf + strlen(buf), _("* You pay %d times normal "
-                  "upkeep for your units.\n"), peffect->value);
+        if (output_type != O_LAST) {
+          sprintf(buf + strlen(buf), _("* You pay %d times extra %s "
+                  "upkeep for your units."), peffect->value, output);
+        } else {
+          sprintf(buf + strlen(buf), _("* You pay %d times extra "
+                  "upkeep for your units."), peffect->value);
         }
         break;
       case EFT_UNIT_UPKEEP_FREE_PER_CITY:
         if (output_type != O_LAST) {
           sprintf(buf + strlen(buf), _("* Each of your cities will avoid "
-                  "paying %d %s towards unit upkeep.\n"), peffect->value, 
+                  "paying %d %s towards unit upkeep."), peffect->value, 
                   output);
         } else {
           sprintf(buf + strlen(buf), _("* Each of your cities will avoid "
-                  "paying %d towards unit upkeep.\n"), peffect->value);
+                  "paying %d towards unit upkeep."), peffect->value);
         }
         break;
       case EFT_CIVIL_WAR_CHANCE:
         sprintf(buf + strlen(buf), _("* Chance of civil war is %d%% if you "
-                "lose your capital.\n"), peffect->value);
+                "lose your capital."), peffect->value);
         break;
       case EFT_EMPIRE_SIZE_MOD:
         sprintf(buf + strlen(buf), _("* The first unhappy citizen in each "
                      "city due to civilization size will appear when you have 
%d"
-                     " cities.\n"), game.info.cityfactor + peffect->value);
+                     " cities."), game.info.cityfactor + peffect->value);
         break;
       case EFT_EMPIRE_SIZE_STEP:
         sprintf(buf + strlen(buf), _("* After the first unhappy citizen "
-                "due to city size, for each %d additional cities, another "
-                "unhappy citizen will appear.\n"), peffect->value);
+                "due to civilization size, for each %d additional cities, "
+                "another unhappy citizen will appear."), peffect->value);
         break;
+      case EFT_MAKE_CONTENT_MIL:
+        sprintf(buf + strlen(buf), _("* Each of your cities will avoid "
+                "unhappiness from the first %d units in the field."),
+                peffect->value);
+        break;
+      case EFT_OUTPUT_BONUS:
+      case EFT_OUTPUT_BONUS_2:
+        sprintf(buf + strlen(buf), _("* You gain %d%% bonus to %s "
+                "production."), peffect->value, output);
+        break;
       case EFT_MAX_RATES:
         if (peffect->value < 100 && game.info.changable_tax) {
           sprintf(buf + strlen(buf), 
                   _("The maximum rate you can set for science, "
-                         "gold, or luxuries is %d%%.\n"), peffect->value);
+                         "gold, or luxuries is %d%%."), peffect->value);
         } else if (game.info.changable_tax) {
           sprintf(buf + strlen(buf), 
-                  _("Has unlimited science/gold/luxuries rates.\n"));
+                  _("Has unlimited science/gold/luxuries rates."));
         }
         break;
       case EFT_MARTIAL_LAW_EACH:
         if (peffect->value == 1) {
           sprintf(buf + strlen(buf), _("* Your units may impose martial "
                   "law. Each military unit inside a city will force an "
-                  "unhappy citizen to become content.\n"));
+                  "unhappy citizen to become content."));
         } else {
           sprintf(buf + strlen(buf), _("* Your units may impose martial law. "
                   "Each military unit inside a city will force %d unhappy "
-                  "citizens to become content.\n"), peffect->value);
+                  "citizens to become content."), peffect->value);
         }
         break;
       case EFT_MARTIAL_LAW_MAX:
         if (peffect->value < 100) {
           sprintf(buf + strlen(buf), _("* A maximum of %d units in each city "
-                  "can enforce martial law.\n"), peffect->value);
+                  "can enforce martial law."), peffect->value);
         }
         break;
       case EFT_RAPTURE_GROW:
         sprintf(buf + strlen(buf), _("* You may grow your cities by "
                 "means of celebrations.  Your cities must be at least "
-                "size %d before they can grown in this manner.\n"),
+                "size %d before they can grown in this manner."),
                 peffect->value);
         break;
       case EFT_UNBRIBABLE_UNITS:
-        sprintf(buf + strlen(buf), _("* Your units cannot be bribed.\n"));
+        sprintf(buf + strlen(buf), _("* Your units cannot be bribed."));
         break;
       case EFT_NO_INCITE:
-        sprintf(buf + strlen(buf), _("* Your cities cannot be incited.\n"));
+        sprintf(buf + strlen(buf), _("* Your cities cannot be incited."));
         break;
       case EFT_REVOLUTION_WHEN_UNHAPPY:
         sprintf(buf + strlen(buf), _("* Government will fall into anarchy "
                 "if any city is in disorder for more than two turns in "
-                "a row.\n"));
+                "a row."));
         break;
       case EFT_HAS_SENATE:
         sprintf(buf + strlen(buf), _("* Has a senate that may prevent "
-                "declaration of war.\n"));
+                "declaration of war."));
         break;
       case EFT_INSPIRE_PARTISANS:
         sprintf(buf + strlen(buf), _("* Allows partisans when cities are "
-                "taken by the enemy.\n"));
+                "taken by the enemy."));
         break;
       case EFT_HAPPINESS_TO_GOLD:
         sprintf(buf + strlen(buf), _("* Buildings that normally confer "
-                "bonuses against unhappiness will instead give gold.\n"));
+                "bonuses against unhappiness will instead give gold."));
         break;
       case EFT_FANATICS:
-        sprintf(buf + strlen(buf), _("* Pays no upkeep for fanatics.\n"));
+        sprintf(buf + strlen(buf), _("* Pays no upkeep for fanatics."));
         break;
       case EFT_NO_UNHAPPY:
-        sprintf(buf + strlen(buf), _("* Has no unhappy citizens.\n"));
+        sprintf(buf + strlen(buf), _("* Has no unhappy citizens."));
         break;
       case EFT_VETERAN_BUILD:
         if (unitclass) {
-          sprintf(buf + strlen(buf), _("* Veteran %s units.\n"),
+          sprintf(buf + strlen(buf), _("* Veteran %s units."),
                   unit_class_name(unitclass));
         } else if (unitflag != F_LAST) {
-          sprintf(buf + strlen(buf), _("* Veteran %s units.\n"),
+          sprintf(buf + strlen(buf), _("* Veteran %s units."),
                   get_unit_flag_name(unitflag));
         } else {
-          sprintf(buf + strlen(buf), _("* Veteran units.\n"));
+          sprintf(buf + strlen(buf), _("* Veteran units."));
         }
         break;
       case EFT_OUTPUT_PENALTY_TILE:
         sprintf(buf + strlen(buf), _("* Each worked tile that gives more "
                 "than %d %s will suffer a -1 penalty when not "
-                "celebrating.\n"), peffect->value, output);
+                "celebrating."), peffect->value, output);
         break;
       case EFT_OUTPUT_INC_TILE_CELEBRATE:
         sprintf(buf + strlen(buf), _("* Each worked tile with at least 1 "
-                "%s will yield %d additional %s when celebrating.\n"),
+                "%s will yield %d additional %s when celebrating."),
                 output, peffect->value, output);
         break;
       case EFT_OUTPUT_INC_TILE:
         sprintf(buf + strlen(buf), _("* Each worked tile with at least 1 "
-                "%s will yield %d additional %s.\n"), output, 
+                "%s will yield %d additional %s."), output, 
                 peffect->value, output);
         break;
+      case EFT_SPECIALIST_OUTPUT:
+        if (specialist) {
+          sprintf(buf + strlen(buf), _("* Each %s generates additional %d 
%s."),
+                  specialist->name, peffect->value, output);
+        } else {
+          sprintf(buf + strlen(buf), _("* Each specialist generates "
+                  "additional %d %s."), peffect->value, output);
+        }
+        break;
       case EFT_OUTPUT_WASTE:
         if (peffect->value > 30) {
           sprintf(buf + strlen(buf), _("* %s production will suffer "
-                  "massive waste.\n"), output);
+                  "massive waste."), output);
         } else if (peffect->value >= 15) {
           sprintf(buf + strlen(buf), _("* %s production will suffer "
-                  "some waste.\n"), output);
+                  "some waste."), output);
         } else {
           sprintf(buf + strlen(buf), _("* %s production will suffer "
-                  "a small amount of waste.\n"), output);
+                  "a small amount of waste."), output);
         }
         break;
       case EFT_OUTPUT_WASTE_BY_DISTANCE:
         if (peffect->value >= 3) {
           sprintf(buf + strlen(buf), _("* %s waste will increase quickly "
-                  "with distance from capital.\n"), output);
+                  "with distance from capital."), output);
         } else if (peffect->value == 2) {
           sprintf(buf + strlen(buf), _("* %s waste will increase "
-                  "with distance from capital.\n"), output);
+                  "with distance from capital."), output);
         } else {
           sprintf(buf + strlen(buf), _("* %s waste will increase slowly "
-                  "with distance from capital.\n"), output);
+                  "with distance from capital."), output);
         }
       default:
         break;
     }
+
+    requirement_list_iterate(peffect->reqs, preq) {
+      sprintf(buf + strlen(buf), " ");
+      insert_requirement(preq, buf, bufsz, TRUE);
+    } requirement_list_iterate_end;
+    sprintf(buf + strlen(buf), "\n");
   } effect_list_iterate_end;
 
   unit_type_iterate(utype) {
Index: client/climisc.c
===================================================================
--- client/climisc.c    (revision 11446)
+++ client/climisc.c    (working copy)
@@ -375,12 +375,14 @@
 
 /**************************************************************************
   Return the sprite for the government indicator.
+
+  FIXME: Show all govt categories' icons...
 **************************************************************************/
 struct sprite *client_government_sprite(void)
 {
   if (can_client_change_view() && game.player_ptr
       && game.control.government_count > 0) {
-    struct government *gov = game.player_ptr->government;
+    struct government *gov = game.player_ptr->govs[0].current_government;
 
     return get_government_sprite(tileset, gov);
   } else {
@@ -1057,13 +1059,13 @@
     return;
   }
 
-  lux_end = game.player_ptr->economic.luxury;
-  sci_end = lux_end + game.player_ptr->economic.science;
+  lux_end = game.player_ptr->economic.rates[O_LUXURY];
+  sci_end = lux_end + game.player_ptr->economic.rates[O_SCIENCE];
   tax_end = 100;
 
-  lux = game.player_ptr->economic.luxury;
-  sci = game.player_ptr->economic.science;
-  tax = game.player_ptr->economic.tax;
+  lux = game.player_ptr->economic.rates[O_LUXURY];
+  sci = game.player_ptr->economic.rates[O_SCIENCE];
+  tax = game.player_ptr->economic.rates[O_GOLD];
 
   i *= 10;
   if (i < lux_end) {

Attachment: newgov_ruleset.tar.gz
Description: application/gunzip


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