diff -Nur -Xfreecivdiff.ignore freeciv-cvs/client/civclient.c freeciv-patched/client/civclient.c
--- freeciv-cvs/client/civclient.c	2003-06-07 19:58:20.000000000 +0100
+++ freeciv-patched/client/civclient.c	2003-06-30 12:33:10.000000000 +0100
@@ -365,6 +365,15 @@
     handle_ruleset_unit((struct packet_ruleset_unit *)packet);
     break;
     
+  case PACKET_RULESET_MODIFIER_TYPE:
+    handle_ruleset_modifier_type(
+	(struct packet_ruleset_modifier_type *)packet);
+    break;
+    
+  case PACKET_RULESET_MODIFIER:
+    handle_ruleset_modifier((struct packet_ruleset_modifier *)packet);
+    break;
+    
   case PACKET_RULESET_TECH:
     handle_ruleset_tech((struct packet_ruleset_tech *)packet);
     break;
diff -Nur -Xfreecivdiff.ignore freeciv-cvs/client/gui-gtk/dialogs.c freeciv-patched/client/gui-gtk/dialogs.c
--- freeciv-cvs/client/gui-gtk/dialogs.c	2003-06-27 15:51:10.000000000 +0100
+++ freeciv-patched/client/gui-gtk/dialogs.c	2003-06-30 12:33:10.000000000 +0100
@@ -176,6 +176,18 @@
   popdown_notify_dialog();
 }
 
+static void workshop_update_selection(bool is_gov);
+static bool is_showing_workshop_dialog = FALSE;
+static bool workshop_is_gov = FALSE;
+static Unit_Type_id workshop_base;
+struct workshop_selection {
+  GtkWidget *option_menu, *popup;
+  int modifier;
+};
+static struct workshop_selection wkshp_selection[MAX_MODS];
+static struct workshop_selection wkshp_name;
+static GtkWidget *wkshp_create_button;
+
 /****************************************************************
 ...
 *****************************************************************/
@@ -1182,6 +1194,380 @@
   send_packet_unit_request(&aconnection, &req, PACKET_UNIT_ESTABLISH_TRADE);
 }
 
+static gint workshop_del_callback(GtkWidget *widget, GdkEvent *event,
+				  gpointer data)
+{
+  gtk_widget_set_sensitive((GtkWidget *)data, TRUE);
+  is_showing_workshop_dialog = FALSE;
+  return FALSE;
+}
+
+static void workshop_create_callback(GtkWidget *widget, gpointer data)
+{
+  struct packet_request_compound packet;
+  int i;
+
+  packet.is_gov = workshop_is_gov;
+  packet.basetype = workshop_base;
+  for (i = 0; i < MAX_MODS; ++i) {
+    packet.modifiers[i] = wkshp_selection[i].modifier;
+  }
+  /* Use an auto-generated name */
+  packet.name[0] ='\0';
+  send_packet_request_compound(&aconnection, &packet);
+}
+
+static void workshop_close_callback(GtkWidget *widget, gpointer data)
+{
+  gtk_widget_set_sensitive(toplevel, TRUE);
+  gtk_widget_destroy((GtkWidget *)data);
+  is_showing_workshop_dialog = FALSE;
+}
+
+static void workshop_select_callback(GtkWidget *widget, gpointer data)
+{
+  int mod, i, modifiers[MAX_MODS], type;
+  int num_modtypes;
+  Unit_Type_id uid;
+
+  mod = GPOINTER_TO_INT(data);
+  type = GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(widget), "type"));
+
+  num_modtypes = workshop_is_gov ? game.num_gov_modifier_types
+    				 : game.num_unit_modifier_types;
+  assert(type >=0 && type < num_modtypes);
+
+  wkshp_selection[type].modifier = mod;
+  for (i = 0; i < num_modtypes; ++i) {
+    modifiers[i] = wkshp_selection[i].modifier;
+  }
+
+  if (workshop_is_gov) {
+    workshop_update_selection(TRUE);
+  } else {
+    uid = find_unit_type_by_modifiers(workshop_base, modifiers);
+    gtk_widget_set_sensitive(wkshp_create_button, uid == U_LAST);
+    if (uid < U_LAST) {
+      workshop_dialog_update(uid);
+    } else {
+      workshop_update_selection(FALSE);
+    }
+  }
+}
+
+static void workshop_selname_callback(GtkWidget *widget, gpointer data)
+{
+  workshop_dialog_update(GPOINTER_TO_INT(data));
+}
+
+static bool is_duplicate_modifier(int type, int num_mod_types, int mod)
+{
+  int i;
+  for (i = 0; i < num_mod_types; ++i) {
+    if (i != type && wkshp_selection[i].modifier == mod) {
+      return TRUE;
+    }
+  }
+  return FALSE;
+}
+
+static void workshop_update_selection(bool is_gov)
+{
+  int i, j, index, selindex, num_mod_types, num_mods;
+  struct modifier *modifiers;
+  struct modifier_type *modifier_types;
+  GtkWidget *item;
+
+  if (is_gov) {
+    num_mod_types = game.num_gov_modifier_types;
+    num_mods = game.num_gov_modifiers;
+    modifier_types = gov_modifier_types;
+    modifiers = gov_modifiers;
+  } else {
+    num_mod_types = game.num_unit_modifier_types;
+    num_mods = game.num_unit_modifiers;
+    modifier_types = unit_modifier_types;
+    modifiers = unit_modifiers;
+  }
+
+  for (i = 0; i < num_mod_types; ++i) {
+    int id = modifier_types[i].id;
+
+    if (wkshp_selection[i].popup) {
+      gtk_widget_destroy(wkshp_selection[i].popup);
+    }
+    wkshp_selection[i].popup = gtk_menu_new();
+
+    if (get_invention(game.player_ptr,
+	  	      modifier_types[i].tech_requirement) == TECH_KNOWN) {
+      index = selindex = -1;
+      if (!modifier_types[i].must_use) {
+        item = gtk_menu_item_new_with_label(_("<None>"));
+        index++;
+        gtk_object_set_data(GTK_OBJECT(item), "type", GINT_TO_POINTER(i));
+        gtk_signal_connect(GTK_OBJECT(item), "activate",
+	      		   GTK_SIGNAL_FUNC(workshop_select_callback),
+			   GINT_TO_POINTER(U_LAST));
+        gtk_menu_append(GTK_MENU(wkshp_selection[i].popup), item);
+      }
+
+      for (j = 0; j < num_mods; ++j) {
+        struct modifier *u = &modifiers[j];
+
+        if (u->type == id && get_invention(game.player_ptr,
+	    			           u->tech_requirement) == TECH_KNOWN
+	    && !is_duplicate_modifier(i, num_mod_types, j)) {
+	  index++;
+	  if (wkshp_selection[i].modifier == j) {
+	    selindex = index;
+	  }
+	  item = gtk_menu_item_new_with_label(modifiers[j].name);
+	  gtk_object_set_data(GTK_OBJECT(item), "type", GINT_TO_POINTER(i));
+	  gtk_signal_connect(GTK_OBJECT(item), "activate",
+	      		     GTK_SIGNAL_FUNC(workshop_select_callback),
+			     GINT_TO_POINTER(j));
+	  gtk_menu_append(GTK_MENU(wkshp_selection[i].popup), item);
+        }
+      }
+      gtk_menu_set_active(GTK_MENU(wkshp_selection[i].popup), selindex);
+      gtk_widget_set_sensitive(wkshp_selection[i].option_menu, TRUE);
+    } else {
+      char buf[100];
+
+      my_snprintf(buf, sizeof(buf), "(Requires %s)",
+	  	  get_tech_name(game.player_ptr,
+				modifier_types[i].tech_requirement));
+      item = gtk_menu_item_new_with_label(buf);
+      gtk_menu_append(GTK_MENU(wkshp_selection[i].popup), item);
+      gtk_widget_set_sensitive(wkshp_selection[i].option_menu, FALSE);
+    }
+    gtk_widget_show_all(wkshp_selection[i].popup);
+    gtk_option_menu_set_menu(GTK_OPTION_MENU(wkshp_selection[i].option_menu),
+			     wkshp_selection[i].popup);
+  }
+}
+
+void workshop_dialog_update(Unit_Type_id id)
+{
+  int i, index, selindex;
+  GtkWidget *item;
+
+  if (!is_showing_workshop_dialog || id < 0 || id >= game.num_unit_types
+      || unit_types[id].basetype == U_LAST
+      || !can_player_build_unit_direct(game.player_ptr, id)) {
+    return;
+  }
+
+  for (i = 0; i < game.num_unit_modifier_types; ++i) {
+    wkshp_selection[i].modifier = unit_types[id].modifiers[i];
+  }
+
+  if (wkshp_name.popup) {
+    gtk_widget_destroy(wkshp_name.popup);
+  }
+  wkshp_name.popup = gtk_menu_new();
+
+  index = selindex = -1;
+  unit_type_iterate(uid) {
+    if (unit_types[uid].basetype >= 0
+	&& unit_types[uid].basetype < game.num_unit_types
+	&& can_player_build_unit_direct(game.player_ptr, uid)) {
+      index++;
+      if (uid == id) {
+	selindex = index;
+      }
+      item = gtk_menu_item_new_with_label(unit_types[uid].name);
+      gtk_signal_connect(GTK_OBJECT(item), "activate",
+	      		 GTK_SIGNAL_FUNC(workshop_selname_callback),
+			 GINT_TO_POINTER(uid));
+      gtk_menu_append(GTK_MENU(wkshp_name.popup), item);
+    }
+  } unit_type_iterate_end;
+  gtk_menu_set_active(GTK_MENU(wkshp_name.popup), selindex);
+  gtk_widget_show_all(wkshp_name.popup);
+  gtk_option_menu_set_menu(GTK_OPTION_MENU(wkshp_name.option_menu),
+			   wkshp_name.popup);
+
+  workshop_update_selection(FALSE);
+
+  gtk_widget_set_sensitive(wkshp_create_button, FALSE);
+}
+
+/****************************************************************
+...
+*****************************************************************/
+static void engineering_dialog_update()
+{
+  int i;
+  struct government *gov = get_gov_pplayer(game.player_ptr);
+
+  for (i = 0; i < game.num_gov_modifier_types; ++i) {
+    wkshp_selection[i].modifier = gov->modifiers[i];
+  }
+  workshop_update_selection(TRUE);
+}
+
+/****************************************************************
+...
+*****************************************************************/
+void popup_engineering_dialog(void)
+{
+  GtkWidget *dshell, *vbox, *table, *label, *hbox, *button, *sep;
+  int i;
+
+  if (!is_showing_workshop_dialog && game.num_gov_modifier_types > 0) {
+    is_showing_workshop_dialog = TRUE;
+    workshop_is_gov = TRUE;
+    workshop_base = 1;
+
+    gtk_widget_set_sensitive(toplevel, FALSE);
+    dshell = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+    gtk_window_set_policy(GTK_WINDOW(dshell), 1, 1, 1);
+    gtk_window_set_position(GTK_WINDOW(dshell), GTK_WIN_POS_MOUSE);
+
+    gtk_signal_connect(GTK_OBJECT(dshell), "delete_event",
+		       GTK_SIGNAL_FUNC(workshop_del_callback),
+		       (gpointer)toplevel);
+    gtk_window_set_title(GTK_WINDOW(dshell), _("Social Engineering"));
+
+    vbox = gtk_vbox_new(FALSE, 8);
+    gtk_container_add(GTK_CONTAINER(dshell), vbox);
+
+    gtk_container_border_width(GTK_CONTAINER(vbox), 5);
+
+    table = gtk_table_new(game.num_gov_modifier_types, 2, FALSE);
+    gtk_table_set_row_spacings(GTK_TABLE(table), 6);
+    gtk_table_set_col_spacings(GTK_TABLE(table), 6);
+
+    for (i = 0; i < game.num_gov_modifier_types; ++i) {
+      label = gtk_label_new(gov_modifier_types[i].name);
+      gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, i, i + 1);
+
+      wkshp_selection[i].option_menu = gtk_option_menu_new();
+      gtk_table_attach_defaults(GTK_TABLE(table),
+	  			wkshp_selection[i].option_menu,
+				1, 2, i, i + 1);
+      wkshp_selection[i].popup = NULL;
+    }
+    engineering_dialog_update();
+
+    gtk_box_pack_start(GTK_BOX(vbox), table, TRUE, TRUE, 0);
+
+    sep = gtk_hseparator_new();
+    gtk_box_pack_start(GTK_BOX(vbox), sep, FALSE, FALSE, 0);
+
+    hbox = gtk_hbox_new(TRUE, 8);
+    button = gtk_button_new_with_label(_("Apply"));
+    gtk_signal_connect(GTK_OBJECT(button), "clicked",
+		       GTK_SIGNAL_FUNC(workshop_create_callback), NULL);
+    gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
+
+    button = gtk_button_new_with_label(_("Close"));
+    gtk_signal_connect(GTK_OBJECT(button), "clicked",
+		       GTK_SIGNAL_FUNC(workshop_close_callback), dshell);
+    gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
+
+    gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+
+    gtk_widget_show_all(vbox);
+    gtk_widget_show(dshell);
+  }
+}
+
+/****************************************************************
+...
+*****************************************************************/
+void popup_workshop_dialog(void)
+{
+  GtkWidget *dshell, *vbox, *table, *label;
+  GtkWidget *button, *hbox, *sep;
+  int i;
+
+  workshop_base = -1;
+  unit_type_iterate(uid) {
+    if (unit_has_role(uid, L_CANMODIFY)) {
+      workshop_base = uid;
+      break;
+    }
+  } unit_type_iterate_end;
+
+  if (!is_showing_workshop_dialog && game.num_unit_modifier_types > 0) {
+    is_showing_workshop_dialog = TRUE;
+    workshop_is_gov = FALSE;
+
+    gtk_widget_set_sensitive(toplevel, FALSE);
+    dshell = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+    gtk_window_set_policy(GTK_WINDOW(dshell), 1, 1, 1);
+    gtk_window_set_position(GTK_WINDOW(dshell), GTK_WIN_POS_MOUSE);
+
+    gtk_signal_connect(GTK_OBJECT(dshell), "delete_event",
+		       GTK_SIGNAL_FUNC(workshop_del_callback),
+		       (gpointer)toplevel);
+    gtk_window_set_title(GTK_WINDOW(dshell), _("Design Workshop"));
+
+    vbox = gtk_vbox_new(FALSE, 8);
+    gtk_container_add(GTK_CONTAINER(dshell), vbox);
+
+    gtk_container_border_width(GTK_CONTAINER(vbox), 5);
+
+    table = gtk_table_new(game.num_unit_modifier_types + 2, 2, FALSE);
+    gtk_table_set_row_spacings(GTK_TABLE(table), 6);
+    gtk_table_set_col_spacings(GTK_TABLE(table), 6);
+
+    label = gtk_label_new(_("Name"));
+    gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 0, 1);
+    wkshp_name.option_menu = gtk_option_menu_new();
+    gtk_table_attach_defaults(GTK_TABLE(table),
+	  		      wkshp_name.option_menu, 1, 2, 0, 1);
+    wkshp_name.popup = NULL;
+
+    sep = gtk_hseparator_new();
+    gtk_table_attach_defaults(GTK_TABLE(table), sep, 0, 2, 1, 2);
+
+    for (i = 0; i < game.num_unit_modifier_types; ++i) {
+      label = gtk_label_new(unit_modifier_types[i].name);
+      gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, i + 2, i + 3);
+
+      wkshp_selection[i].option_menu = gtk_option_menu_new();
+      gtk_table_attach_defaults(GTK_TABLE(table),
+	  			wkshp_selection[i].option_menu,
+				1, 2, i + 2, i + 3);
+      wkshp_selection[i].popup = NULL;
+    }
+
+    gtk_box_pack_start(GTK_BOX(vbox), table, TRUE, TRUE, 0);
+
+    sep = gtk_hseparator_new();
+    gtk_box_pack_start(GTK_BOX(vbox), sep, FALSE, FALSE, 0);
+
+    hbox = gtk_hbox_new(TRUE, 8);
+    button = gtk_button_new_with_label(_("Create"));
+    wkshp_create_button = button;
+    gtk_widget_set_sensitive(button, FALSE);
+    gtk_signal_connect(GTK_OBJECT(button), "clicked",
+		       GTK_SIGNAL_FUNC(workshop_create_callback), NULL);
+    gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
+
+    button = gtk_button_new_with_label(_("Close"));
+    gtk_signal_connect(GTK_OBJECT(button), "clicked",
+		       GTK_SIGNAL_FUNC(workshop_close_callback), dshell);
+    gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
+
+    gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+
+    unit_type_iterate(uid) {
+      if (unit_types[uid].basetype >= 0
+	  && unit_types[uid].basetype < game.num_unit_types
+	  && can_player_build_unit_direct(game.player_ptr, uid)) {
+	workshop_dialog_update(uid);
+	break;
+      }
+    } unit_type_iterate_end;
+
+    gtk_widget_show_all(vbox);
+    gtk_widget_show(dshell);
+  }
+}
 
 /****************************************************************
 ...
diff -Nur -Xfreecivdiff.ignore freeciv-cvs/client/gui-gtk/menu.c freeciv-patched/client/gui-gtk/menu.c
--- freeciv-cvs/client/gui-gtk/menu.c	2003-06-26 20:13:14.000000000 +0100
+++ freeciv-patched/client/gui-gtk/menu.c	2003-06-30 12:33:10.000000000 +0100
@@ -79,6 +79,8 @@
   MENU_KINGDOM_TAX_RATE,
   MENU_KINGDOM_FIND_CITY,
   MENU_KINGDOM_WORKLISTS,
+  MENU_KINGDOM_WORKSHOP,
+  MENU_KINGDOM_ENGINEERING,
   MENU_KINGDOM_REVOLUTION,
 
   MENU_VIEW_SHOW_MAP_GRID,
@@ -211,6 +213,12 @@
   case MENU_KINGDOM_WORKLISTS:
     popup_worklists_report(game.player_ptr);
     break;
+  case MENU_KINGDOM_WORKSHOP:
+    popup_workshop_dialog();
+    break;
+  case MENU_KINGDOM_ENGINEERING:
+    popup_engineering_dialog();
+    break;
   case MENU_KINGDOM_REVOLUTION:
     popup_revolution_dialog();
     break;
@@ -598,6 +606,12 @@
 	kingdom_menu_callback,	MENU_KINGDOM_WORKLISTS					},
   { "/" N_("Kingdom") "/sep2",				NULL,
 	NULL,			0,					"<Separator>"	},
+  { "/" N_("Kingdom") "/" N_("Design _Workshop"),	NULL,
+	kingdom_menu_callback,	MENU_KINGDOM_WORKSHOP					},
+  { "/" N_("Kingdom") "/" N_("Social _Engineering"),	NULL,
+	kingdom_menu_callback,	MENU_KINGDOM_ENGINEERING					},
+  { "/" N_("Kingdom") "/sep3",				NULL,
+	NULL,			0,					"<Separator>"	},
   { "/" N_("Kingdom") "/" N_("_Government"),  NULL,
 	NULL,           0,                  "<Branch>"    },
   { "/" N_("Kingdom") "/" N_("_Government") "/" N_("_Revolution!"),
@@ -1033,6 +1047,10 @@
     }
 
     menus_set_sensitive("<main>/_Reports", TRUE);
+    menus_set_sensitive("<main>/_Kingdom/Design _Workshop",
+			game.num_unit_modifier_types > 0);
+    menus_set_sensitive("<main>/_Kingdom/Social _Engineering",
+			game.num_gov_modifier_types > 0);
     menus_set_sensitive("<main>/_Kingdom", TRUE);
     menus_set_sensitive("<main>/_View", TRUE);
     menus_set_sensitive("<main>/_Orders", can_client_issue_orders());
diff -Nur -Xfreecivdiff.ignore freeciv-cvs/client/gui-gtk-2.0/dialogs.c freeciv-patched/client/gui-gtk-2.0/dialogs.c
--- freeciv-cvs/client/gui-gtk-2.0/dialogs.c	2003-06-27 15:51:10.000000000 +0100
+++ freeciv-patched/client/gui-gtk-2.0/dialogs.c	2003-06-30 12:33:10.000000000 +0100
@@ -130,6 +130,18 @@
 static int connect_unit_x;
 static int connect_unit_y;
 
+static void workshop_update_selection(bool is_gov);
+static bool is_showing_workshop_dialog = FALSE;
+static bool workshop_is_gov = FALSE;
+static Unit_Type_id workshop_base;
+struct workshop_selection {
+  GtkWidget *option_menu, *popup;
+  int modifier;
+};
+static struct workshop_selection wkshp_selection[MAX_MODS];
+static struct workshop_selection wkshp_name;
+static GtkWidget *wkshp_create_button;
+
 /****************************************************************
 ...
 *****************************************************************/
@@ -2423,3 +2435,375 @@
   g_list_free(res);
 }
 
+static gint workshop_del_callback(GtkWidget *widget, GdkEvent *event,
+				  gpointer data)
+{
+  gtk_widget_set_sensitive((GtkWidget *)data, TRUE);
+  is_showing_workshop_dialog = FALSE;
+  return FALSE;
+}
+
+static void workshop_create_callback(GtkWidget *widget, gpointer data)
+{
+  struct packet_request_compound packet;
+  int i;
+
+  packet.is_gov = workshop_is_gov;
+  packet.basetype = workshop_base;
+  for (i = 0; i < MAX_MODS; ++i) {
+    packet.modifiers[i] = wkshp_selection[i].modifier;
+  }
+  /* Use an auto-generated name */
+  packet.name[0] ='\0';
+  send_packet_request_compound(&aconnection, &packet);
+}
+
+static void workshop_close_callback(GtkWidget *widget, gpointer data)
+{
+  gtk_widget_set_sensitive(toplevel, TRUE);
+  gtk_widget_destroy((GtkWidget *)data);
+  is_showing_workshop_dialog = FALSE;
+}
+
+static void workshop_select_callback(GtkWidget *widget, gpointer data)
+{
+  int mod, i, modifiers[MAX_MODS], type;
+  int num_modtypes;
+  Unit_Type_id uid;
+
+  mod = GPOINTER_TO_INT(data);
+  type = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), "type"));
+
+  num_modtypes = workshop_is_gov ? game.num_gov_modifier_types
+    				 : game.num_unit_modifier_types;
+  assert(type >=0 && type < num_modtypes);
+
+  wkshp_selection[type].modifier = mod;
+  for (i = 0; i < num_modtypes; ++i) {
+    modifiers[i] = wkshp_selection[i].modifier;
+  }
+
+  if (workshop_is_gov) {
+    workshop_update_selection(TRUE);
+  } else {
+    uid = find_unit_type_by_modifiers(workshop_base, modifiers);
+    gtk_widget_set_sensitive(wkshp_create_button, uid == U_LAST);
+    if (uid < U_LAST) {
+      workshop_dialog_update(uid);
+    } else {
+      workshop_update_selection(FALSE);
+    }
+  }
+}
+
+static void workshop_selname_callback(GtkWidget *widget, gpointer data)
+{
+  workshop_dialog_update(GPOINTER_TO_INT(data));
+}
+
+static bool is_duplicate_modifier(int type, int num_mod_types, int mod)
+{
+  int i;
+  for (i = 0; i < num_mod_types; ++i) {
+    if (i != type && wkshp_selection[i].modifier == mod) {
+      return TRUE;
+    }
+  }
+  return FALSE;
+}
+
+static void workshop_update_selection(bool is_gov)
+{
+  int i, j, index, selindex, num_mod_types, num_mods;
+  struct modifier *modifiers;
+  struct modifier_type *modifier_types;
+  GtkWidget *item;
+
+  if (is_gov) {
+    num_mod_types = game.num_gov_modifier_types;
+    num_mods = game.num_gov_modifiers;
+    modifier_types = gov_modifier_types;
+    modifiers = gov_modifiers;
+  } else {
+    num_mod_types = game.num_unit_modifier_types;
+    num_mods = game.num_unit_modifiers;
+    modifier_types = unit_modifier_types;
+    modifiers = unit_modifiers;
+  }
+
+  for (i = 0; i < num_mod_types; ++i) {
+    int id = modifier_types[i].id;
+
+    if (wkshp_selection[i].popup) {
+      gtk_widget_destroy(wkshp_selection[i].popup);
+    }
+    wkshp_selection[i].popup = gtk_menu_new();
+
+    if (get_invention(game.player_ptr,
+	  	      modifier_types[i].tech_requirement) == TECH_KNOWN) {
+      index = selindex = -1;
+      if (!modifier_types[i].must_use) {
+        item = gtk_menu_item_new_with_label(_("<None>"));
+        index++;
+        g_object_set_data(G_OBJECT(item), "type", GINT_TO_POINTER(i));
+        g_signal_connect(G_OBJECT(item), "activate",
+	      		 G_CALLBACK(workshop_select_callback),
+			 GINT_TO_POINTER(U_LAST));
+        gtk_menu_shell_append(GTK_MENU_SHELL(wkshp_selection[i].popup), item);
+      }
+
+      for (j = 0; j < num_mods; ++j) {
+        struct modifier *u = &modifiers[j];
+
+        if (u->type == id && get_invention(game.player_ptr,
+	    			           u->tech_requirement) == TECH_KNOWN
+	    && !is_duplicate_modifier(i, num_mod_types, j)) {
+	  index++;
+	  if (wkshp_selection[i].modifier == j) {
+	    selindex = index;
+	  }
+	  item = gtk_menu_item_new_with_label(modifiers[j].name);
+	  g_object_set_data(G_OBJECT(item), "type", GINT_TO_POINTER(i));
+	  g_signal_connect(G_OBJECT(item), "activate",
+	      		   G_CALLBACK(workshop_select_callback),
+			   GINT_TO_POINTER(j));
+	  gtk_menu_shell_append(GTK_MENU_SHELL(wkshp_selection[i].popup), item);
+        }
+      }
+      gtk_menu_set_active(GTK_MENU(wkshp_selection[i].popup), selindex);
+      gtk_widget_set_sensitive(wkshp_selection[i].option_menu, TRUE);
+    } else {
+      char buf[100];
+
+      my_snprintf(buf, sizeof(buf), "(Requires %s)",
+	  	  get_tech_name(game.player_ptr,
+				modifier_types[i].tech_requirement));
+      item = gtk_menu_item_new_with_label(buf);
+      gtk_menu_shell_append(GTK_MENU_SHELL(wkshp_selection[i].popup), item);
+      gtk_widget_set_sensitive(wkshp_selection[i].option_menu, FALSE);
+    }
+    gtk_widget_show_all(wkshp_selection[i].popup);
+    gtk_option_menu_set_menu(GTK_OPTION_MENU(wkshp_selection[i].option_menu),
+			     wkshp_selection[i].popup);
+  }
+}
+
+void workshop_dialog_update(Unit_Type_id id)
+{
+  int i, index, selindex;
+  GtkWidget *item;
+
+  if (!is_showing_workshop_dialog || id < 0 || id >= game.num_unit_types
+      || unit_types[id].basetype == U_LAST
+      || !can_player_build_unit_direct(game.player_ptr, id)) {
+    return;
+  }
+
+  for (i = 0; i < game.num_unit_modifier_types; ++i) {
+    wkshp_selection[i].modifier = unit_types[id].modifiers[i];
+  }
+
+  if (wkshp_name.popup) {
+    gtk_widget_destroy(wkshp_name.popup);
+  }
+  wkshp_name.popup = gtk_menu_new();
+
+  index = selindex = -1;
+  unit_type_iterate(uid) {
+    if (unit_types[uid].basetype >= 0
+	&& unit_types[uid].basetype < game.num_unit_types
+	&& can_player_build_unit_direct(game.player_ptr, uid)) {
+      index++;
+      if (uid == id) {
+	selindex = index;
+      }
+      item = gtk_menu_item_new_with_label(unit_types[uid].name);
+      g_signal_connect(G_OBJECT(item), "activate",
+	      	       G_CALLBACK(workshop_selname_callback),
+		       GINT_TO_POINTER(uid));
+      gtk_menu_shell_append(GTK_MENU_SHELL(wkshp_name.popup), item);
+    }
+  } unit_type_iterate_end;
+  gtk_menu_set_active(GTK_MENU(wkshp_name.popup), selindex);
+  gtk_widget_show_all(wkshp_name.popup);
+  gtk_option_menu_set_menu(GTK_OPTION_MENU(wkshp_name.option_menu),
+			   wkshp_name.popup);
+
+  workshop_update_selection(FALSE);
+
+  gtk_widget_set_sensitive(wkshp_create_button, FALSE);
+}
+
+/****************************************************************
+...
+*****************************************************************/
+static void engineering_dialog_update()
+{
+  int i;
+  struct government *gov = get_gov_pplayer(game.player_ptr);
+
+  for (i = 0; i < game.num_gov_modifier_types; ++i) {
+    wkshp_selection[i].modifier = gov->modifiers[i];
+  }
+  workshop_update_selection(TRUE);
+}
+
+/****************************************************************
+...
+*****************************************************************/
+void popup_engineering_dialog(void)
+{
+  GtkWidget *dshell, *vbox, *table, *label, *hbox, *button, *sep;
+  int i;
+
+  if (!is_showing_workshop_dialog && game.num_gov_modifier_types > 0) {
+    is_showing_workshop_dialog = TRUE;
+    workshop_is_gov = TRUE;
+    workshop_base = 1;
+
+    gtk_widget_set_sensitive(toplevel, FALSE);
+    dshell = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+    gtk_window_set_position(GTK_WINDOW(dshell), GTK_WIN_POS_MOUSE);
+
+    g_signal_connect(G_OBJECT(dshell), "delete_event",
+		     G_CALLBACK(workshop_del_callback),
+		     (gpointer)toplevel);
+    gtk_window_set_title(GTK_WINDOW(dshell), _("Social Engineering"));
+
+    vbox = gtk_vbox_new(FALSE, 8);
+    gtk_container_add(GTK_CONTAINER(dshell), vbox);
+
+    gtk_container_set_border_width(GTK_CONTAINER(vbox), 5);
+
+    table = gtk_table_new(game.num_gov_modifier_types, 2, FALSE);
+    gtk_table_set_row_spacings(GTK_TABLE(table), 6);
+    gtk_table_set_col_spacings(GTK_TABLE(table), 6);
+
+    for (i = 0; i < game.num_gov_modifier_types; ++i) {
+      label = gtk_label_new(gov_modifier_types[i].name);
+      gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, i, i + 1);
+
+      wkshp_selection[i].option_menu = gtk_option_menu_new();
+      gtk_table_attach_defaults(GTK_TABLE(table),
+	  			wkshp_selection[i].option_menu,
+				1, 2, i, i + 1);
+      wkshp_selection[i].popup = NULL;
+    }
+    engineering_dialog_update();
+
+    gtk_box_pack_start(GTK_BOX(vbox), table, TRUE, TRUE, 0);
+
+    sep = gtk_hseparator_new();
+    gtk_box_pack_start(GTK_BOX(vbox), sep, FALSE, FALSE, 0);
+
+    hbox = gtk_hbox_new(TRUE, 8);
+    button = gtk_button_new_from_stock(GTK_STOCK_APPLY);
+    g_signal_connect(G_OBJECT(button), "clicked",
+		     G_CALLBACK(workshop_create_callback), NULL);
+    gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
+
+    button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
+    g_signal_connect(G_OBJECT(button), "clicked",
+		     G_CALLBACK(workshop_close_callback), dshell);
+    gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
+
+    gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+
+    gtk_widget_show_all(vbox);
+    gtk_widget_show(dshell);
+  }
+}
+
+/****************************************************************
+...
+*****************************************************************/
+void popup_workshop_dialog(void)
+{
+  GtkWidget *dshell, *vbox, *table, *label;
+  GtkWidget *button, *hbox, *sep;
+  int i;
+
+  workshop_base = -1;
+  unit_type_iterate(uid) {
+    if (unit_has_role(uid, L_CANMODIFY)) {
+      workshop_base = uid;
+      break;
+    }
+  } unit_type_iterate_end;
+
+  if (!is_showing_workshop_dialog && game.num_unit_modifier_types > 0) {
+    is_showing_workshop_dialog = TRUE;
+    workshop_is_gov = FALSE;
+
+    gtk_widget_set_sensitive(toplevel, FALSE);
+    dshell = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+    gtk_window_set_position(GTK_WINDOW(dshell), GTK_WIN_POS_MOUSE);
+
+    g_signal_connect(G_OBJECT(dshell), "delete_event",
+		     G_CALLBACK(workshop_del_callback),
+		     (gpointer)toplevel);
+    gtk_window_set_title(GTK_WINDOW(dshell), _("Design Workshop"));
+
+    vbox = gtk_vbox_new(FALSE, 8);
+    gtk_container_add(GTK_CONTAINER(dshell), vbox);
+
+    gtk_container_set_border_width(GTK_CONTAINER(vbox), 5);
+
+    table = gtk_table_new(game.num_unit_modifier_types + 2, 2, FALSE);
+    gtk_table_set_row_spacings(GTK_TABLE(table), 6);
+    gtk_table_set_col_spacings(GTK_TABLE(table), 6);
+
+    label = gtk_label_new(_("Name"));
+    gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 0, 1);
+    wkshp_name.option_menu = gtk_option_menu_new();
+    gtk_table_attach_defaults(GTK_TABLE(table),
+	  		      wkshp_name.option_menu, 1, 2, 0, 1);
+    wkshp_name.popup = NULL;
+
+    sep = gtk_hseparator_new();
+    gtk_table_attach_defaults(GTK_TABLE(table), sep, 0, 2, 1, 2);
+
+    for (i = 0; i < game.num_unit_modifier_types; ++i) {
+      label = gtk_label_new(unit_modifier_types[i].name);
+      gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, i + 2, i + 3);
+
+      wkshp_selection[i].option_menu = gtk_option_menu_new();
+      gtk_table_attach_defaults(GTK_TABLE(table),
+	  			wkshp_selection[i].option_menu,
+				1, 2, i + 2, i + 3);
+      wkshp_selection[i].popup = NULL;
+    }
+
+    gtk_box_pack_start(GTK_BOX(vbox), table, TRUE, TRUE, 0);
+
+    sep = gtk_hseparator_new();
+    gtk_box_pack_start(GTK_BOX(vbox), sep, FALSE, FALSE, 0);
+
+    hbox = gtk_hbox_new(TRUE, 8);
+    button = gtk_stockbutton_new(GTK_STOCK_NEW, _("C_reate"));
+    wkshp_create_button = button;
+    gtk_widget_set_sensitive(button, FALSE);
+    g_signal_connect(G_OBJECT(button), "clicked",
+		     G_CALLBACK(workshop_create_callback), NULL);
+    gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
+
+    button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
+    g_signal_connect(G_OBJECT(button), "clicked",
+		     G_CALLBACK(workshop_close_callback), dshell);
+    gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
+
+    gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+
+    unit_type_iterate(uid) {
+      if (unit_types[uid].basetype >= 0
+	  && unit_types[uid].basetype < game.num_unit_types
+	  && can_player_build_unit_direct(game.player_ptr, uid)) {
+	workshop_dialog_update(uid);
+	break;
+      }
+    } unit_type_iterate_end;
+
+    gtk_widget_show_all(vbox);
+    gtk_widget_show(dshell);
+  }
+}
diff -Nur -Xfreecivdiff.ignore freeciv-cvs/client/gui-gtk-2.0/menu.c freeciv-patched/client/gui-gtk-2.0/menu.c
--- freeciv-cvs/client/gui-gtk-2.0/menu.c	2003-06-26 20:13:14.000000000 +0100
+++ freeciv-patched/client/gui-gtk-2.0/menu.c	2003-06-30 12:33:10.000000000 +0100
@@ -80,6 +80,8 @@
   MENU_KINGDOM_TAX_RATE,
   MENU_KINGDOM_FIND_CITY,
   MENU_KINGDOM_WORKLISTS,
+  MENU_KINGDOM_WORKSHOP,
+  MENU_KINGDOM_ENGINEERING,
   MENU_KINGDOM_REVOLUTION,
 
   MENU_VIEW_SHOW_MAP_GRID,
@@ -212,6 +214,12 @@
   case MENU_KINGDOM_WORKLISTS:
     popup_worklists_report();
     break;
+  case MENU_KINGDOM_WORKSHOP:
+    popup_workshop_dialog();
+    break;
+  case MENU_KINGDOM_ENGINEERING:
+    popup_engineering_dialog();
+    break;
   case MENU_KINGDOM_REVOLUTION:
     popup_revolution_dialog();
     break;
@@ -597,6 +605,12 @@
 	kingdom_menu_callback,	MENU_KINGDOM_WORKLISTS					},
   { "/" N_("Kingdom") "/sep2",				NULL,
 	NULL,			0,					"<Separator>"	},
+  { "/" N_("Kingdom") "/" N_("Design _Workshop"),	NULL,
+	kingdom_menu_callback,	MENU_KINGDOM_WORKSHOP					},
+  { "/" N_("Kingdom") "/" N_("Social _Engineering"),	NULL,
+	kingdom_menu_callback,	MENU_KINGDOM_ENGINEERING					},
+  { "/" N_("Kingdom") "/sep3",				NULL,
+	NULL,			0,					"<Separator>"	},
   { "/" N_("_Kingdom") "/" N_("_Government"),           NULL,
 	NULL,			0,					"<Branch>"	},
   { "/" N_("Kingdom") "/" N_("_Government") "/" N_("_Revolution"),
@@ -1052,6 +1066,10 @@
     }
 
     menus_set_sensitive("<main>/_Reports", TRUE);
+    menus_set_sensitive("<main>/_Kingdom/Design _Workshop",
+			game.num_unit_modifier_types > 0);
+    menus_set_sensitive("<main>/_Kingdom/Social _Engineering",
+			game.num_gov_modifier_types > 0);
     menus_set_sensitive("<main>/_Kingdom", TRUE);
     menus_set_sensitive("<main>/_View", TRUE);
     menus_set_sensitive("<main>/_Orders", can_client_issue_orders());
diff -Nur -Xfreecivdiff.ignore freeciv-cvs/client/include/dialogs_g.h freeciv-patched/client/include/dialogs_g.h
--- freeciv-cvs/client/include/dialogs_g.h	2002-11-29 12:25:10.000000000 +0000
+++ freeciv-patched/client/include/dialogs_g.h	2003-06-30 12:33:10.000000000 +0100
@@ -15,10 +15,9 @@
 
 #include "shared.h"		/* bool type */
 #include "terrain.h"		/* enum tile_special_type */
+#include "unittype.h"		/* Unit_Type_id type */
 
 struct tile;
-struct unit;
-struct city;
 struct packet_nations_used;
 
 void popup_notify_goto_dialog(char *headline, char *lines, int x, int y);
@@ -33,6 +32,9 @@
 
 void popup_revolution_dialog(void);
 void popup_government_dialog(void);
+void popup_workshop_dialog(void);
+void popup_engineering_dialog(void);
+void workshop_dialog_update(Unit_Type_id id);
 void popup_caravan_dialog(struct unit *punit,
 			  struct city *phomecity, struct city *pdestcity);
 bool caravan_dialog_is_open(void);
diff -Nur -Xfreecivdiff.ignore freeciv-cvs/client/packhand.c freeciv-patched/client/packhand.c
--- freeciv-cvs/client/packhand.c	2003-06-30 12:33:43.000000000 +0100
+++ freeciv-patched/client/packhand.c	2003-06-30 12:33:10.000000000 +0100
@@ -1945,6 +1945,25 @@
   game.num_impr_types = packet->num_impr_types;
   game.num_tech_types = packet->num_tech_types;
 
+  game.num_unit_modifier_types = packet->num_unit_modifier_types;
+  game.num_unit_modifiers = packet->num_unit_modifiers;
+  game.num_gov_modifier_types = packet->num_gov_modifier_types;
+  game.num_gov_modifiers = packet->num_gov_modifiers;
+
+  if (game.num_unit_modifiers > 0) {
+    unit_modifiers = fc_calloc(game.num_unit_modifiers,
+			       sizeof(struct modifier));
+  } else {
+    unit_modifiers = NULL;
+  }
+
+  if (game.num_gov_modifiers > 0) {
+    gov_modifiers = fc_calloc(game.num_gov_modifiers,
+			      sizeof(struct modifier));
+  } else {
+    gov_modifiers = NULL;
+  }
+
   governments_alloc(packet->government_count);
 
   nations_alloc(packet->nation_count);
@@ -1965,12 +1984,17 @@
 void handle_ruleset_unit(struct packet_ruleset_unit *p)
 {
   struct unit_type *u;
+  int i;
 
-  if(p->id < 0 || p->id >= game.num_unit_types || p->id >= U_LAST) {
+  if(p->id < 0 || p->id >= U_LAST) {
     freelog(LOG_ERROR, "Received bad unit_type id %d in handle_ruleset_unit()",
 	    p->id);
     return;
   }
+  if (p->id >= game.num_unit_types) {
+    freelog(LOG_NORMAL, "Number of unit types increased to %d", p->id + 1);
+    game.num_unit_types = p->id + 1;
+  }
   u = get_unit_type(p->id);
 
   sz_strlcpy(u->name, p->name);
@@ -2006,9 +2030,72 @@
   u->paratroopers_mr_sub = p->paratroopers_mr_sub;
   u->effect             = p->effect;  /* pointer assignment */
 
+  u->basetype         = p->basetype;
+  for (i = 0; i < MAX_MODS; ++i) {
+    u->modifiers[i]   = p->modifiers[i];
+  }
+
   u->helptext = p->helptext;	/* pointer assignment */
 
   tilespec_setup_unit_type(p->id);
+
+  workshop_dialog_update(p->id);
+}
+
+/**************************************************************************
+...
+**************************************************************************/
+void handle_ruleset_modifier_type(
+    struct packet_ruleset_modifier_type *p)
+{
+  struct modifier_type *u;
+  int max_mod;
+
+  max_mod = (p->is_gov ? game.num_gov_modifier_types
+      		       : game.num_unit_modifier_types);
+
+  if (p->id < 0 || p->id >= max_mod) {
+    freelog(LOG_ERROR, "Received bad modifier type id %d in "
+                     "handle_ruleset_modifier_type()", p->id);
+    return;
+  }
+  if (p->is_gov) {
+    u = &gov_modifier_types[p->id];
+  } else {
+    u = &unit_modifier_types[p->id];
+  }
+  sz_strlcpy(u->name, p->name);
+  u->id = p->basetype;
+  u->tech_requirement = p->tech_requirement;
+  u->must_use = p->must_use;
+}
+
+/**************************************************************************
+...
+**************************************************************************/
+void handle_ruleset_modifier(struct packet_ruleset_modifier *p)
+{
+  struct modifier *u;
+  int max_mod;
+
+  max_mod = (p->is_gov ? game.num_gov_modifiers
+      		       : game.num_unit_modifiers);
+
+  if (p->id < 0 || p->id >= max_mod) {
+    freelog(LOG_ERROR,
+      "Received bad modifier id %d in handle_ruleset_modifier()",
+      p->id);
+    return;
+  }
+  if (p->is_gov) {
+    u = &gov_modifiers[p->id];
+  } else {
+    u = &unit_modifiers[p->id];
+  }
+  sz_strlcpy(u->name, p->name);
+  u->type = p->type;
+  u->tech_requirement = p->tech_requirement;
+  u->effect = p->effect;	/* pointer assignment */
 }
 
 /**************************************************************************
@@ -2211,13 +2298,21 @@
 void handle_ruleset_government(struct packet_ruleset_government *p)
 {
   struct government *gov;
+  int i;
 
-  if (p->id < 0 || p->id >= game.government_count) {
+  if (p->id < 0 || p->id >= G_MAGIC) {
     freelog(LOG_ERROR,
 	    "Received bad government id %d in handle_ruleset_government",
 	    p->id);
     return;
   }
+  if (p->id >= game.government_count) {
+    freelog(LOG_NORMAL, "Number of gov types increased to %d", p->id + 1);
+    game.government_count = p->id + 1;
+    governments = (struct government *)fc_realloc(governments,
+        					  game.government_count
+                                                  * sizeof(struct government));
+  }
   gov = &governments[p->id];
 
   gov->index             = p->id;
@@ -2230,6 +2325,11 @@
   gov->empire_size_mod   = p->empire_size_mod;
   gov->empire_size_inc   = p->empire_size_inc;
   gov->rapture_size      = p->rapture_size;
+  gov->basetype		 = p->basetype;
+
+  for (i = 0; i < MAX_MODS; ++i) {
+    gov->modifiers[i]    = p->modifiers[i];
+  }
   
   gov->unit_happy_cost_factor  = p->unit_happy_cost_factor;
   gov->unit_shield_cost_factor = p->unit_shield_cost_factor;
@@ -2286,6 +2386,13 @@
   gov->helptext = p->helptext;	/* pointer assignment */
   
   tilespec_setup_government(p->id);
+
+  /* If we're getting governments re-sent after the game has started,
+   * then this generally means a player has changed government, and
+   * thus an effect update is needed. */
+  if(get_client_state()==CLIENT_GAME_RUNNING_STATE) {
+    update_all_effects();
+  }
 }
 void handle_ruleset_government_ruler_title
   (struct packet_ruleset_government_ruler_title *p)
diff -Nur -Xfreecivdiff.ignore freeciv-cvs/client/packhand.h freeciv-patched/client/packhand.h
--- freeciv-cvs/client/packhand.h	2003-06-27 15:51:10.000000000 +0100
+++ freeciv-patched/client/packhand.h	2003-06-30 12:33:10.000000000 +0100
@@ -47,6 +47,9 @@
 void handle_remove_player(struct packet_generic_integer *packet);
 void handle_ruleset_control(struct packet_ruleset_control *packet);
 void handle_ruleset_unit(struct packet_ruleset_unit *p);
+void handle_ruleset_modifier_type(
+    struct packet_ruleset_modifier_type *p);
+void handle_ruleset_modifier(struct packet_ruleset_modifier *p);
 void handle_ruleset_tech(struct packet_ruleset_tech *p);
 void handle_ruleset_building(struct packet_ruleset_building *p);
 void handle_ruleset_terrain(struct packet_ruleset_terrain *p);
diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/capstr.c freeciv-patched/common/capstr.c
--- freeciv-cvs/common/capstr.c	2003-06-30 12:33:43.000000000 +0100
+++ freeciv-patched/common/capstr.c	2003-06-30 12:33:33.000000000 +0100
@@ -78,7 +78,7 @@
                    "city_struct_minor_cleanup obsolete_last class_legend " \
                    "+impr_req +waste +fastfocus +continent +small_dipl " \
                    "+no_nation_selected +diplomacy +no_extra_tiles " \
-                   "+diplomacy2 +citizens_style +root_tech impr_gen"
+                   "+diplomacy2 +citizens_style +root_tech impr_gen compounds"
 
 /* "+1.14.0" is protocol for 1.14.0 release.
  *
diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/game.h freeciv-patched/common/game.h
--- freeciv-cvs/common/game.h	2003-06-30 12:33:43.000000000 +0100
+++ freeciv-patched/common/game.h	2003-06-30 12:33:10.000000000 +0100
@@ -152,6 +152,10 @@
   int num_unit_types;
   int num_impr_types;
   int num_tech_types;  /* including A_NONE */
+  int num_unit_modifier_types;
+  int num_unit_modifiers;
+  int num_gov_modifier_types;
+  int num_gov_modifiers;
 
   int government_count;
   int default_government;
diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/government.c freeciv-patched/common/government.c
--- freeciv-cvs/common/government.c	2003-06-30 12:33:43.000000000 +0100
+++ freeciv-patched/common/government.c	2003-06-30 12:33:10.000000000 +0100
@@ -19,9 +19,11 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include "fcintl.h"
 #include "game.h"
 #include "log.h"
 #include "mem.h"
+#include "member.h"
 #include "player.h"
 #include "shared.h"
 #include "support.h"
@@ -59,6 +61,8 @@
  */
 
 struct government *governments = NULL;
+struct modifier_type gov_modifier_types[MAX_MODS];
+struct modifier *gov_modifiers = NULL;
 
 struct ai_gov_tech_hint ai_gov_tech_hints[MAX_NUM_TECH_LIST];
 
@@ -66,7 +70,7 @@
   "(unused)", "Revolution_When_Unhappy", "Has_Senate",
   "Unbribable", "Inspires_Partisans", "Rapture_City_Growth",
   "Fanatic_Troops", "No_Unhappy_Citizens", "Convert_Tithes_To_Money",
-  "Reduced_Research"
+  "Reduced_Research", "CanModify"
 };
 static const char *hint_names[] = {
   "Is_Nice", "Favors_Growth"
@@ -143,6 +147,25 @@
   return NULL;
 }
 
+int find_gov_modifier_by_name(const char *s)
+{
+  return find_modifier_by_name(s, gov_modifiers, game.num_gov_modifiers);
+}
+
+bool government_has_modifier(struct government *gov, int govmod)
+{
+  if (gov && gov->basetype != G_MAGIC) {
+    int i;
+
+    for (i = 0; i < game.num_gov_modifier_types; ++i) {
+      if (gov->modifiers[i] == govmod) {
+	return TRUE;
+      }
+    }
+  }
+  return FALSE;
+}
+
 /***************************************************************
 ...
 ***************************************************************/
@@ -319,3 +342,226 @@
   governments = NULL;
   game.government_count = 0;
 }
+
+static void set_city_cost(void *structpt, unsigned long offset,
+    			  const size_t size, const char *val)
+{
+  int *member = FC_STRUCT_MEMBER_P(int, structpt, offset);
+
+  if (mystrcasecmp(val, "City_Size") == 0) {
+    *member = G_CITY_SIZE_FREE;
+  } else {
+    *member = atoi(val);
+  }
+}
+
+static void read_city_cost(void *structpt, unsigned long offset,
+    			   const size_t size, struct section_file *file,
+			   const char *sec, const char *membername)
+{
+  char *sval;
+
+  sval = secfile_lookup_str(file, "%s.%s", sec, membername);
+  set_city_cost(structpt, offset, size, sval);
+}
+
+#define FC_GOV_OFFSET(member) \
+    FC_STRUCT_OFFSET(struct government, member)
+#define FC_GOV_SIZE(member) \
+    FC_MEMBER_SIZE(struct government, member)
+
+static struct members gov_members[] = {
+  { "name",
+    FC_GOV_OFFSET(name_orig),
+    FC_GOV_SIZE(name_orig),
+    NULL, set_string, get_string, binary_string },
+  { "graphic", FC_GOV_OFFSET(graphic_str), FC_GOV_SIZE(graphic_str),
+    read_string, set_string, get_string, binary_string },
+  { "graphic_alt", FC_GOV_OFFSET(graphic_alt), FC_GOV_SIZE(graphic_alt),
+    read_string, set_string, get_string, binary_string },
+  { "tech_req", FC_GOV_OFFSET(required_tech),
+    FC_GOV_SIZE(required_tech),
+    NULL, set_tech, get_tech, NULL },
+  { "martial_law_max", FC_GOV_OFFSET(martial_law_max),
+    FC_GOV_SIZE(martial_law_max),
+    read_int, set_int, get_int, binary_int },
+  { "martial_law_per", FC_GOV_OFFSET(martial_law_per),
+    FC_GOV_SIZE(martial_law_per),
+    read_int, set_int, get_int, binary_int },
+  { "max_single_rate", FC_GOV_OFFSET(max_rate), FC_GOV_SIZE(max_rate),
+    read_int, set_int, get_int, binary_int },
+  { "civil_war_chance", FC_GOV_OFFSET(civil_war), FC_GOV_SIZE(civil_war),
+    read_int, set_int, get_int, binary_int },
+  { "empire_size_mod", FC_GOV_OFFSET(empire_size_mod),
+    FC_GOV_SIZE(empire_size_mod),
+    read_int, set_int, get_int, binary_int },
+  { "empire_size_inc", FC_GOV_OFFSET(empire_size_inc),
+    FC_GOV_SIZE(empire_size_inc),
+    read_int, set_int, get_int, binary_int },
+  { "rapture_size", FC_GOV_OFFSET(rapture_size), FC_GOV_SIZE(rapture_size),
+    read_int, set_int, get_int, binary_int },
+  { "unit_free_unhappy", FC_GOV_OFFSET(free_happy),
+    FC_GOV_SIZE(free_happy),
+    read_city_cost, set_city_cost, get_int, binary_int },
+  { "unit_free_shield", FC_GOV_OFFSET(free_shield),
+    FC_GOV_SIZE(free_shield),
+    read_city_cost, set_city_cost, get_int, binary_int },
+  { "unit_free_food", FC_GOV_OFFSET(free_food),
+    FC_GOV_SIZE(free_food),
+    read_city_cost, set_city_cost, get_int, binary_int },
+  { "unit_free_gold", FC_GOV_OFFSET(free_gold),
+    FC_GOV_SIZE(free_gold),
+    read_city_cost, set_city_cost, get_int, binary_int },
+  { "unit_unhappy_factor", FC_GOV_OFFSET(unit_happy_cost_factor),
+    FC_GOV_SIZE(unit_happy_cost_factor),
+    read_int, set_int, get_int, binary_int },
+  { "unit_shield_factor", FC_GOV_OFFSET(unit_shield_cost_factor),
+    FC_GOV_SIZE(unit_shield_cost_factor),
+    read_int, set_int, get_int, binary_int },
+  { "unit_food_factor", FC_GOV_OFFSET(unit_food_cost_factor),
+    FC_GOV_SIZE(unit_food_cost_factor),
+    read_int, set_int, get_int, binary_int },
+  { "unit_gold_factor", FC_GOV_OFFSET(unit_gold_cost_factor),
+    FC_GOV_SIZE(unit_gold_cost_factor),
+    read_int, set_int, get_int, binary_int },
+  { "corruption_level", FC_GOV_OFFSET(corruption_level),
+    FC_GOV_SIZE(corruption_level),
+    read_int, set_int, get_int, binary_int },
+  { "corruption_modifier", FC_GOV_OFFSET(corruption_modifier),
+    FC_GOV_SIZE(corruption_modifier),
+    read_int, set_int, get_int, binary_int },
+  { "corruption_fixed_distance", FC_GOV_OFFSET(fixed_corruption_distance),
+    FC_GOV_SIZE(fixed_corruption_distance),
+    read_int, set_int, get_int, binary_int },
+  { "corruption_distance_factor", FC_GOV_OFFSET(corruption_distance_factor),
+    FC_GOV_SIZE(corruption_distance_factor),
+    read_int, set_int, get_int, binary_int },
+  { "corruption_extra_distance", FC_GOV_OFFSET(extra_corruption_distance),
+    FC_GOV_SIZE(extra_corruption_distance),
+    read_int, set_int, get_int, binary_int },
+  { "trade_penalty", FC_GOV_OFFSET(trade_before_penalty),
+    FC_GOV_SIZE(trade_before_penalty),
+    read_int, set_int, get_int, binary_int },
+  { "shield_penalty", FC_GOV_OFFSET(shields_before_penalty),
+    FC_GOV_SIZE(shields_before_penalty),
+    read_int, set_int, get_int, binary_int },
+  { "food_penalty", FC_GOV_OFFSET(food_before_penalty),
+    FC_GOV_SIZE(food_before_penalty),
+    read_int, set_int, get_int, binary_int },
+  { "celeb_trade_penalty", FC_GOV_OFFSET(celeb_trade_before_penalty),
+    FC_GOV_SIZE(celeb_trade_before_penalty),
+    read_int, set_int, get_int, binary_int },
+  { "celeb_shield_penalty", FC_GOV_OFFSET(celeb_shields_before_penalty),
+    FC_GOV_SIZE(celeb_shields_before_penalty),
+    read_int, set_int, get_int, binary_int },
+  { "celeb_food_penalty", FC_GOV_OFFSET(celeb_food_before_penalty),
+    FC_GOV_SIZE(celeb_food_before_penalty),
+    read_int, set_int, get_int, binary_int },
+  { "trade_bonus", FC_GOV_OFFSET(trade_bonus),
+    FC_GOV_SIZE(trade_bonus),
+    read_int, set_int, get_int, binary_int },
+  { "shield_bonus", FC_GOV_OFFSET(shield_bonus),
+    FC_GOV_SIZE(shield_bonus),
+    read_int, set_int, get_int, binary_int },
+  { "food_bonus", FC_GOV_OFFSET(food_bonus),
+    FC_GOV_SIZE(food_bonus),
+    read_int, set_int, get_int, binary_int },
+  { "celeb_trade_bonus", FC_GOV_OFFSET(celeb_trade_bonus),
+    FC_GOV_SIZE(celeb_trade_bonus),
+    read_int, set_int, get_int, binary_int },
+  { "celeb_shield_bonus", FC_GOV_OFFSET(celeb_shield_bonus),
+    FC_GOV_SIZE(celeb_shield_bonus),
+    read_int, set_int, get_int, binary_int },
+  { "celeb_food_bonus", FC_GOV_OFFSET(celeb_food_bonus),
+    FC_GOV_SIZE(celeb_food_bonus),
+    read_int, set_int, get_int, binary_int },
+  { NULL, 0, 0, NULL, NULL, NULL, NULL }
+};
+
+void gov_copy_members(struct government *dest, const struct government *src)
+{
+  copy_members((void *)dest, (const void *)src, gov_members);
+}
+
+void gov_read_members(struct government *gov, struct section_file *file,
+    		      const char *sec)
+{
+  read_members((void *)gov, gov_members, file, sec);
+}
+
+static void ApplyGovModAction(struct government *gov, char *action)
+{
+  apply_action((void *)gov, gov_members, action);
+}
+
+/***************************************************************
+...
+***************************************************************/
+int form_compound_gov(const struct player *pplayer, const char *name,
+    		      const int basetype, const int modifiers[MAX_MODS])
+{
+  struct government *gov = NULL, *base;
+  struct modifier *mod;
+  int id = -1, i;
+
+  if (pplayer) {
+    id = pplayer->government;
+    gov = get_gov_pplayer(pplayer);
+  }
+  if (!pplayer || gov->basetype == G_MAGIC) {
+    id = game.government_count++;
+    if (id >= G_MAGIC) {
+      freelog(LOG_FATAL, "form_compound_gov: out of government types");
+      exit(EXIT_FAILURE);
+    }
+
+    governments = (struct government *)fc_realloc(governments,
+						  game.government_count
+						  * sizeof(struct government));
+    gov = &governments[id];
+    gov->helptext = NULL;
+    gov->index = id;
+  } else if (pplayer) {
+    government_free(gov);
+  }
+  base = &governments[basetype];
+  gov_copy_members(gov, base);
+  gov->effect = NULL;
+  append_effects(&gov->effect, base->effect);
+  gov->flags = base->flags;
+  gov->hints = base->hints;
+  gov->subgoal = base->subgoal;
+  gov->num_ruler_titles = base->num_ruler_titles;
+  gov->ruler_titles = fc_calloc(gov->num_ruler_titles,
+      				sizeof(struct ruler_title));
+  for (i = 0; i < gov->num_ruler_titles; ++i) {
+    struct ruler_title *fromtitle = &(base->ruler_titles[i]);
+    struct ruler_title *totitle = &(gov->ruler_titles[i]);
+
+    totitle->nation = fromtitle->nation;
+    sz_strlcpy(totitle->male_title, fromtitle->male_title);
+    sz_strlcpy(totitle->female_title, fromtitle->female_title);
+  }
+  gov->basetype = basetype;
+  for (i = 0; i < game.num_gov_modifier_types; ++i) {
+    gov->modifiers[i] = modifiers[i];
+  }
+  gov->required_tech = A_NONE;
+  gov->name_orig[0] = '\0';
+  for (i = 0; i < game.num_gov_modifier_types; ++i) {
+    if (gov->modifiers[i] < U_LAST) {
+      mod = &gov_modifiers[gov->modifiers[i]];
+      ApplyGovModAction(gov, mod->action);
+    }
+  }
+  for (i = 0; i < game.num_gov_modifier_types; ++i) {
+    if (gov->modifiers[i] < U_LAST) {
+      mod = &gov_modifiers[gov->modifiers[i]];
+      ApplyGovModAction(gov, mod->final_action);
+      append_effects(&gov->effect, mod->effect);
+    }
+  }
+  sz_strlcpy(gov->name, _(gov->name_orig));
+
+  return id;
+}
diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/government.h freeciv-patched/common/government.h
--- freeciv-cvs/common/government.h	2003-06-30 12:33:43.000000000 +0100
+++ freeciv-patched/common/government.h	2003-06-30 12:33:10.000000000 +0100
@@ -14,11 +14,13 @@
 #define FC__GOVERNMENT_H
 
 #include "shared.h"
+#include "modifier.h"
 
 struct city;
 struct player;
 struct Sprite;			/* opaque; client-gui specific */
 struct impr_effect;
+struct section_file;
 
 #define G_MAGIC (127)		/* magic constant, used as flag value */
 
@@ -39,6 +41,7 @@
   G_CONVERT_TITHES_TO_MONEY,    /* tithes to money, needed by fundamentalism */
   G_REDUCED_RESEARCH,           /* penalty for research, needed by
 				   fundamentalism */
+  G_CAN_MODIFY,			/* can apply government modifiers */
   G_LAST_FLAG
 };
 #define G_FIRST_FLAG G_BUILD_VETERAN_DIPLOMAT
@@ -156,6 +159,9 @@
   struct Sprite *sprite;
 
   struct impr_effect *effect; /* list; .type==EFT_LAST terminated */
+
+  int basetype;		/* if !G_MAGIC, government modifiers are applied */
+  int modifiers[MAX_MODS];
   
   char *helptext;
 };
@@ -172,6 +178,8 @@
 };
 
 extern struct government *governments;
+extern struct modifier_type gov_modifier_types[MAX_MODS];
+extern struct modifier *gov_modifiers;
 
 extern struct ai_gov_tech_hint ai_gov_tech_hints[MAX_NUM_TECH_LIST];
 /* like game.rtech lists, A_LAST terminated (for .tech)
@@ -182,6 +190,8 @@
 struct government *get_gov_pcity(struct city *pcity);
 
 struct government *find_government_by_name(const char *name);
+int find_gov_modifier_by_name(const char *s);
+bool government_has_modifier(struct government *gov, int govmod);
 
 enum government_flag_id government_flag_from_str(const char *s);
 bool government_has_flag(const struct government *gov,
@@ -201,5 +211,10 @@
                      const char *male, const char *female);
 void governments_alloc(int num);
 void governments_free(void);
+void gov_copy_members(struct government *dest, const struct government *src);
+void gov_read_members(struct government *gov, struct section_file *file,
+		      const char *sec);
+int form_compound_gov(const struct player *pplayer, const char *name,
+    		      const int basetype, const int modifiers[MAX_MODS]);
 
 #endif  /* FC__GOVERNMENT_H */
diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/improvement.c freeciv-patched/common/improvement.c
--- freeciv-cvs/common/improvement.c	2003-06-30 12:33:43.000000000 +0100
+++ freeciv-patched/common/improvement.c	2003-06-30 12:33:10.000000000 +0100
@@ -1035,6 +1035,12 @@
     freelog(LOG_DEBUG, "Effect requires a government");
     return FALSE;
   }
+  if (imeff->cond_govmod != game.num_gov_modifiers && pplayer
+      && !government_has_modifier(get_gov_pplayer(pplayer),
+				  imeff->cond_govmod)) {
+    freelog(LOG_DEBUG, "Effect requires a government modifier");
+    return FALSE;
+  }
   if (pplayer && get_invention(pplayer, imeff->cond_adv) != TECH_KNOWN) {
     freelog(LOG_DEBUG, "Effect requires a future tech");
     return FALSE;
@@ -1457,3 +1463,30 @@
     }
   }
 }
+
+void append_effects(struct impr_effect **dest, struct impr_effect *src)
+{
+  int numsrc = 0, numdest = 0, numeff;
+  struct impr_effect *eff;
+
+  for (eff = *dest; eff && eff->type != EFT_LAST; ++eff) {
+    numdest++;
+  }
+  for (eff = src; eff && eff->type != EFT_LAST; ++eff) {
+    numsrc++;
+  }
+  numeff = numdest + numsrc;
+
+  if (numeff > MAX_EFFECTS) {
+    freelog(LOG_FATAL, "MAX_EFFECTS exceeded");
+    exit(EXIT_FAILURE);
+  }
+
+  if (numsrc) {
+    *dest = fc_realloc(*dest, (numeff + 1) * sizeof(struct impr_effect));
+    memcpy(*dest + numdest, src, (numsrc + 1) * sizeof(struct impr_effect));
+  } else if (!*dest) {
+    *dest = fc_malloc(sizeof(struct impr_effect));
+    (*dest)->type = EFT_LAST;
+  }
+}
diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/improvement.h freeciv-patched/common/improvement.h
--- freeciv-cvs/common/improvement.h	2003-06-30 12:33:43.000000000 +0100
+++ freeciv-patched/common/improvement.h	2003-06-30 12:33:10.000000000 +0100
@@ -201,6 +201,7 @@
 					 * center tile */
   Impr_Type_id cond_bldg;	   /* B_LAST = unconditional */
   int cond_gov;			   /* game.government_count = unconditional */
+  int cond_govmod;		   /* game.num_gov_modifiers = unconditional */
   Tech_Type_id cond_adv;	   /* A_NONE = unconditional; A_LAST = never */
   enum effect_type cond_eff;	   /* EFT_LAST = unconditional */
   Unit_Class_id aff_unit;	   /* UCL_LAST = all */
@@ -440,6 +441,7 @@
 struct ceff_vector *get_eff_city(struct city *pcity);
 void remove_global_effects(Impr_Type_id impr, struct player *pplayer);
 void add_global_effects(Impr_Type_id impr, struct player *pplayer);
+void append_effects(struct impr_effect **dest, struct impr_effect *src);
 
 void allot_island_improvs(void);
 void improvements_update_obsolete(void);
diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/Makefile.am freeciv-patched/common/Makefile.am
--- freeciv-cvs/common/Makefile.am	2003-06-07 19:58:26.000000000 +0100
+++ freeciv-patched/common/Makefile.am	2003-06-30 12:33:10.000000000 +0100
@@ -63,6 +63,10 @@
 		pqueue.h	\
 		mem.c           \
 		mem.h		\
+		member.c        \
+		member.h	\
+		modifier.c	\
+		modifier.h	\
 		rand.c		\
 		rand.h		\
 		registry.c	\
diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/member.c freeciv-patched/common/member.c
--- freeciv-cvs/common/member.c	1970-01-01 01:00:00.000000000 +0100
+++ freeciv-patched/common/member.c	2003-06-30 12:33:10.000000000 +0100
@@ -0,0 +1,216 @@
+/********************************************************************** 
+ Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include "member.h"
+#include "support.h"
+
+void read_sound(void *structpt, unsigned long offset, const size_t size,
+                struct section_file *file,
+                const char *sec, const char *membername)
+{
+  char *member = FC_STRUCT_MEMBER_P(char, structpt, offset);
+
+  mystrlcpy(member,
+            secfile_lookup_str_default(file, "-", "%s.%s", sec, membername),
+            size);
+//printf("Read %s -> (sound) %s\n", membername, member);
+}
+
+void read_string(void *structpt, unsigned long offset, const size_t size,
+                 struct section_file *file,
+                 const char *sec, const char *membername)
+{
+  char *member = FC_STRUCT_MEMBER_P(char, structpt, offset);
+
+  mystrlcpy(member,
+            secfile_lookup_str(file, "%s.%s", sec, membername),
+            size);
+//printf("Read %s -> (string) %s\n", membername, member);
+}
+
+void set_string(void *structpt, unsigned long offset, const size_t size,
+                const char *val)
+{
+  char *member = FC_STRUCT_MEMBER_P(char, structpt, offset);
+
+  mystrlcpy(member, val, size);
+}
+
+void get_string(const void *structpt, unsigned long offset,
+    		struct memberdata *data)
+{
+  char *member = FC_STRUCT_MEMBER_P(char, structpt, offset);
+
+  sz_strlcpy(data->str, member);
+}
+
+void binary_string(const char *arg1, const char *arg2, const char op,
+		   struct memberdata *result)
+{
+  if (op == '+') {
+    sz_strlcpy(result->str, arg1);
+    sz_strlcat(result->str, arg2);
+  }
+}
+
+void read_int(void *structpt, unsigned long offset, const size_t size,
+              struct section_file *file,
+              const char *sec, const char *membername)
+{
+  int *member = FC_STRUCT_MEMBER_P(int, structpt, offset);
+
+  *member = secfile_lookup_int(file, "%s.%s", sec, membername);
+//printf("Read %s -> (int) %d\n", membername, *member);
+}
+
+void set_int(void *structpt, unsigned long offset, const size_t size,
+             const char *val)
+{
+  int *member = FC_STRUCT_MEMBER_P(int, structpt, offset);
+
+  *member = atoi(val);
+}
+
+void get_int(const void *structpt, unsigned long offset,
+	     struct memberdata *data)
+{
+  int *member = FC_STRUCT_MEMBER_P(int, structpt, offset);
+
+  my_snprintf(data->str, sizeof(data->str), "%d", *member);
+}
+
+void binary_int(const char *arg1, const char *arg2, const char op,
+		struct memberdata *result)
+{
+  int int1, int2, ires;
+
+  int1 = atoi(arg1);
+  int2 = atoi(arg2);
+  switch(op) {
+  case '+':
+    ires = int1 + int2;
+    break;
+  case '-':
+    ires = int1 - int2;
+    break;
+  case '*':
+    ires = int1 * int2;
+    break;
+  case '/':
+    ires = int1 / int2;
+    break;
+  default:
+    return;
+  }
+  my_snprintf(result->str, sizeof(result->str), "%d", ires);
+}
+
+void read_tech(void *structpt, unsigned long offset, const size_t size,
+               struct section_file *file,
+               const char *sec, const char *membername)
+{
+}
+
+void set_tech(void *structpt, unsigned long offset, const size_t size,
+              const char *val)
+{
+}
+
+void get_tech(const void *structpt, unsigned long offset,
+	      struct memberdata *data)
+{
+}
+
+void copy_members(void *deststructpt, const void *srcstructpt,
+		  const struct members *memberlist)
+{
+  const struct members *pt;
+  struct memberdata data;
+
+  for (pt = memberlist; pt && pt->name; ++pt) {
+    pt->getfunc(srcstructpt, pt->offset, &data);
+    pt->setfunc(deststructpt, pt->offset, pt->size, data.str);
+  }
+}
+
+void read_members(void *structpt, const struct members *memberlist,
+                  struct section_file *file, const char *sec)
+{
+  const struct members *pt;
+
+  for (pt = memberlist; pt && pt->name; ++pt) {
+    if (pt->readfunc) {
+      pt->readfunc(structpt, pt->offset, pt->size, file, sec, pt->name);
+    }
+  }
+}
+
+void read_member(void *structpt, const struct members *memberlist,
+                 const char *membername, struct section_file *file,
+		 const char *sec)
+{
+  const struct members *pt;
+
+  for (pt = memberlist; pt && pt->name; ++pt) {
+    if (strcmp(pt->name, membername) == 0 && pt->readfunc) {
+      pt->readfunc(structpt, pt->offset, pt->size, file, sec, pt->name);
+      return;
+    }
+  }
+}
+
+void set_member(void *structpt, const struct members *memberlist,
+                const char *membername, const char *val)
+{
+  const struct members *pt;
+
+  for (pt = memberlist; pt && pt->name; ++pt) {
+    if (strcmp(pt->name, membername) == 0) {
+      pt->setfunc(structpt, pt->offset, pt->size, val);
+      return;
+    }
+  }
+}
+
+void binary_member(void *structpt, const struct members *memberlist,
+    		   const char *membername, const char *arg1, const char *arg2,
+		   const char op, struct memberdata *result)
+{
+  const struct members *pt;
+
+  for (pt = memberlist; pt && pt->name; ++pt) {
+    if (strcmp(pt->name, membername) == 0 && pt->binfunc) {
+      pt->binfunc(arg1, arg2, op, result);
+      return;
+    }
+  }
+}
+
+void get_member(const void *structpt, const struct members *memberlist,
+                const char *membername, struct memberdata *data)
+{
+  const struct members *pt;
+
+  for (pt = memberlist; pt && pt->name; ++pt) {
+    if (strcmp(pt->name, membername) == 0) {
+      pt->getfunc(structpt, pt->offset, data);
+      return;
+    }
+  }
+}
diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/member.h freeciv-patched/common/member.h
--- freeciv-cvs/common/member.h	1970-01-01 01:00:00.000000000 +0100
+++ freeciv-patched/common/member.h	2003-06-30 12:33:10.000000000 +0100
@@ -0,0 +1,98 @@
+/********************************************************************** 
+ Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+***********************************************************************/
+#ifndef FC__MEMBER_H
+#define FC__MEMBER_H
+
+/* Functions for accessing structure members by name at runtime */
+
+#include "registry.h"
+
+/* Macros for converting structure members to and from offsets - taken
+ * from GLib.
+ */
+#define FC_STRUCT_OFFSET(struct_type, member) \
+    ((unsigned long) ((char *) &((struct_type *) 0)->member))
+#define FC_STRUCT_MEMBER_P(member_type, struct_p, struct_offset) \
+    ((member_type *) ((char *)(struct_p) + (unsigned long)(struct_offset)))
+#define FC_MEMBER_SIZE(struct_type, member) \
+    (sizeof ((struct_type *) 0)->member)
+ 
+struct memberdata {
+  char str[400];
+};
+
+typedef void (*MemberReadFunc)(void *structpt, unsigned long offset,
+                               const size_t size, struct section_file *file,
+                               const char *sec, const char *membername);
+typedef void (*MemberSetFunc)(void *structpt, unsigned long offset,
+                              const size_t size, const char *val);
+typedef void (*MemberGetFunc)(const void *structpt, unsigned long offset,
+    			      struct memberdata *data);
+typedef void (*MemberBinaryFunc)(const char *arg1, const char *arg2,
+    				 const char op, struct memberdata *result);
+
+struct members {
+  char *name;
+  unsigned long offset;
+  size_t size;
+  MemberReadFunc readfunc;
+  MemberSetFunc setfunc;
+  MemberGetFunc getfunc;
+  MemberBinaryFunc binfunc;
+};
+
+void read_sound(void *structpt, unsigned long offset, const size_t size,
+                struct section_file *file,
+                const char *sec, const char *membername);
+void read_string(void *structpt, unsigned long offset, const size_t size,
+                 struct section_file *file,
+                 const char *sec, const char *membername);
+void set_string(void *structpt, unsigned long offset, const size_t size,
+                const char *val);
+void get_string(const void *structpt, unsigned long offset,
+    		struct memberdata *data);
+void binary_string(const char *arg1, const char *arg2, const char op,
+    		   struct memberdata *result);
+void read_int(void *structpt, unsigned long offset, const size_t size,
+              struct section_file *file,
+              const char *sec, const char *membername);
+void set_int(void *structpt, unsigned long offset, const size_t size,
+             const char *val);
+void get_int(const void *structpt, unsigned long offset,
+    	     struct memberdata *data);
+void binary_int(const char *arg1, const char *arg2, const char op,
+		struct memberdata *result);
+void read_tech(void *structpt, unsigned long offset, const size_t size,
+               struct section_file *file,
+               const char *sec, const char *membername);
+void set_tech(void *structpt, unsigned long offset, const size_t size,
+              const char *val);
+void get_tech(const void *structpt, unsigned long offset,
+    	      struct memberdata *data);
+
+void binary_member(void *structpt, const struct members *memberlist,
+    		   const char *membername, const char *arg1, const char *arg2,
+		   const char op, struct memberdata *result);
+void copy_members(void *deststructpt, const void *srcstructpt,
+		  const struct members *memberlist);
+void read_members(void *structpt, const struct members *memberlist,
+                  struct section_file *file, const char *sec);
+void read_member(void *structpt, const struct members *memberlist,
+                 const char *membername, struct section_file *file,
+		 const char *sec);
+void set_member(void *structpt, const struct members *memberlist,
+                const char *membername, const char *val);
+void get_member(const void *structpt, const struct members *memberlist,
+                const char *membername, struct memberdata *data);
+
+#endif  /* FC__MEMBER_H */
diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/modifier.c freeciv-patched/common/modifier.c
--- freeciv-cvs/common/modifier.c	1970-01-01 01:00:00.000000000 +0100
+++ freeciv-patched/common/modifier.c	2003-06-30 12:33:10.000000000 +0100
@@ -0,0 +1,56 @@
+/********************************************************************** 
+ Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include "modifier.h"
+#include "shared.h"
+#include "support.h"
+
+/**************************************************************************
+  Convert modifier_type names to int; case insensitive;
+  returns -1 if can't match.
+**************************************************************************/
+int modifier_type_from_str(const char *s,
+    			   struct modifier_type *modifier_types,
+			   int num_mod_types)
+{
+  int i;
+  for (i = 0; i < num_mod_types; ++i) {
+    if (mystrcasecmp(modifier_types[i].name, s) == 0) {
+      return i;
+    }
+  }
+  return -1;
+}
+
+/**************************************************************************
+Does a linear search of modifiers[].name
+Returns -1 if none match.
+**************************************************************************/
+int find_modifier_by_name(const char *s,
+    			  struct modifier *modifiers,
+			  int num_mods)
+{
+  int i;
+  for (i = 0; i < num_mods; ++i) {
+    if (mystrcasecmp(modifiers[i].name, s) == 0) {
+      return i;
+    }
+  }
+  return -1;
+}
diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/modifier.h freeciv-patched/common/modifier.h
--- freeciv-cvs/common/modifier.h	1970-01-01 01:00:00.000000000 +0100
+++ freeciv-patched/common/modifier.h	2003-06-30 12:33:10.000000000 +0100
@@ -0,0 +1,48 @@
+/********************************************************************** 
+ Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+***********************************************************************/
+#ifndef FC__MODIFIER_H
+#define FC__MODIFIER_H
+
+#include "shared.h"
+
+struct impr_effect;
+
+/* Maximum number of modifier types */
+#define MAX_MODS 16
+
+struct modifier {
+  char name[MAX_LEN_NAME];
+  int type;
+  int tech_requirement;
+  int obsoleted_by;
+  char *action;
+  char *final_action;
+  struct impr_effect *effect; /* list; .type==EFT_LAST terminated */
+};
+  
+struct modifier_type {
+  char name[MAX_LEN_NAME];
+  int id;
+  int tech_requirement;
+  int obsoleted_by;
+  bool must_use;
+};
+
+int modifier_type_from_str(const char *s,
+    			   struct modifier_type *modifier_types,
+			   int num_mod_types);
+int find_modifier_by_name(const char *s,
+    			  struct modifier *modifiers,
+			  int num_mods);
+
+#endif  /* FC__MODIFIER_H */
diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/packets.c freeciv-patched/common/packets.c
--- freeciv-cvs/common/packets.c	2003-06-30 12:33:43.000000000 +0100
+++ freeciv-patched/common/packets.c	2003-06-30 12:33:10.000000000 +0100
@@ -38,6 +38,7 @@
 #include "dataio.h"
 #include "events.h"
 #include "fcintl.h"
+#include "government.h"
 #include "log.h"
 #include "mem.h"
 #include "support.h"
@@ -425,6 +426,12 @@
     return receive_packet_ruleset_government_ruler_title(pc);
   case PACKET_RULESET_NATION:
     return receive_packet_ruleset_nation(pc);
+  case PACKET_RULESET_MODIFIER_TYPE:
+    return receive_packet_ruleset_modifier_type(pc);
+  case PACKET_RULESET_MODIFIER:
+    return receive_packet_ruleset_modifier(pc);
+  case PACKET_REQUEST_COMPOUND:
+    return receive_packet_request_compound(pc);
   case PACKET_RULESET_CITY:
     return receive_packet_ruleset_city(pc);
   case PACKET_RULESET_GAME:
@@ -1890,6 +1897,12 @@
   if (has_capability("impr_gen", pc->capability)) {
     dio_put_uint8(&dout, packet->num_unit_flags);
   }
+  if (has_capability("compounds", pc->capability)) {
+    dio_put_uint8(&dout, packet->num_unit_modifier_types);
+    dio_put_uint8(&dout, packet->num_unit_modifiers);
+    dio_put_uint8(&dout, packet->num_gov_modifier_types);
+    dio_put_uint8(&dout, packet->num_gov_modifiers);
+  }
   dio_put_uint8(&dout, packet->num_unit_types);
   dio_put_uint8(&dout, packet->num_impr_types);
   dio_put_uint8(&dout, packet->num_tech_types);
@@ -1941,6 +1954,15 @@
   } else {
     packet->num_unit_flags = F_LAST;
   }
+  if (has_capability("compounds", pc->capability)) {
+    dio_get_uint8(&din, &packet->num_unit_modifier_types);
+    dio_get_uint8(&din, &packet->num_unit_modifiers);
+    dio_get_uint8(&din, &packet->num_gov_modifier_types);
+    dio_get_uint8(&din, &packet->num_gov_modifiers);
+  } else {
+    packet->num_unit_modifier_types = packet->num_unit_modifiers = 0;
+    packet->num_gov_modifier_types  = packet->num_gov_modifiers = 0;
+  }
   dio_get_uint8(&din, &packet->num_unit_types);
   dio_get_uint8(&din, &packet->num_impr_types);
   dio_get_uint8(&din, &packet->num_tech_types);
@@ -2009,6 +2031,13 @@
   if (has_capability("impr_gen", pc->capability)) {
     dio_put_effects(&dout, pc, packet->effect);
   }
+  if (has_capability("compounds", pc->capability)) {
+    int i;
+    dio_put_uint16(&dout, packet->basetype);
+    for (i = 0; i < MAX_MODS; ++i) {
+      dio_put_uint16(&dout, packet->modifiers[i]);
+    }
+  }
 
   /* This must be last, so client can determine length: */
   if(packet->helptext) {
@@ -2076,6 +2105,15 @@
     packet->effect = fc_malloc(sizeof(struct impr_effect));
     packet->effect[0].type = EFT_LAST;
   }
+  if (has_capability("compounds", pc->capability)) {
+    int i;
+    dio_get_uint16(&din, &packet->basetype);
+    for (i = 0; i < MAX_MODS; ++i) {
+      dio_get_uint16(&din, &packet->modifiers[i]);
+    }
+  } else {
+    packet->basetype = U_LAST;
+  }
 
   len = dio_input_remaining(&din);
   if (len > 0) {
@@ -2162,6 +2200,123 @@
 /**************************************************************************
 ...
 **************************************************************************/
+int send_packet_ruleset_modifier_type(struct connection *pc,
+                const struct packet_ruleset_modifier_type *packet)
+{
+  SEND_PACKET_START(PACKET_RULESET_MODIFIER_TYPE);
+
+  dio_put_uint8(&dout, packet->id);
+  dio_put_bool8(&dout, packet->is_gov);
+  dio_put_uint8(&dout, packet->basetype);
+  dio_put_string(&dout, packet->name);
+  dio_put_uint8(&dout, packet->tech_requirement);
+  dio_put_bool8(&dout, packet->must_use);
+
+  SEND_PACKET_END;
+}
+
+/**************************************************************************
+...
+**************************************************************************/
+struct packet_ruleset_modifier_type *
+receive_packet_ruleset_modifier_type(struct connection *pc)
+{
+  RECEIVE_PACKET_START(packet_ruleset_modifier_type, packet);
+
+  dio_get_uint8(&din, &packet->id);
+  dio_get_bool8(&din, &packet->is_gov);
+  dio_get_uint8(&din, &packet->basetype);
+  dio_get_string(&din, packet->name, sizeof(packet->name));
+  dio_get_uint8(&din, &packet->tech_requirement);
+  dio_get_bool8(&din, &packet->must_use);
+
+  RECEIVE_PACKET_END(packet);
+}
+
+/**************************************************************************
+...
+**************************************************************************/
+int send_packet_ruleset_modifier(struct connection *pc,
+                const struct packet_ruleset_modifier *packet)
+{
+  SEND_PACKET_START(PACKET_RULESET_MODIFIER);
+
+  dio_put_uint8(&dout, packet->id);
+  dio_put_bool8(&dout, packet->is_gov);
+  dio_put_string(&dout, packet->name);
+  dio_put_uint8(&dout, packet->type);
+  dio_put_uint8(&dout, packet->tech_requirement);
+  if (has_capability("impr_gen", pc->capability)) {
+    dio_put_effects(&dout, pc, packet->effect);
+  }
+
+  SEND_PACKET_END;
+}
+
+/**************************************************************************
+...
+**************************************************************************/
+struct packet_ruleset_modifier *
+receive_packet_ruleset_modifier(struct connection *pc)
+{
+  RECEIVE_PACKET_START(packet_ruleset_modifier, packet);
+
+  dio_get_uint8(&din, &packet->id);
+  dio_get_bool8(&din, &packet->is_gov);
+  dio_get_string(&din, packet->name, sizeof(packet->name));
+  dio_get_uint8(&din, &packet->type);
+  dio_get_uint8(&din, &packet->tech_requirement);
+  if (has_capability("impr_gen", pc->capability)) {
+    dio_get_effects(&din, pc, &packet->effect);
+  } else {
+    packet->effect = fc_malloc(sizeof(struct impr_effect));
+    packet->effect[0].type = EFT_LAST;
+  }
+
+  RECEIVE_PACKET_END(packet);
+}
+
+/**************************************************************************
+...
+**************************************************************************/
+int send_packet_request_compound(struct connection *pc,
+				 struct packet_request_compound *req)
+{
+  int i;
+  SEND_PACKET_START(PACKET_REQUEST_COMPOUND);
+
+  dio_put_bool8(&dout, req->is_gov);
+  dio_put_string(&dout, req->name);
+  dio_put_uint16(&dout, req->basetype);
+  for (i = 0; i < MAX_MODS; ++i) {
+    dio_put_uint16(&dout, req->modifiers[i]);
+  }
+
+  SEND_PACKET_END;
+}
+
+/**************************************************************************
+...
+**************************************************************************/
+struct packet_request_compound *
+receive_packet_request_compound(struct connection *pc)
+{
+  int i;
+  RECEIVE_PACKET_START(packet_request_compound, packet);
+
+  dio_get_bool8(&din, &packet->is_gov);
+  dio_get_string(&din, packet->name, sizeof(packet->name));
+  dio_get_uint16(&din, &packet->basetype);
+  for (i = 0; i < MAX_MODS; ++i) {
+    dio_get_uint16(&din, &packet->modifiers[i]);
+  }
+
+  RECEIVE_PACKET_END(packet);
+}
+
+/**************************************************************************
+...
+**************************************************************************/
 static void dio_put_effects(struct data_out *dout,
                             struct connection *pc,
                             const struct impr_effect *effect)
@@ -2186,6 +2341,9 @@
     }
     dio_put_uint8(dout, eff->cond_bldg);
     dio_put_uint8(dout, eff->cond_gov);
+    if (has_capability("compounds", pc->capability)) {
+      dio_put_uint8(dout, eff->cond_govmod);
+    }
     dio_put_uint8(dout, eff->cond_adv);
     dio_put_uint8(dout, eff->cond_eff);
     dio_put_uint8(dout, eff->aff_unit);
@@ -2266,6 +2424,9 @@
     }
     dio_get_uint8(din, &(effect[inx].cond_bldg));
     dio_get_uint8(din, &(effect[inx].cond_gov));
+    if (has_capability("compounds", pc->capability)) {
+      dio_get_uint8(din, &(effect[inx].cond_govmod));
+    }
     dio_get_uint8(din, &(effect[inx].cond_adv));
     dio_get_uint8(din, (int *)&(effect[inx].cond_eff));
     dio_get_uint8(din, (int *)&(effect[inx].aff_unit));
@@ -2588,6 +2749,13 @@
   if (has_capability("impr_gen", pc->capability)) {
     dio_put_effects(&dout, pc, packet->effect);
   }
+  if (has_capability("compounds", pc->capability)) {
+    int i;
+    dio_put_uint16(&dout, packet->basetype);
+    for (i = 0; i < MAX_MODS; ++i) {
+      dio_put_uint16(&dout, packet->modifiers[i]);
+    }
+  }
 
   /* This must be last, so client can determine length: */
   if(packet->helptext) {
@@ -2688,6 +2856,15 @@
     packet->effect = fc_malloc(sizeof(struct impr_effect));
     packet->effect[0].type = EFT_LAST;
   }
+  if (has_capability("compounds", pc->capability)) {
+    int i;
+    dio_get_uint16(&din, &packet->basetype);
+    for (i = 0; i < MAX_MODS; ++i) {
+      dio_get_uint16(&din, &packet->modifiers[i]);
+    }
+  } else {
+    packet->basetype = G_MAGIC;
+  }
 
   len = dio_input_remaining(&din);
   if (len > 0) {
diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/packets.h freeciv-patched/common/packets.h
--- freeciv-cvs/common/packets.h	2003-06-30 12:33:43.000000000 +0100
+++ freeciv-patched/common/packets.h	2003-06-30 12:33:10.000000000 +0100
@@ -128,6 +128,9 @@
   PACKET_FREEZE_HINT,
   PACKET_THAW_HINT,
   PACKET_PING_INFO,
+  PACKET_RULESET_MODIFIER_TYPE,
+  PACKET_RULESET_MODIFIER,
+  PACKET_REQUEST_COMPOUND,
   PACKET_LAST  /* leave this last */
 };
 
@@ -572,6 +575,10 @@
   int num_unit_types;
   int num_impr_types;
   int num_tech_types;
+  int num_unit_modifier_types;
+  int num_unit_modifiers;
+  int num_gov_modifier_types;
+  int num_gov_modifiers;
   struct {
     int cathedral_plus;
     int cathedral_minus;
@@ -628,6 +635,9 @@
   int paratroopers_mr_sub;
   struct impr_effect *effect;
 
+  int basetype;
+  int modifiers[MAX_MODS];
+
   /* Following is a pointer to malloced memory; on the server, it
      points to putype->helptext, malloced earlier; on the client,
      it is malloced when packet received, and then putype->helptext
@@ -636,6 +646,33 @@
   char *helptext;
 };
 
+struct packet_ruleset_modifier_type {
+  bool is_gov;
+  int id;
+  char name[MAX_LEN_NAME];
+  int basetype;
+  int tech_requirement;
+  bool must_use;
+};
+
+struct packet_ruleset_modifier {
+  bool is_gov;
+  int id;
+  char name[MAX_LEN_NAME];
+  int type;
+  int tech_requirement;
+  char *action;
+  char *final_action;
+  struct impr_effect *effect;
+};
+
+struct packet_request_compound {
+  bool is_gov;
+  int basetype;
+  char name[MAX_LEN_NAME];
+  int modifiers[MAX_MODS];
+};
+
 struct packet_ruleset_tech {
   int id, req[2], root_req;	/* indices for advances[] */
   int flags;
@@ -729,6 +766,9 @@
   int empire_size_mod;
   int empire_size_inc;
   int rapture_size;
+
+  int basetype;
+  int modifiers[MAX_MODS];
       
   int unit_happy_cost_factor;
   int unit_shield_cost_factor;
@@ -1061,6 +1101,19 @@
 struct packet_ruleset_unit *
 receive_packet_ruleset_unit(struct connection *pc);
 
+int send_packet_ruleset_modifier_type(struct connection *pc,
+                const struct packet_ruleset_modifier_type *packet);
+struct packet_ruleset_modifier_type *
+receive_packet_ruleset_modifier_type(struct connection *pc);
+int send_packet_ruleset_modifier(struct connection *pc,
+                const struct packet_ruleset_modifier *packet);
+struct packet_ruleset_modifier *
+receive_packet_ruleset_modifier(struct connection *pc);
+int send_packet_request_compound(struct connection *pc,
+    				 struct packet_request_compound *req);
+struct packet_request_compound *
+receive_packet_request_compound(struct connection *pc);
+
 int send_packet_ruleset_tech(struct connection *pc,
 			     const struct packet_ruleset_tech *packet);
 struct packet_ruleset_tech *
diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/packets_lsend.c freeciv-patched/common/packets_lsend.c
--- freeciv-cvs/common/packets_lsend.c	2003-06-07 19:58:26.000000000 +0100
+++ freeciv-patched/common/packets_lsend.c	2003-06-30 12:33:10.000000000 +0100
@@ -239,6 +239,30 @@
   conn_list_iterate_end;
 }
 
+void lsend_packet_ruleset_modifier_type(struct conn_list *dest,
+                const struct packet_ruleset_modifier_type *packet)
+{
+  conn_list_iterate(*dest, pconn)
+    send_packet_ruleset_modifier_type(pconn, packet);
+  conn_list_iterate_end;
+}
+
+void lsend_packet_ruleset_modifier(struct conn_list *dest,
+                const struct packet_ruleset_modifier *packet)
+{
+  conn_list_iterate(*dest, pconn)
+    send_packet_ruleset_modifier(pconn, packet);
+  conn_list_iterate_end;
+}
+
+void lsend_packet_request_compound(struct conn_list *dest,
+    				 struct packet_request_compound *req)
+{
+  conn_list_iterate(*dest, pconn)
+    send_packet_request_compound(pconn, req);
+  conn_list_iterate_end;
+}
+
 void lsend_packet_ruleset_tech(struct conn_list *dest,
 			     const struct packet_ruleset_tech *packet)
 {
diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/packets_lsend.h freeciv-patched/common/packets_lsend.h
--- freeciv-cvs/common/packets_lsend.h	2003-06-07 19:58:26.000000000 +0100
+++ freeciv-patched/common/packets_lsend.h	2003-06-30 12:33:10.000000000 +0100
@@ -72,6 +72,12 @@
 				const struct packet_ruleset_control *packet);
 void lsend_packet_ruleset_unit(struct conn_list *dest,
 			     const struct packet_ruleset_unit *packet);
+void lsend_packet_ruleset_modifier_type(struct conn_list *dest,
+                const struct packet_ruleset_modifier_type *packet);
+void lsend_packet_ruleset_modifier(struct conn_list *dest,
+                const struct packet_ruleset_modifier *packet);
+void lsend_packet_request_compound(struct conn_list *dest,
+    				 struct packet_request_compound *req);
 void lsend_packet_ruleset_tech(struct conn_list *dest,
 			     const struct packet_ruleset_tech *packet);
 void lsend_packet_ruleset_building(struct conn_list *dest,
diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/shared.c freeciv-patched/common/shared.c
--- freeciv-cvs/common/shared.c	2003-06-07 19:58:26.000000000 +0100
+++ freeciv-patched/common/shared.c	2003-06-30 12:33:10.000000000 +0100
@@ -186,7 +186,7 @@
   Like strcspn but also handles quotes, i.e. *reject chars are 
   ignored if they are inside single or double quotes.
 ***************************************************************/
-static size_t my_strcspn(const char *s, const char *reject)
+size_t my_strcspn(const char *s, const char *reject)
 {
   bool in_single_quotes = FALSE, in_double_quotes = FALSE;
   size_t i, len = strlen(s);
diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/shared.h freeciv-patched/common/shared.h
--- freeciv-cvs/common/shared.h	2003-06-26 20:13:14.000000000 +0100
+++ freeciv-patched/common/shared.h	2003-06-30 12:33:10.000000000 +0100
@@ -136,6 +136,7 @@
 
 char *create_centered_string(const char *s);
 
+size_t my_strcspn(const char *s, const char *reject);
 char * get_option(const char *option_name,char **argv,int *i,int argc);
 bool is_option(const char *option_name,char *option);
 int get_tokens(const char *str, char **tokens, size_t num_tokens,
diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/unittype.c freeciv-patched/common/unittype.c
--- freeciv-cvs/common/unittype.c	2003-06-30 12:33:43.000000000 +0100
+++ freeciv-patched/common/unittype.c	2003-06-30 12:33:10.000000000 +0100
@@ -17,12 +17,16 @@
 
 #include <assert.h>
 #include <string.h>
+#include <ctype.h>
 
 #include "astring.h"
 #include "fcintl.h"
 #include "game.h"
 #include "government.h"
+#include "log.h"
 #include "mem.h"
+#include "member.h"
+#include "modifier.h"
 #include "player.h"
 #include "shared.h"
 #include "support.h"
@@ -30,7 +34,30 @@
 
 #include "unittype.h"
 
+struct atoken {
+  enum {
+    NULL_TOKEN,
+    OP_TOKEN,
+    EQUALS_TOKEN,
+    LEFTPAREN_TOKEN,
+    RIGHTPAREN_TOKEN,
+    IF_TOKEN,
+    STRING_TOKEN,
+    IDENT_TOKEN
+  } type;
+  char *str;
+  char optype;
+  bool has_equals;
+};
+
+char *action_getline(char **iter, struct astring *str);
+void action_doline(void *structpt, struct members *memberlist, char *line);
+void action_parsetokens(void *structpt, struct members *memberlist,
+    			struct atoken *tokens, int numtokens);
+
 struct unit_type unit_types[U_LAST];
+struct modifier_type unit_modifier_types[MAX_MODS];
+struct modifier *unit_modifiers = NULL;
 /* the unit_types array is now setup in:
    server/ruleset.c (for the server)
    client/packhand.c (for the client)
@@ -52,9 +79,11 @@
   "DefendOk", "DefendGood", "AttackFast", "AttackStrong",
   "Ferryboat", "Barbarian", "BarbarianTech", "BarbarianBoat",
   "BarbarianBuild", "BarbarianBuildTech", "BarbarianLeader",
-  "BarbarianSea", "BarbarianSeaTech"
+  "BarbarianSea", "BarbarianSeaTech", "CanModify"
 };
 
+static void ApplyUnitModAction(struct unit_type *u, char *action);
+
 /**************************************************************************
 Returns 1 if the unit_type "exists" in this game, 0 otherwise.
 A unit_type doesn't exist if one of:
@@ -379,6 +408,27 @@
 }
 
 /**************************************************************************
+  Returns the unit type which is built from the given base type and unit
+  modifiers, or U_LAST if none match.
+**************************************************************************/
+Unit_Type_id find_unit_type_by_modifiers(Unit_Type_id basetype, int *modifiers)
+{
+  unit_type_iterate(uid) {
+    int i;
+    if (unit_types[uid].basetype == basetype) {
+      for (i = 0; i < game.num_unit_modifier_types; ++i) {
+	if (unit_types[uid].modifiers[i] != modifiers[i]) {
+	  break;
+	} else if (i == game.num_unit_modifier_types - 1) {
+	  return uid;
+	}
+      }
+    }
+  } unit_type_iterate_end;
+  return U_LAST;
+}
+
+/**************************************************************************
   Convert unit_move_type names to enum; case insensitive;
   returns 0 if can't match.
 **************************************************************************/
@@ -449,17 +499,31 @@
 **************************************************************************/
 bool can_player_build_unit_direct(struct player *p, Unit_Type_id id)
 {
+  struct unit_type *u;
   Impr_Type_id impr_req;
   Tech_Type_id tech_req;
 
   if (!unit_type_exists(id))
     return FALSE;
+  u = &unit_types[id];
   if (unit_type_flag(id, F_FANATIC)
       && !government_has_flag(get_gov_pplayer(p), G_FANATIC_TROOPS))
     return FALSE;
-  if (get_invention(p,unit_types[id].tech_requirement)!=TECH_KNOWN)
+  if (get_invention(p, u->tech_requirement) != TECH_KNOWN)
     return FALSE;
 
+  /* Check tech requirements of unit modifiers if applicable */
+  if (u->basetype != U_LAST) {
+    int i;
+    for (i = 0; i < game.num_unit_modifier_types; ++i) {
+      if (u->modifiers[i] < U_LAST
+	  && get_invention(p, unit_modifiers[u->modifiers[i]].tech_requirement)
+			   != TECH_KNOWN) {
+	return FALSE;
+      }
+    }
+  }
+
   /* If the unit has a building requirement, we check to see if the player
    * can build that building.  Note that individual cities may not have
    * that building, so they still may not be able to build the unit. */
@@ -654,3 +718,714 @@
     unit_type_free(i);
   } unit_type_iterate_end;
 }
+
+/**************************************************************************
+  Convert unit_modifier_type names to int; case insensitive;
+  returns -1 if can't match.
+**************************************************************************/
+int unit_modifier_type_from_str(const char *s)
+{
+  return modifier_type_from_str(s, unit_modifier_types,
+      				game.num_unit_modifier_types);
+}
+
+/**************************************************************************
+Does a linear search of unit_modifiers[].name
+Returns -1 if none match.
+**************************************************************************/
+int find_unit_modifier_by_name(const char *s)
+{
+  return find_modifier_by_name(s, unit_modifiers, game.num_unit_modifiers);
+}
+
+static void ensure_unique_unit_name(struct unit_type *utype)
+{
+  char *tmpname, *tmporig;
+  int mark = 2;
+  int maxlen = sizeof(utype->name) - 10; /* To allow for longer translations */
+  int maxorig = sizeof(utype->name_orig) - 6;
+
+  assert(maxlen > 0 && maxorig > 0);
+  tmpname = mystrdup(utype->name);
+  tmporig = mystrdup(utype->name_orig);
+
+  /* Make sure that the suffix won't be truncated each time */
+  if (strlen(tmpname) > maxlen) {
+    tmpname[maxlen - 1] = '\0';
+  }
+  if (strlen(tmporig) > maxorig) {
+    tmporig[maxorig - 1] = '\0';
+  }
+
+  while (find_unit_type_by_name(utype->name) != U_LAST) {
+    my_snprintf(utype->name, sizeof(utype->name), _("%s Mk%d"), tmpname,
+		mark);
+    my_snprintf(utype->name_orig, sizeof(utype->name_orig), "%s Mk%d",
+		tmporig, mark++);
+    assert(mark < 100);
+  }
+  free(tmpname);
+  free(tmporig);
+}
+
+Unit_Type_id form_compound_unit(const char *name,
+    				const Unit_Type_id basetype,
+				const int modifiers[MAX_MODS])
+{
+  Unit_Type_id id;
+  struct unit_type *u, *base;
+  struct modifier *mod;
+  int i;
+
+  id = game.num_unit_types;
+  if (id >= U_LAST) {
+    freelog(LOG_FATAL, "form_compound_unit: out of unit types");
+    exit(EXIT_FAILURE);
+  }
+  u = &unit_types[id];
+  base = &unit_types[basetype];
+  utype_copy_members(u, base);
+  u->basetype = basetype;
+  u->effect = NULL;
+  append_effects(&u->effect, base->effect);
+  for (i = 0; i < game.num_unit_modifier_types; ++i) {
+    u->modifiers[i] = modifiers[i];
+  }
+  u->tech_requirement = A_NONE;
+  u->obsoleted_by = -1;
+  u->name_orig[0] = '\0';
+  for (i = 0; i < game.num_unit_modifier_types; ++i) {
+    if (u->modifiers[i] < U_LAST) {
+      mod = &unit_modifiers[u->modifiers[i]];
+      ApplyUnitModAction(u, mod->action);
+    }
+  }
+  for (i = 0; i < game.num_unit_modifier_types; ++i) {
+    if (u->modifiers[i] < U_LAST) {
+      mod = &unit_modifiers[u->modifiers[i]];
+      ApplyUnitModAction(u, mod->final_action);
+      append_effects(&u->effect, mod->effect);
+    }
+  }
+  /* Overwrite the auto-generated name if a name is supplied */
+  if (name && name[0]) {
+    sz_strlcpy(u->name_orig, name);
+  }
+  sz_strlcpy(u->name, _(u->name_orig));
+  ensure_unique_unit_name(u);
+  game.num_unit_types++;
+  return id;
+}
+
+static void read_moverate(void *structpt, unsigned long offset,
+                          const size_t size, struct section_file *file,
+                          const char *sec, const char *membername)
+{
+  int *member = FC_STRUCT_MEMBER_P(int, structpt, offset);
+
+  *member = secfile_lookup_int(file, "%s.%s", sec, membername)
+            * SINGLE_MOVE;
+}
+
+static void set_moverate(void *structpt, unsigned long offset,
+                         const size_t size, const char *val)
+{
+  int *member = FC_STRUCT_MEMBER_P(int, structpt, offset);
+
+  *member = SINGLE_MOVE * atoi(val);
+}
+
+static void get_moverate(const void *structpt, unsigned long offset,
+    			 struct memberdata *data)
+{
+  int *member = FC_STRUCT_MEMBER_P(int, structpt, offset);
+
+  my_snprintf(data->str, sizeof(data->str), "%d", *member / SINGLE_MOVE);
+}
+
+static void set_unit(void *structpt, unsigned long offset, const size_t size,
+                     const char *val)
+{
+}
+
+static void get_unit(const void *structpt, unsigned long offset,
+    		     struct memberdata *data)
+{
+}
+
+static void read_movetype(void *structpt, unsigned long offset,
+                          const size_t size, struct section_file *file,
+                          const char *sec, const char *membername)
+{
+  char *str;
+  enum unit_move_type *member = FC_STRUCT_MEMBER_P(enum unit_move_type,
+                                                   structpt, offset);
+
+  str = secfile_lookup_str(file, "%s.%s", sec, membername);
+  if ((*member = unit_move_type_from_str(str)) == 0) {
+    freelog(LOG_FATAL, "for unit_type \"%s\": bad move_type %s",
+	    ((struct unit_type *)structpt)->name, str);
+    exit(EXIT_FAILURE);
+  }
+//printf("Read %s -> (movetype) %s\n", membername, str);
+}
+
+static void set_movetype(void *structpt, unsigned long offset,
+                         const size_t size, const char *val)
+{
+  enum unit_move_type *member = FC_STRUCT_MEMBER_P(enum unit_move_type,
+                                                   structpt, offset);
+
+  if ((*member = unit_move_type_from_str(val)) == 0) {
+    freelog(LOG_FATAL, "bad move type %s", val);
+  }
+}
+
+static void get_movetype(const void *structpt, unsigned long offset,
+    			 struct memberdata *data)
+{
+  enum unit_move_type *member = FC_STRUCT_MEMBER_P(enum unit_move_type,
+                                                   structpt, offset);
+
+  sz_strlcpy(data->str, move_type_names[*member - LAND_MOVING]);
+}
+
+static void read_flags(void *structpt, unsigned long offset, const size_t size,
+                       struct section_file *file,
+                       const char *sec, const char *membername)
+{
+  char **slist, *str;
+  int nval, i;
+  enum unit_flag_id flag;
+  bv_flags *member = FC_STRUCT_MEMBER_P(bv_flags, structpt, offset);
+
+  BV_CLR_ALL(*member);
+  slist = secfile_lookup_str_vec(file, &nval, "%s.%s", sec, membername);
+  for (i = 0; i < nval; ++i) {
+    str = slist[i];
+    if (strcmp(str, "") == 0) {
+      continue;
+    }
+    if (strcmp(str, "Submarine")==0) {
+      /* Backwards compatibility */
+      freelog(LOG_NORMAL, "Old-style \"Submarine\" flag (ok)");
+      BV_SET(*member, F_NO_LAND_ATTACK);
+      BV_SET(*member, F_MISSILE_CARRIER);
+      flag = F_PARTIAL_INVIS;
+    } else {
+      flag = unit_flag_from_str(str);
+    }
+    if (flag == F_MAX) {
+      freelog(LOG_ERROR, "for unit_type \"%s\": bad flag name \"%s\"",
+	      ((struct unit_type *)structpt)->name, str);
+    } else {
+      BV_SET(*member, flag);
+    }
+  }
+  free(slist);
+//printf("Read %s -> (flag) %u\n", membername, *member);
+}
+
+#define FLAGS_MAX_TOKENS 80
+static void set_flags(void *structpt, unsigned long offset, const size_t size,
+                      const char *val)
+{
+  bv_flags *member = FC_STRUCT_MEMBER_P(bv_flags, structpt, offset);
+  char *tokens[FLAGS_MAX_TOKENS];
+  int numtokens, i;
+  enum unit_flag_id flag;
+
+  BV_CLR_ALL(*member);
+  numtokens = get_tokens(val, tokens, FLAGS_MAX_TOKENS, " ,");
+  for (i = 0; i < numtokens; ++i) {
+    flag = unit_flag_from_str(tokens[i]);
+    if (flag == F_MAX) {
+      freelog(LOG_NORMAL, "bad flag name \"%s\" - ignoring", tokens[i]);
+    } else {
+      BV_SET(*member, flag);
+    }
+    free(tokens[i]);
+  }
+}
+
+static void get_flags(const void *structpt, unsigned long offset,
+    		      struct memberdata *data)
+{
+  enum unit_flag_id i;
+  bool first = TRUE;
+  bv_flags *member = FC_STRUCT_MEMBER_P(bv_flags, structpt, offset);
+
+  data->str[0] = '\0';
+  for (i = 0; i < game.num_unit_flags; ++i) {
+    if (BV_ISSET(*member, i)) {
+      if (!first) {
+	sz_strlcat(data->str, ", ");
+      } else {
+	first = FALSE;
+      }
+      sz_strlcat(data->str, "'");
+      sz_strlcat(data->str, flag_names[i]);
+      sz_strlcat(data->str, "'");
+    }
+  }
+
+  freelog(LOG_DEBUG, "(flag) = %s\n", data->str);
+}
+
+static void binary_roleflags(const char *arg1, const char *arg2, const char op,
+			     struct memberdata *result)
+{
+  if (op == '+') {
+    sz_strlcpy(result->str, arg1);
+    if (arg1[0] && arg2[0]) {
+      sz_strlcat(result->str, ", ");
+    }
+    sz_strlcat(result->str, arg2);
+  } else if (op == '-') {
+    freelog(LOG_NORMAL, "Fixme...");
+  }
+}
+
+static void read_roles(void *structpt, unsigned long offset, const size_t size,
+                       struct section_file *file,
+                       const char *sec, const char *membername)
+{
+  char **slist, *str;
+  int nval, i;
+  enum unit_role_id role;
+  bv_roles *member = FC_STRUCT_MEMBER_P(bv_roles, structpt, offset);
+
+  BV_CLR_ALL(*member);
+  slist = secfile_lookup_str_vec(file, &nval, "%s.%s", sec, membername);
+  for (i = 0; i < nval; ++i) {
+    str = slist[i];
+    if (strcmp(str, "") == 0) {
+      continue;
+    }
+    role = unit_role_from_str(str);
+    if (role == L_LAST) {
+      freelog(LOG_ERROR, "for unit_type \"%s\": bad role name \"%s\"",
+	      ((struct unit_type *)structpt)->name, str);
+    } else {
+      BV_SET(*member, role - L_FIRST);
+    }
+  }
+  free(slist);
+//printf("Read %s -> (role) %u\n", membername, *member);
+}
+
+static void set_roles(void *structpt, unsigned long offset, const size_t size,
+                      const char *val)
+{
+}
+
+static void get_roles(const void *structpt, unsigned long offset,
+    		      struct memberdata *data)
+{
+  enum unit_role_id i;
+  bool first = TRUE;
+  bv_roles *member = FC_STRUCT_MEMBER_P(bv_roles, structpt, offset);
+
+  data->str[0] = '\0';
+  for (i = L_FIRST; i < L_LAST; ++i) {
+    if (BV_ISSET(*member, (i - L_FIRST))) {
+      if (!first) {
+	sz_strlcat(data->str, ", ");
+      } else {
+	first = FALSE;
+      }
+      sz_strlcat(data->str, "'");
+      sz_strlcat(data->str, role_names[i - L_FIRST]);
+      sz_strlcat(data->str, "'");
+    }
+  }
+
+  freelog(LOG_DEBUG, "(role) = %s\n", data->str);
+}
+
+#define FC_UTYPE_OFFSET(member) \
+    FC_STRUCT_OFFSET(struct unit_type, member)
+#define FC_UTYPE_SIZE(member) \
+    FC_MEMBER_SIZE(struct unit_type, member)
+
+static struct members utype_members[] = {
+  { "name",
+    FC_UTYPE_OFFSET(name_orig),
+    FC_UTYPE_SIZE(name_orig),
+    NULL, set_string, get_string, binary_string },
+  { "tech_req", FC_UTYPE_OFFSET(tech_requirement),
+    FC_UTYPE_SIZE(tech_requirement), NULL, set_tech, get_tech, NULL },
+  { "impr_req", FC_UTYPE_OFFSET(impr_requirement),
+    FC_UTYPE_SIZE(impr_requirement), NULL, set_int, get_int, NULL },
+  { "obsolete_by", FC_UTYPE_OFFSET(obsoleted_by), FC_UTYPE_SIZE(obsoleted_by),
+    NULL, set_unit, get_unit, NULL },
+  { "move_type", FC_UTYPE_OFFSET(move_type), FC_UTYPE_SIZE(move_type),
+    read_movetype, set_movetype, get_movetype, binary_int },
+  { "sound_move", FC_UTYPE_OFFSET(sound_move), FC_UTYPE_SIZE(sound_move),
+    read_sound, set_string, get_string, binary_string },
+  { "sound_move_alt", FC_UTYPE_OFFSET(sound_move_alt),
+    FC_UTYPE_SIZE(sound_move_alt), read_sound, set_string, get_string,
+    binary_string },
+  { "sound_fight", FC_UTYPE_OFFSET(sound_fight), FC_UTYPE_SIZE(sound_fight),
+    read_sound, set_string, get_string, binary_string },
+  { "sound_fight_alt", FC_UTYPE_OFFSET(sound_fight_alt),
+    FC_UTYPE_SIZE(sound_fight_alt), read_sound, set_string, get_string,
+    binary_string },
+  { "graphic", FC_UTYPE_OFFSET(graphic_str), FC_UTYPE_SIZE(graphic_str),
+    read_string, set_string, get_string, binary_string },
+  { "graphic_alt", FC_UTYPE_OFFSET(graphic_alt), FC_UTYPE_SIZE(graphic_alt),
+    read_string, set_string, get_string, binary_string },
+  { "build_cost", FC_UTYPE_OFFSET(build_cost), FC_UTYPE_SIZE(build_cost),
+    read_int, set_int, get_int, binary_int },
+  { "pop_cost", FC_UTYPE_OFFSET(pop_cost), FC_UTYPE_SIZE(pop_cost),
+    read_int, set_int, get_int, binary_int },
+  { "attack", FC_UTYPE_OFFSET(attack_strength),
+    FC_UTYPE_SIZE(attack_strength), read_int, set_int, get_int, binary_int },
+  { "defense", FC_UTYPE_OFFSET(defense_strength),
+    FC_UTYPE_SIZE(defense_strength), read_int, set_int, get_int, binary_int },
+  { "move_rate", FC_UTYPE_OFFSET(move_rate), FC_UTYPE_SIZE(move_rate),
+    read_moverate, set_moverate, get_moverate, binary_int },
+  { "vision_range", FC_UTYPE_OFFSET(vision_range), FC_UTYPE_SIZE(vision_range),
+    read_int, set_int, get_int, binary_int },
+  { "transport_cap", FC_UTYPE_OFFSET(transport_capacity),
+    FC_UTYPE_SIZE(transport_capacity), read_int, set_int, get_int,
+    binary_int },
+  { "hitpoints", FC_UTYPE_OFFSET(hp), FC_UTYPE_SIZE(hp),
+    read_int, set_int, get_int, binary_int },
+  { "firepower", FC_UTYPE_OFFSET(firepower), FC_UTYPE_SIZE(firepower),
+    read_int, set_int, get_int, binary_int },
+  { "fuel", FC_UTYPE_OFFSET(fuel), FC_UTYPE_SIZE(fuel),
+    read_int, set_int, get_int, binary_int },
+  { "uk_happy", FC_UTYPE_OFFSET(happy_cost), FC_UTYPE_SIZE(happy_cost),
+    read_int, set_int, get_int, binary_int },
+  { "uk_shield", FC_UTYPE_OFFSET(shield_cost), FC_UTYPE_SIZE(shield_cost),
+    read_int, set_int, get_int, binary_int },
+  { "uk_food", FC_UTYPE_OFFSET(food_cost), FC_UTYPE_SIZE(food_cost),
+    read_int, set_int, get_int, binary_int },
+  { "uk_gold", FC_UTYPE_OFFSET(gold_cost), FC_UTYPE_SIZE(gold_cost),
+    read_int, set_int, get_int, binary_int },
+  { "flags", FC_UTYPE_OFFSET(flags), FC_UTYPE_SIZE(flags),
+    read_flags, set_flags, get_flags, binary_roleflags },
+  { "roles", FC_UTYPE_OFFSET(roles), FC_UTYPE_SIZE(roles),
+    read_roles, set_roles, get_roles, binary_roleflags },
+  { "paratroopers_range", FC_UTYPE_OFFSET(paratroopers_range),
+    FC_UTYPE_SIZE(paratroopers_range), NULL, set_int, get_int, binary_int },
+  { "paratroopers_mr_req", FC_UTYPE_OFFSET(paratroopers_mr_req),
+    FC_UTYPE_SIZE(paratroopers_mr_req), NULL, set_moverate, get_moverate,
+    binary_int },
+  { "paratroopers_mr_sub", FC_UTYPE_OFFSET(paratroopers_mr_sub),
+    FC_UTYPE_SIZE(paratroopers_mr_sub), NULL, set_moverate, get_moverate,
+    binary_int },
+  { NULL, 0, 0, NULL, NULL, NULL, NULL }
+};
+
+void utype_copy_members(struct unit_type *dest, const struct unit_type *src)
+{
+  copy_members((void *)dest, (const void *)src, utype_members);
+}
+
+void utype_read_members(struct unit_type *utype,
+                        struct section_file *file, const char *sec)
+{
+  read_members((void *)utype, utype_members, file, sec);
+}
+
+void utype_read_member(struct unit_type *utype, const char *membername,
+    		       struct section_file *file, const char *sec)
+{
+  read_member((void *)utype, utype_members, membername, file, sec);
+}
+
+void utype_set_member(struct unit_type *utype, const char *membername,
+                      const char *val)
+{
+  set_member((void *)utype, utype_members, membername, val);
+}
+
+void utype_get_member(struct unit_type *utype, const char *membername,
+    		      struct memberdata *data)
+{
+  get_member((void *)utype, utype_members, membername, data);
+}
+
+char *action_getline(char **iter, struct astring *str)
+{
+  int linelen;
+  char *startline = *iter;
+
+  startline += strspn(startline, "\r\n\t ");
+  linelen = my_strcspn(startline, "\r\n");
+  if (linelen) {
+    astr_minsize(str, linelen + 1);
+    mystrlcpy(str->str, startline, linelen + 1); /* to add the '/0' */
+    *iter = startline + linelen;
+  }
+
+  return startline[0] ? str->str : NULL;
+}
+
+static void get_str_data(void *structpt, struct members *memberlist,
+    			 struct atoken *token, struct memberdata *result,
+                         char **identname)
+{
+  if (token->type == STRING_TOKEN) {
+    sz_strlcpy(result->str, token->str);
+  } else if (token->type == IDENT_TOKEN) {
+    get_member(structpt, memberlist, token->str, result);
+    if (identname) {
+      *identname = token->str;
+    }
+  } else {
+    freelog(LOG_NORMAL, "parse error");
+  }
+}
+
+static int find_operator(struct atoken *tokens, int numtokens, char *ops)
+{
+  int i, numparen = 0;
+
+  for (i = 0; i < numtokens; ++i) {
+    switch(tokens[i].type) {
+    case LEFTPAREN_TOKEN:
+      numparen++;
+      break;
+    case RIGHTPAREN_TOKEN:
+      numparen--;
+      break;
+    case OP_TOKEN:
+      if (numparen == 0 && (!ops || strchr(ops, tokens[i].optype))) {
+	return i;
+      }
+      break;
+    default:
+      break;
+    }
+  }
+  if (numparen != 0) {
+    freelog(LOG_NORMAL, "Unbalanced parentheses");
+  }
+  return -1;
+}
+
+static void eval_expr(void *structpt, struct members *memberlist,
+    		      struct atoken *tokens, int numtokens,
+    		      struct atoken *result, char **identname)
+{
+  result->type = STRING_TOKEN;
+  result->str = NULL;
+
+  /* Strip surrounding parentheses */
+  while (numtokens >= 2 && tokens[0].type == LEFTPAREN_TOKEN
+         && tokens[numtokens - 1].type == RIGHTPAREN_TOKEN) {
+    numtokens -= 2;
+    tokens++;
+  }
+
+  if (numtokens == 1) {
+    struct memberdata rhs;
+    size_t len;
+
+    get_str_data(structpt, memberlist, &tokens[0], &rhs, identname);
+    len = strlen(rhs.str) + 1;
+    result->str = fc_malloc(len);
+    mystrlcpy(result->str, rhs.str, len);
+  } else {
+    struct atoken lhs, rhs;
+    struct memberdata member;
+    size_t len;
+    int oppos;
+
+    oppos = find_operator(tokens, numtokens, "+-");
+    if (oppos < 0) {
+      oppos = find_operator(tokens, numtokens, "*/");
+    }
+    if (oppos < 0) {
+      oppos = find_operator(tokens, numtokens, NULL);
+    }
+    if (oppos <= 0 || oppos >= numtokens) {
+      freelog(LOG_NORMAL, "parse error");
+    } else {
+      char *membname = NULL;
+
+      eval_expr(structpt, memberlist, &tokens[0], oppos, &lhs, &membname);
+      eval_expr(structpt, memberlist, &tokens[oppos + 1],
+	  	numtokens - oppos - 1, &rhs, &membname);
+
+      freelog(LOG_DEBUG, "Binary op %c on %s and %s", tokens[oppos].optype,
+              lhs.str, rhs.str);
+
+      if (membname) {
+        binary_member(structpt, memberlist, membname,
+          	      lhs.str, rhs.str, tokens[oppos].optype, &member);
+	if (identname) {
+	  *identname = membname;
+	}
+      } else if (isdigit(lhs.str[0]) || isdigit(rhs.str[0])) {
+        binary_int(lhs.str, rhs.str, tokens[oppos].optype, &member);
+      } else {
+        binary_string(lhs.str, rhs.str, tokens[oppos].optype, &member);
+      }
+      len = strlen(member.str) + 1;
+      result->str = fc_malloc(len);
+      mystrlcpy(result->str, member.str, len);
+    }
+  }
+}
+
+void action_parsetokens(void *structpt, struct members *memberlist,
+    			struct atoken *tokens, int numtokens)
+{
+  struct memberdata data;
+
+  if (numtokens > 4 && tokens[0].type == IF_TOKEN
+      && tokens[2].type == EQUALS_TOKEN) {
+    get_member(structpt, memberlist, tokens[1].str, &data);
+    if (strcmp(data.str, tokens[3].str) == 0) {
+      freelog(LOG_DEBUG, "if %s = %s: TRUE", tokens[1].str, tokens[3].str);
+      action_parsetokens(structpt, memberlist, &tokens[4], numtokens - 4);
+    } else {
+      freelog(LOG_DEBUG, "if %s = %s: FALSE", tokens[1].str, tokens[3].str);
+    }
+  } else if (numtokens >= 3 && tokens[1].type == EQUALS_TOKEN
+	     && tokens[0].type == IDENT_TOKEN) {
+    struct atoken result;
+
+    eval_expr(structpt, memberlist, &tokens[2], numtokens - 2, &result, NULL);
+    freelog(LOG_DEBUG, "%s set to %s", tokens[0].str, result.str);
+    set_member(structpt, memberlist, tokens[0].str, result.str);
+  } else if (numtokens >= 3 && tokens[1].type == OP_TOKEN
+             && tokens[1].has_equals && tokens[0].type == IDENT_TOKEN) {
+    struct atoken rhs;
+    struct memberdata result;
+
+    get_member(structpt, memberlist, tokens[0].str, &data);
+    eval_expr(structpt, memberlist, &tokens[2], numtokens - 2, &rhs, NULL);
+    binary_member(structpt, memberlist, tokens[0].str, data.str, rhs.str,
+        	  tokens[1].optype, &result);
+    freelog(LOG_DEBUG, "%s %c= %s -> %s", tokens[0].str, tokens[1].optype,
+            rhs.str, result.str);
+    set_member(structpt, memberlist, tokens[0].str, result.str);
+  } else {
+    freelog(LOG_NORMAL, "Parse error");
+  }
+}
+
+static void add_op_token(const char **strpt, struct atoken *token)
+{
+  const char *str = *strpt;
+
+  switch(str[0]) {
+  case '(':
+    token->type = LEFTPAREN_TOKEN; break;
+  case ')':
+    token->type = RIGHTPAREN_TOKEN; break;
+  case '=':
+    token->type = EQUALS_TOKEN; break;
+  default:
+    token->type = OP_TOKEN;
+    token->optype = str[0];
+    token->has_equals = FALSE;
+    if (str[1] == '=') {
+      token->has_equals = TRUE;
+      (*strpt)++;
+    }
+    break;
+  }
+  (*strpt)++;
+}
+
+static int action_tokens(const char *str, struct atoken *tokens,
+    			 size_t num_tokens)
+{
+  int token;
+
+  for (token = 0; token < num_tokens; ++token) {
+    tokens[token].str = NULL;
+    tokens[token].type = NULL_TOKEN;
+  }
+
+  token = 0;
+  while (token < num_tokens && str[0] != '\0') {
+    size_t len;
+    const char *stpt;
+    bool strlist;
+
+    switch (str[0]) {
+    case '+': case '-': case '*': case '/':
+    case '(': case ')': case '=':
+      add_op_token(&str, &tokens[token]);
+      token++;
+      break;
+    default:
+      strlist = FALSE;
+      str += strspn(str, "\t, ");
+      stpt = str;
+      len = my_strcspn(str, "\t -+=*/()");
+      while (len > 0 && str[len - 1] == ',' && (strchr("\t ", str[len]))) {
+        strlist = TRUE;
+        str += len;
+        str += strspn(str, "\t, ");
+        len = my_strcspn(str, "\t -+=*/()");
+      }
+      str += len;
+      if (str > stpt) {
+        len = str - stpt;
+        if (strlist) {
+          tokens[token].type = STRING_TOKEN;
+        } else if ((stpt[0] == '\'' && str[-1] == '\'')
+            	   || (stpt[0] == '\"' && str[-1] == '\"')) {
+          len -= 2;
+          stpt++;
+          tokens[token].type = STRING_TOKEN;
+        } else if (!isalpha(stpt[0])) {
+          tokens[token].type = STRING_TOKEN;
+        } else {
+          tokens[token].type = IDENT_TOKEN;
+        }
+        if (strncmp(stpt, "if", len) == 0) {
+          tokens[token].type = IF_TOKEN;
+        } else {
+          tokens[token].str = fc_malloc(len + 1);
+          mystrlcpy(tokens[token].str, stpt, len + 1);
+        }
+        token++;
+      }
+      break;
+    }
+  }
+  return token;
+}
+
+#define ACTION_MAX_TOKENS 80
+void action_doline(void *structpt, struct members *memberlist, char *line)
+{
+  struct atoken atokens[ACTION_MAX_TOKENS];
+  int numtokens, i;
+
+  numtokens = action_tokens(line, atokens, ACTION_MAX_TOKENS);
+  freelog(LOG_DEBUG, "Attempting to apply action line %s", line);
+  action_parsetokens(structpt, memberlist, atokens, numtokens);
+  for (i = 0; i < numtokens; ++i) {
+    free(atokens[i].str);
+  }
+}
+
+void apply_action(void *structpt, struct members *memberlist, char *action)
+{
+  char *iter, *line;
+  struct astring str;
+
+  assert(action);
+  astr_init(&str);
+  iter = action;
+  while ((line = action_getline(&iter, &str)) != NULL) {
+    action_doline(structpt, memberlist, line);
+  }
+  astr_free(&str);
+}
+
+static void ApplyUnitModAction(struct unit_type *u, char *action)
+{
+  apply_action((void *)u, utype_members, action);
+}
+
diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/unittype.h freeciv-patched/common/unittype.h
--- freeciv-cvs/common/unittype.h	2003-06-30 12:33:43.000000000 +0100
+++ freeciv-patched/common/unittype.h	2003-06-30 12:33:10.000000000 +0100
@@ -14,6 +14,7 @@
 #define FC__UNITTYPE_H
 
 #include "shared.h"
+#include "modifier.h"
 
 struct player;
 struct city;
@@ -21,6 +22,9 @@
 struct Sprite;			/* opaque; client-gui specific */
 struct unit;
 struct impr_effect;
+struct section_file;
+struct memberdata;
+struct members;
 
 typedef int Unit_Type_id;
 /*
@@ -132,6 +136,7 @@
   L_BARBARIAN_LEADER,   /* barbarian leader */
   L_BARBARIAN_SEA,      /* sea raider unit */
   L_BARBARIAN_SEA_TECH, /* sea raider unit, global tech required */
+  L_CANMODIFY,          /* we can apply unit modifiers to this unit type */
   L_LAST
 };
 #define L_MAX 64
@@ -179,6 +184,9 @@
 
   struct impr_effect *effect; /* list; .type==EFT_LAST terminated */
 
+  Unit_Type_id basetype;  /* if !U_LAST, unit modifiers are applied */
+  int modifiers[MAX_MODS];
+
   char *helptext;
 };
 
@@ -188,6 +196,8 @@
 };
 
 extern struct unit_type unit_types[U_LAST];
+extern struct modifier_type unit_modifier_types[MAX_MODS];
+extern struct modifier *unit_modifiers;
 
 bool unit_type_exists(Unit_Type_id id);
 struct unit_type *get_unit_type(Unit_Type_id id);
@@ -224,6 +234,7 @@
 
 Unit_Type_id find_unit_type_by_name(const char *s);
 
+Unit_Type_id find_unit_type_by_modifiers(Unit_Type_id basetype, int *modifiers);
 enum unit_move_type unit_move_type_from_str(const char *s);
 enum unit_flag_id unit_flag_from_str(const char *s);
 void unit_flag_set_name(enum unit_flag_id id, const char *s);
@@ -250,4 +261,22 @@
   }                                                                           \
 }
 
+int unit_modifier_type_from_str(const char *s);
+int find_unit_modifier_by_name(const char *s);
+
+Unit_Type_id form_compound_unit(const char *name,
+    				const Unit_Type_id basetype,
+				const int modifiers[MAX_MODS]);
+
+void utype_copy_members(struct unit_type *dest, const struct unit_type *src);
+void utype_read_members(struct unit_type *utype,
+                        struct section_file *file, const char *sec);
+void utype_read_member(struct unit_type *utype, const char *membername,
+    		       struct section_file *file, const char *sec);
+void utype_set_member(struct unit_type *utype, const char *membername,
+                      const char *val);
+void utype_get_member(struct unit_type *utype, const char *membername,
+    		      struct memberdata *data);
+void apply_action(void *structpt, struct members *memberlist, char *action);
+
 #endif  /* FC__UNITTYPE_H */
diff -Nur -Xfreecivdiff.ignore freeciv-cvs/data/default/governments.ruleset freeciv-patched/data/default/governments.ruleset
--- freeciv-cvs/data/default/governments.ruleset	2003-06-30 12:33:48.000000000 +0100
+++ freeciv-patched/data/default/governments.ruleset	2003-06-30 12:33:10.000000000 +0100
@@ -154,13 +154,19 @@
 waste_extra_distance       = 0 
 waste_max_distance_cap     = 36
 
-production_trade_bonus    = 0, 0
-production_shield_bonus   = 0, 0
-production_food_bonus     = 0, 0
-
-production_trade_penalty  = 2, 0
-production_shield_penalty = 2, 0
-production_food_penalty   = 2, 0
+trade_bonus    = 0
+shield_bonus   = 0
+food_bonus     = 0
+celeb_trade_bonus    = 0
+celeb_shield_bonus   = 0
+celeb_food_bonus     = 0
+
+trade_penalty  = 2
+shield_penalty = 2
+food_penalty   = 2
+celeb_trade_penalty  = 0
+celeb_shield_penalty = 0
+celeb_food_penalty   = 0
 
 ruler_male_title = _("Mr.")
 ruler_female_title = _("Ms.")
@@ -232,13 +238,19 @@
 waste_extra_distance       = 0 
 waste_max_distance_cap     = 36
 
-production_trade_bonus    = 0, 0
-production_shield_bonus   = 0, 0
-production_food_bonus     = 0, 0
-
-production_trade_penalty  = 2, 0
-production_shield_penalty = 2, 0
-production_food_penalty   = 2, 0
+trade_bonus    = 0
+shield_bonus   = 0
+food_bonus     = 0
+celeb_trade_bonus    = 0
+celeb_shield_bonus   = 0
+celeb_food_bonus     = 0
+
+trade_penalty  = 2
+shield_penalty = 2
+food_penalty   = 2
+celeb_trade_penalty  = 0
+celeb_shield_penalty = 0
+celeb_food_penalty   = 0
 
 ruler_male_title = _("Emperor")
 ruler_female_title = _("Empress")
@@ -311,13 +323,19 @@
 waste_extra_distance       = 0 
 waste_max_distance_cap     = 36
 
-production_trade_bonus    = 0, 1
-production_shield_bonus   = 0, 0
-production_food_bonus     = 0, 0
-
-production_trade_penalty  = 0, 0
-production_shield_penalty = 0, 0
-production_food_penalty   = 0, 0
+trade_bonus    = 0
+shield_bonus   = 0
+food_bonus     = 0
+celeb_trade_bonus    = 1
+celeb_shield_bonus   = 0
+celeb_food_bonus     = 0
+
+trade_penalty  = 0
+shield_penalty = 0
+food_penalty   = 0
+celeb_trade_penalty  = 0
+celeb_shield_penalty = 0
+celeb_food_penalty   = 0
 
 ruler_male_title = _("King")
 ruler_female_title = _("Queen")
@@ -389,13 +407,19 @@
 waste_extra_distance       = 0
 waste_max_distance_cap     = 36
 
-production_trade_bonus    = 0, 1
-production_shield_bonus   = 0, 0
-production_food_bonus     = 0, 0
-
-production_trade_penalty  = 0, 0
-production_shield_penalty = 0, 0
-production_food_penalty   = 0, 0
+trade_bonus    = 0
+shield_bonus   = 0
+food_bonus     = 0
+celeb_trade_bonus    = 1
+celeb_shield_bonus   = 0
+celeb_food_bonus     = 0
+
+trade_penalty  = 0
+shield_penalty = 0
+food_penalty   = 0
+celeb_trade_penalty  = 0
+celeb_shield_penalty = 0
+celeb_food_penalty   = 0
 
 ruler_male_title = _("Comrade")
 ruler_female_title = _("Comrade")
@@ -475,13 +499,19 @@
 waste_extra_distance       = 0
 waste_max_distance_cap     = 36
 
-production_trade_bonus    = 1, 1
-production_shield_bonus   = 0, 0
-production_food_bonus     = 0, 0
-
-production_trade_penalty  = 0, 0
-production_shield_penalty = 0, 0
-production_food_penalty   = 0, 0
+trade_bonus    = 1
+shield_bonus   = 0
+food_bonus     = 0
+celeb_trade_bonus    = 1
+celeb_shield_bonus   = 0
+celeb_food_bonus     = 0
+
+trade_penalty  = 0
+shield_penalty = 0
+food_penalty   = 0
+celeb_trade_penalty  = 0
+celeb_shield_penalty = 0
+celeb_food_penalty   = 0
 
 ruler_male_title = _("Consul")
 ruler_female_title = _("Consul")
@@ -564,13 +594,19 @@
 waste_extra_distance       = 0
 waste_max_distance_cap     = 36
 
-production_trade_bonus    = 1, 1
-production_shield_bonus   = 0, 0
-production_food_bonus     = 0, 0
-
-production_trade_penalty  = 0, 0
-production_shield_penalty = 0, 0
-production_food_penalty   = 0, 0
+trade_bonus    = 1
+shield_bonus   = 0
+food_bonus     = 0
+celeb_trade_bonus    = 1
+celeb_shield_bonus   = 0
+celeb_food_bonus     = 0
+
+trade_penalty  = 0
+shield_penalty = 0
+food_penalty   = 0
+celeb_trade_penalty  = 0
+celeb_shield_penalty = 0
+celeb_food_penalty   = 0
 
 ruler_male_title = _("President")
 ruler_female_title = _("President")
diff -Nur -Xfreecivdiff.ignore freeciv-cvs/data/smac/buildings.ruleset freeciv-patched/data/smac/buildings.ruleset
--- freeciv-cvs/data/smac/buildings.ruleset	2003-06-30 12:33:48.000000000 +0100
+++ freeciv-patched/data/smac/buildings.ruleset	2003-06-30 12:33:10.000000000 +0100
@@ -45,6 +45,8 @@
 name          = _("Aerospace Complex")
 tech_req      = "Doctrine: Air Power"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -70,6 +72,8 @@
 name          = _("Bioenhancement Center")
 tech_req      = "Neural Grafting"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -93,6 +97,8 @@
 name          = _("Biology Lab")
 tech_req      = "Centauri Empathy"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -113,6 +119,8 @@
 name          = _("Centauri Preserve")
 tech_req      = "Centauri Meditation"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -130,6 +138,8 @@
 name          = _("Children's Creche")
 tech_req      = "Ethical Calculus"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -147,6 +157,8 @@
 name          = _("Command Center")
 tech_req      = "Doctrine: Mobility"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -174,6 +186,8 @@
 name          = _("Energy Bank")
 tech_req      = "Industrial Economics"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -196,6 +210,8 @@
 name          = _("Fusion Lab")
 tech_req      = "Fusion Power"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -219,6 +235,8 @@
 name          = _("Genejack Factory")
 tech_req      = "Retroviral Engineering"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -243,6 +261,8 @@
 name          = _("Hab Complex")
 tech_req      = "Industrial Automation"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -266,6 +286,8 @@
 name          = _("Habitation Dome")
 tech_req      = "Super Tensile Solids"
 bldg_req      = "Hab Complex"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -289,6 +311,8 @@
 name          = _("Headquarters")
 tech_req      = "None"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "Player"
@@ -319,6 +343,8 @@
 name          = _("Hologram Theatre")
 tech_req      = "Planetary Networks"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -343,6 +369,8 @@
 name          = _("Hybrid Forest")
 tech_req      = "Planetary Economics"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -373,6 +401,8 @@
 name          = _("Nanohospital")
 tech_req      = "Homo Superior"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -399,6 +429,8 @@
 name          = _("Nanoreplicator")
 tech_req      = "Matter Editation"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -421,6 +453,8 @@
 name          = _("Naval Yard")
 tech_req      = "Doctrine: Initiative"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 terr_gate     = "Ocean"
 ;spec_gate    =
 equiv_range   = "City"
@@ -446,6 +480,8 @@
 name          = _("Nessus Mining Station")
 tech_req      = "Self-Aware Machines"
 bldg_req      = "Aerospace Complex"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -463,6 +499,8 @@
 name          = _("Network Node")
 tech_req      = "Information Networks"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -485,6 +523,8 @@
 name          = _("Orbital Defense Pod")
 tech_req      = "Self-Aware Machines"
 bldg_req      = "Aerospace Complex"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -502,6 +542,8 @@
 name          = _("Orbital Power Transmitter")
 tech_req      = "Advanced Spaceflight"
 bldg_req      = "Aerospace Complex"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -519,6 +561,8 @@
 name          = _("Paradise Garden")
 tech_req      = "Sentient Econometrics"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -541,6 +585,8 @@
 name          = _("Perimeter Defense")
 tech_req      = "Doctrine: Loyalty"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -566,6 +612,8 @@
 name          = _("Pressure Dome")
 tech_req      = "Doctrine: Flexibility"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 terr_gate     = "Ocean"
 ;spec_gate    =
 equiv_range   = "City"
@@ -583,6 +631,8 @@
 name          = _("Psi Gate")
 tech_req      = "Matter Transmission"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -608,6 +658,8 @@
 name          = _("Punishment Sphere")
 tech_req      = "Advanced Military Algorithms"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -634,6 +686,8 @@
 name          = _("Quantum Converter")
 tech_req      = "Quantum Machinery"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -656,6 +710,8 @@
 name          = _("Quantum Lab")
 tech_req      = "Quantum Power"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -679,6 +735,8 @@
 name          = _("Recreation Commons")
 tech_req      = "Social Psych"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -701,6 +759,8 @@
 name          = _("Recycling Tanks")
 tech_req      = "Biogenetics"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -718,6 +778,8 @@
 name          = _("Research Hospital")
 tech_req      = "Gene Splicing"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -744,6 +806,8 @@
 name          = _("Robotic Assembly Plant")
 tech_req      = "Industrial Nanorobotics"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -767,6 +831,8 @@
 name          = _("Skunkworks")
 tech_req      = "Advanced Subatomic Theory"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -784,6 +850,8 @@
 name          = _("Sky Hydroponics Lab")
 tech_req      = "Orbital Spaceflight"
 bldg_req      = "Aerospace Complex"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -801,6 +869,8 @@
 name          = _("Stockpile Energy")
 tech_req      = "None"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -825,6 +895,8 @@
 name          = _("Tachyon Field")
 tech_req      = "Probability Mechanics"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -854,6 +926,8 @@
 name          = _("Temple of Planet")
 tech_req      = "Secrets of Alpha Centauri"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -871,6 +945,8 @@
 name          = _("Tree Farm")
 tech_req      = "Environmental Economics"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -902,6 +978,8 @@
 name          = _("The Ascent to Transcendence")
 tech_req      = "Threshold of Transcendence"
 bldg_req      = "The Voice Of Planet"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -919,6 +997,8 @@
 name        = _("The Ascetic Virtues")
 tech_req    = "Planetary Economics"
 bldg_req    = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -936,6 +1016,8 @@
 name          = _("The Bulk Matter Transmitter")
 tech_req      = "Matter Transmission"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -953,6 +1035,8 @@
 name          = _("The Citizens' Defense Force")
 tech_req      = "Intellectual Integrity"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "Player"
@@ -978,6 +1062,8 @@
 name          = _("Clinical Immortality")
 tech_req      = "Matter Editation"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -1000,6 +1086,8 @@
 name          = _("The Cloning Vats")
 tech_req      = "Biomachinery"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -1017,6 +1105,8 @@
 name          = _("The Command Nexus")
 tech_req      = "Doctrine: Loyalty"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "Player"
@@ -1040,6 +1130,8 @@
 name          = _("The Cyborg Factory")
 tech_req      = "Mind-Machine Interface"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "Player"
@@ -1062,6 +1154,8 @@
 name          = _("The Dream Twister")
 tech_req      = "The Will To Power"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -1079,6 +1173,8 @@
 name          = _("The Empath Guild")
 tech_req      = "Centauri Empathy"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -1101,6 +1197,8 @@
 name          = _("The Human Genome Project")
 tech_req      = "Biogenetics"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -1123,6 +1221,8 @@
 name          = _("The Hunter-Seeker Algorithm")
 tech_req      = "Presentient Algorithms"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -1143,6 +1243,8 @@
 name          = _("The Living Refinery")
 tech_req      = "Advanced Spaceflight"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -1160,6 +1262,8 @@
 name          = _("The Longevity Vaccine")
 tech_req      = "Bio-Engineering"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -1188,6 +1292,8 @@
 name          = _("The Maritime Control Center")
 tech_req      = "Doctrine: Initiative"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "Player"
@@ -1213,6 +1319,8 @@
 name          = _("The Merchant Exchange")
 tech_req      = "Industrial Base"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -1236,6 +1344,8 @@
 name          = _("The Nano Factory")
 tech_req      = "Industrial Nanorobotics"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -1262,6 +1372,8 @@
 name          = _("The Network Backbone")
 tech_req      = "Digital Sentience"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -1279,6 +1391,8 @@
 name          = _("The Neural Amplifier")
 tech_req      = "Neural Grafting"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -1296,6 +1410,8 @@
 name          = _("The Pholus Mutagen")
 tech_req      = "Centauri Genetics"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -1313,6 +1429,8 @@
 name          = _("The Planetary Datalinks")
 tech_req      = "Cyberethics"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -1336,6 +1454,8 @@
 name          = _("The Planetary Transit System")
 tech_req      = "Industrial Automation"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -1353,6 +1473,8 @@
 name          = _("The Self-Aware Colony")
 tech_req      = "Self-Aware Machines"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -1370,6 +1492,8 @@
 name          = _("The Singularity Inductor")
 tech_req      = "Controlled Singularity"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "Player"
@@ -1392,6 +1516,8 @@
 name          = _("The Space Elevator")
 tech_req      = "Super Tensile Solids"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -1409,6 +1535,8 @@
 name          = _("The Supercollider")
 tech_req      = "Applied Relativity"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -1431,6 +1559,8 @@
 name          = _("The Telepathic Matrix")
 tech_req      = "Eudaimonia"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -1448,6 +1578,8 @@
 name          = _("The Theory of Everything")
 tech_req      = "Unified Field Theory"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -1470,6 +1602,8 @@
 name          = _("The Universal Translator")
 tech_req      = "Homo Superior"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -1492,6 +1626,8 @@
 name          = _("The Virtual World")
 tech_req      = "Planetary Networks"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -1520,6 +1656,8 @@
 name          = _("The Voice Of Planet")
 tech_req      = "Threshold of Transcendence"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -1537,6 +1675,8 @@
 name          = _("The Weather Paradigm")
 tech_req      = "Centauri Ecology"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
@@ -1554,6 +1694,8 @@
 name          = _("The Xenoempathy Dome")
 tech_req      = "Centauri Meditation"
 bldg_req      = "None"
+graphic	= "b.oracle"
+graphic_alt	= "-"
 ;terr_gate    =
 ;spec_gate    =
 equiv_range   = "City"
diff -Nur -Xfreecivdiff.ignore freeciv-cvs/data/smac/governments.ruleset freeciv-patched/data/smac/governments.ruleset
--- freeciv-cvs/data/smac/governments.ruleset	2003-06-30 12:33:48.000000000 +0100
+++ freeciv-patched/data/smac/governments.ruleset	2003-06-30 12:33:10.000000000 +0100
@@ -15,7 +15,7 @@
 options="1.9"
 
 [governments]
-default="Simple"
+default="Simple Frontier State"
 when_anarchy="Anarchy"
 ai_goal="Simple"
 max_revolution_turns=0
@@ -158,6 +158,13 @@
 corruption_distance_factor = 1 
 corruption_extra_distance  = 0 
 
+waste_level                = 0
+waste_modifier             = 1
+waste_fixed_distance       = 0
+waste_distance_factor      = 0
+waste_extra_distance       = 0
+waste_max_distance_cap     = 36
+
 trade_bonus    = 1
 shield_bonus   = 0
 food_bonus     = 0
@@ -211,6 +218,13 @@
 corruption_distance_factor = 1 
 corruption_extra_distance  = 0 
 
+waste_level                = 0
+waste_modifier             = 1
+waste_fixed_distance       = 0
+waste_distance_factor      = 0
+waste_extra_distance       = 0
+waste_max_distance_cap     = 36
+
 trade_bonus    = 1
 shield_bonus   = 0
 food_bonus     = 0
@@ -389,3 +403,8 @@
          "Unit_Cost", "Player", -10
     }
 final_action = ""
+
+[compound_government_default]
+name         = "Simple Frontier State"
+base         = "Simple"
+modifiers    = "Simple", "None", "Frontier", "Survival"
diff -Nur -Xfreecivdiff.ignore freeciv-cvs/server/plrhand.c freeciv-patched/server/plrhand.c
--- freeciv-cvs/server/plrhand.c	2003-06-30 12:33:44.000000000 +0100
+++ freeciv-patched/server/plrhand.c	2003-06-30 12:33:10.000000000 +0100
@@ -38,6 +38,7 @@
 #include "gamehand.h"
 #include "gamelog.h"
 #include "maphand.h"
+#include "ruleset.h"
 #include "sernet.h"
 #include "settlers.h"
 #include "srv_main.h"
@@ -743,26 +744,16 @@
 /**************************************************************************
 ...
 **************************************************************************/
-void handle_player_government(struct player *pplayer,
-			     struct packet_player_request *preq)
+static void change_player_gov(struct player *pplayer, int newgov)
 {
-  if (pplayer->government != game.government_when_anarchy ||
-      preq->government < 0 || preq->government >= game.government_count ||
-      !can_change_to_government(pplayer, preq->government)) {
-    return;
-  }
-
-  if((pplayer->revolution<=5) && (pplayer->revolution>0))
-    return;
-
-  pplayer->government=preq->government;
+  pplayer->government = newgov;
   notify_player(pplayer, _("Game: %s now governs the %s as a %s."), 
 		pplayer->name, 
   	        get_nation_name_plural(pplayer->nation),
-		get_government_name(preq->government));  
+		get_government_name(newgov));  
   gamelog(GAMELOG_GOVERNMENT, _("%s form a %s"),
 	  get_nation_name_plural(pplayer->nation),
-	  get_government_name(preq->government));
+	  get_government_name(newgov));
 
   if (!pplayer->ai.control) {
     /* Keep luxuries if we have any.  Try to max out science. -GJW */
@@ -784,6 +775,24 @@
 /**************************************************************************
 ...
 **************************************************************************/
+void handle_player_government(struct player *pplayer,
+			     struct packet_player_request *preq)
+{
+  if (pplayer->government != game.government_when_anarchy ||
+      preq->government < 0 || preq->government >= game.government_count ||
+      !can_change_to_government(pplayer, preq->government)) {
+    return;
+  }
+
+  if((pplayer->revolution<=5) && (pplayer->revolution>0))
+    return;
+
+  change_player_gov(pplayer, preq->government);
+}
+
+/**************************************************************************
+...
+**************************************************************************/
 void handle_player_revolution(struct player *pplayer)
 {
   if ((pplayer->revolution<=5) &&
@@ -845,6 +854,26 @@
 }
 
 /**************************************************************************
+...
+**************************************************************************/
+void handle_request_compound(struct player *pplayer,
+			     struct packet_request_compound *req)
+{
+  if (req->is_gov) {
+    int id;
+
+    id = form_compound_gov(pplayer, req->name, req->basetype, req->modifiers);
+    send_single_government(&game.game_connections, id);
+    change_player_gov(pplayer, id);
+  } else {
+    Unit_Type_id id;
+
+    id = form_compound_unit(req->name, req->basetype, req->modifiers);
+    send_single_unit(&game.game_connections, id);
+  }
+}
+
+/**************************************************************************
   Handles a player cancelling a "pact" with another player.
 
   packet.id is id of player we want to cancel a pact with
diff -Nur -Xfreecivdiff.ignore freeciv-cvs/server/plrhand.h freeciv-patched/server/plrhand.h
--- freeciv-cvs/server/plrhand.h	2003-06-07 19:58:49.000000000 +0100
+++ freeciv-patched/server/plrhand.h	2003-06-30 12:33:10.000000000 +0100
@@ -63,6 +63,8 @@
 			     struct packet_player_request *preq);
 void handle_player_research(struct player *pplayer,
 			    struct packet_player_request *preq);
+void handle_request_compound(struct player *pplayer,
+			     struct packet_request_compound *req);
 void handle_player_tech_goal(struct player *pplayer,
                             struct packet_player_request *preq);
 void handle_player_attribute_chunk(struct player *pplayer,
diff -Nur -Xfreecivdiff.ignore freeciv-cvs/server/ruleset.c freeciv-patched/server/ruleset.c
--- freeciv-cvs/server/ruleset.c	2003-06-30 12:33:44.000000000 +0100
+++ freeciv-patched/server/ruleset.c	2003-06-30 12:33:10.000000000 +0100
@@ -35,6 +35,7 @@
 #include "support.h"
 #include "tech.h"
 #include "unit.h"
+#include "unittype.h"
 
 #include "citytools.h"
 
@@ -63,8 +64,8 @@
 static Impr_Type_id lookup_impr_type(struct section_file *file, const char *prefix,
 				     const char *entry, bool required,
 				     const char *filename, const char *description);
-static int lookup_government(struct section_file *file, const char *entry,
-			     const char *filename);
+static int lookup_government(struct section_file *file, const char *prefix,
+			     const char *entry, const char *filename);
 static int lookup_city_cost(struct section_file *file, const char *prefix,
 			    const char *entry, const char *filename);
 static char *lookup_helptext(struct section_file *file, char *prefix);
@@ -96,6 +97,10 @@
 
 static void send_ruleset_techs(struct conn_list *dest);
 static void send_ruleset_units(struct conn_list *dest);
+static void send_ruleset_unit_modifier_types(struct conn_list *dest);
+static void send_ruleset_unit_modifiers(struct conn_list *dest);
+static void send_ruleset_gov_modifier_types(struct conn_list *dest);
+static void send_ruleset_gov_modifiers(struct conn_list *dest);
 static void send_ruleset_buildings(struct conn_list *dest);
 static void send_ruleset_terrain(struct conn_list *dest);
 static void send_ruleset_governments(struct conn_list *dest);
@@ -347,13 +352,13 @@
   Lookup entry in the file and return the corresponding government index;
   dies if can't find/match.  filename is for error message.
 **************************************************************************/
-static int lookup_government(struct section_file *file, const char *entry,
-			     const char *filename)
+static int lookup_government(struct section_file *file, const char *prefix,
+			     const char *entry, const char *filename)
 {
   char *sval;
   struct government *gov;
   
-  sval = secfile_lookup_str(file, "%s", entry);
+  sval = secfile_lookup_str(file, "%s.%s", prefix, entry);
   gov = find_government_by_name(sval);
   if (!gov) {
     freelog(LOG_FATAL, "for %s couldn't match government \"%s\" (%s)",
@@ -784,6 +789,23 @@
       e->cond_bldg = B_LAST;
     }
 
+    item = secfile_lookup_str_default(file, "", "%s.effect%d.cond_govmod",
+				      secname, j);
+    if (*item != '\0') {
+      int govmod = find_gov_modifier_by_name(item);
+      if (govmod < 0) {
+	freelog(LOG_ERROR, "for %s effect[%d].cond_govmod couldn't match "
+			   "government modifier \"%s\" (%s)",
+		name, j, item, filename);
+	e->cond_govmod = game.num_gov_modifiers;
+	problem = TRUE;
+      } else {
+	e->cond_govmod = govmod;
+      }
+    } else {
+      e->cond_govmod = game.num_gov_modifiers;
+    }
+
     item = secfile_lookup_str_default(file, "", "%s.effect%d.cond_gov",
 				      secname, j);
     if (*item != '\0') {
@@ -900,17 +922,105 @@
 /**************************************************************************
 ...  
 **************************************************************************/
+static void read_modifier_types(struct section_file *file,
+    				struct modifier_type *modifier_types,
+				int *num_modifier_types)
+{
+  char **sec;
+  int num_mods, i;
+  const char *filename = secfile_filename(file);
+
+  /* Read modifier types from mod_type_xxx entries */
+  sec = secfile_get_secnames_prefix(file, "mod_type_", &num_mods);
+  if (num_mods > MAX_MODS) {
+    freelog(LOG_FATAL, "Too many modifier types! (%d, max %d; %s)",
+	    num_mods, MAX_MODS, filename);
+    exit(EXIT_FAILURE);
+  }
+  *num_modifier_types = num_mods;
+
+  /* Read in names first so that lookup works later on */
+  for (i = 0; i < num_mods; ++i) {
+    struct modifier_type *um = &modifier_types[i];
+    sz_strlcpy(um->name, secfile_lookup_str(file, "%s.name", sec[i]));
+  }
+
+  for (i = 0; i < num_mods; ++i) {
+    struct modifier_type *um = &modifier_types[i];
+
+    um->id = modifier_type_from_str(um->name, modifier_types, num_mods);
+    um->tech_requirement = lookup_tech(file, sec[i], "tech_req", 0,
+				       filename, um->name);
+    um->obsoleted_by = lookup_unit_type(file, sec[i], "obsolete_by", 0,
+					filename, um->name);
+    um->must_use = secfile_lookup_bool(file, "%s.must_use", sec[i]);
+  }
+  free(sec);
+}
+
+/**************************************************************************
+...  
+**************************************************************************/
+static struct modifier *read_modifiers(struct section_file *file,
+    				       int *num_modifiers,
+				       struct modifier_type *modifier_types,
+				       int num_modifier_types)
+{
+  char **sec;
+  int num_mods, i;
+  struct modifier *modifiers;
+  const char *filename = secfile_filename(file);
+
+  sec = secfile_get_secnames_prefix(file, "modifier_", &num_mods);
+  if (num_mods > U_LAST) {
+    freelog(LOG_FATAL, "Too many unit modifiers (%d, max %d) in %s",
+	    num_mods, U_LAST, filename);
+    exit(EXIT_FAILURE);
+  }
+  *num_modifiers = num_mods;
+  if (num_mods) {
+    modifiers = fc_calloc(num_mods, sizeof(struct modifier));
+  } else {
+    modifiers = NULL;
+  }
+  for (i = 0; i < num_mods; ++i) {
+    struct modifier *um = &modifiers[i];
+    char *str;
+
+    sz_strlcpy(um->name, secfile_lookup_str(file, "%s.name", sec[i]));
+    um->tech_requirement = lookup_tech(file, sec[i], "tech_req",
+				       FALSE, filename, um->name);
+    um->action = mystrdup(secfile_lookup_str(file, "%s.action", sec[i]));
+    um->final_action = mystrdup(secfile_lookup_str(file, "%s.final_action",
+						   sec[i]));
+    str = secfile_lookup_str(file, "%s.type", sec[i]);
+    if ((um->type = modifier_type_from_str(str, modifier_types, num_modifier_types)) == -1) {
+      freelog(LOG_FATAL, "Bad unit modifier type %s for \"%s\" (%s)", str,
+	      um->name, filename);
+      exit(EXIT_FAILURE);
+    }
+    um->effect = load_effects(sec[i], um->name, file, FALSE, TRUE);
+  }
+  free(sec);
+  return modifiers;
+}
+
+/**************************************************************************
+...  
+**************************************************************************/
 static void load_ruleset_units(struct section_file *file)
 {
   char *datafile_options;
   struct unit_type *u;
-  int i, j, ival, nval;
-  char *sval, **slist, **sec;
+  int i, j, nval;
+  char **sec;
   const char *filename = secfile_filename(file);
 
   datafile_options =
     check_ruleset_capabilities(file, "+1.9", filename);
 
+  read_modifier_types(file, unit_modifier_types, &game.num_unit_modifier_types);
+
   sec = secfile_get_secnames_prefix(file, "unit_", &nval);
 
   /* Tech requirement is used to flag removed unit_types, which
@@ -923,69 +1033,16 @@
 				      FALSE, filename, u->name);
   } unit_type_iterate_end;
 
-  unit_type_iterate(i) {
-    u = &unit_types[i];
-    if (unit_type_exists(i)) {
-      u->obsoleted_by = lookup_unit_type(file, sec[i],
-					 "obsolete_by", FALSE, filename, u->name);
-    } else {
-      (void) section_file_lookup(file, "%s.obsolete_by", sec[i]); /* unused */
-      u->obsoleted_by = -1;
-    }
-  } unit_type_iterate_end;
-
   /* main stats: */
   unit_type_iterate(i) {
     u = &unit_types[i];
+    u->basetype = U_LAST;
+    utype_read_members(u, file, sec[i]);
 
     u->impr_requirement =
       find_improvement_by_name(secfile_lookup_str_default(file, "None", 
 					"%s.impr_req", sec[i]));
 
-    sval = secfile_lookup_str(file, "%s.move_type", sec[i]);
-    ival = unit_move_type_from_str(sval);
-    if (ival==0) {
-      freelog(LOG_FATAL, "for unit_type \"%s\": bad move_type %s (%s)",
-	   u->name, sval, filename);
-      exit(EXIT_FAILURE);
-    }
-    u->move_type = ival;
-    
-    sz_strlcpy(u->sound_move,
-	       secfile_lookup_str_default(file, "-", "%s.sound_move",
-					  sec[i]));
-    sz_strlcpy(u->sound_move_alt,
-	       secfile_lookup_str_default(file, "-", "%s.sound_move_alt",
-					  sec[i]));
-    sz_strlcpy(u->sound_fight,
-	       secfile_lookup_str_default(file, "-", "%s.sound_fight",
-					  sec[i]));
-    sz_strlcpy(u->sound_fight_alt,
-	       secfile_lookup_str_default(file, "-", "%s.sound_fight_alt",
-					  sec[i]));
-    
-    sz_strlcpy(u->graphic_str,
-	       secfile_lookup_str(file,"%s.graphic", sec[i]));
-    sz_strlcpy(u->graphic_alt,
-	       secfile_lookup_str(file,"%s.graphic_alt", sec[i]));
-    
-    u->build_cost =
-      secfile_lookup_int(file,"%s.build_cost", sec[i]);
-    u->pop_cost =
-      secfile_lookup_int(file,"%s.pop_cost", sec[i]);
-    u->attack_strength =
-      secfile_lookup_int(file,"%s.attack", sec[i]);
-    u->defense_strength =
-      secfile_lookup_int(file,"%s.defense", sec[i]);
-    u->move_rate =
-      SINGLE_MOVE*secfile_lookup_int(file,"%s.move_rate", sec[i]);
-    
-    u->vision_range =
-      secfile_lookup_int(file,"%s.vision_range", sec[i]);
-    u->transport_capacity =
-      secfile_lookup_int(file,"%s.transport_cap", sec[i]);
-    u->hp = secfile_lookup_int(file,"%s.hitpoints", sec[i]);
-    u->firepower = secfile_lookup_int(file,"%s.firepower", sec[i]);
     if (u->firepower <= 0) {
       freelog(LOG_FATAL, "for unit_type \"%s\": firepower is %d but "
 	      "must be at least 1.\nSet the unit's attack strength to 0 "
@@ -993,78 +1050,96 @@
 	      u->name, u->firepower, filename);
       exit(EXIT_FAILURE);
     }
-    u->fuel = secfile_lookup_int(file,"%s.fuel", sec[i]);
-
-    u->happy_cost  = secfile_lookup_int(file, "%s.uk_happy", sec[i]);
-    u->shield_cost = secfile_lookup_int(file, "%s.uk_shield", sec[i]);
-    u->food_cost   = secfile_lookup_int(file, "%s.uk_food", sec[i]);
-    u->gold_cost   = secfile_lookup_int(file, "%s.uk_gold", sec[i]);
-
     u->effect = load_effects(sec[i], u->name, file, TRUE, FALSE);
 
     u->helptext = lookup_helptext(file, sec[i]);
   } unit_type_iterate_end;
   
-  /* flags */
   unit_type_iterate(i) {
     u = &unit_types[i];
-    BV_CLR_ALL(u->flags);
-    assert(!unit_type_flag(i, F_LAST-1));
+    if (unit_type_flag(i, F_PARATROOPERS)) {
+      u->paratroopers_range = secfile_lookup_int(file,
+	  		"%s.paratroopers_range", sec[i]);
+      u->paratroopers_mr_req = SINGLE_MOVE * secfile_lookup_int(file,
+	  		"%s.paratroopers_mr_req", sec[i]);
+      u->paratroopers_mr_sub = SINGLE_MOVE * secfile_lookup_int(file,
+	  		"%s.paratroopers_mr_sub", sec[i]);
+    } else {
+      u->paratroopers_range = 0;
+      u->paratroopers_mr_req = 0;
+      u->paratroopers_mr_sub = 0;
+    }
+  } unit_type_iterate_end;
+  free(sec);
 
-    slist = secfile_lookup_str_vec(file, &nval, "%s.flags", sec[i]);
-    for(j=0; j<nval; j++) {
-      sval = slist[j];
-      if(strcmp(sval,"")==0) {
-	continue;
-      }
-	ival = unit_flag_from_str(sval);
-      if (ival==F_MAX) {
-	freelog(LOG_ERROR, "for unit_type \"%s\": bad flag name \"%s\" (%s)",
-	     u->name, sval, filename);
-      }
-      BV_SET(u->flags, ival);
-      assert(unit_type_flag(i, ival));
-
-      if(ival == F_PARATROOPERS) {
-        u->paratroopers_range = secfile_lookup_int(file,
-            "%s.paratroopers_range", sec[i]);
-        u->paratroopers_mr_req = 3*secfile_lookup_int(file,
-            "%s.paratroopers_mr_req", sec[i]);
-        u->paratroopers_mr_sub = 3*secfile_lookup_int(file,
-            "%s.paratroopers_mr_sub", sec[i]);
+  lookup_tech_list(file, "u_specials", "partisan_req",
+		   game.rtech.partisan_req, filename);
+
+  unit_modifiers = read_modifiers(file, &game.num_unit_modifiers,
+				  unit_modifier_types,
+				  game.num_unit_modifier_types);
+
+  sec = secfile_get_secnames_prefix(file, "compound_unit_", &nval);
+  for (i = 0; i < nval; ++i) {
+    int modifiers[MAX_MODS];
+    char *name, *sval;
+    Unit_Type_id basetype;
+    struct unit_type *utype;
+
+    name = secfile_lookup_str(file, "%s.name", sec[i]);
+    basetype = lookup_unit_type(file, sec[i], "base", TRUE,
+				filename, name);
+    freelog(LOG_DEBUG, "Forming compound unit %s", name);
+
+    for (j = 0; j < game.num_unit_modifier_types; ++j) {
+      sval = secfile_lookup_str(file, "%s.modifiers,%d", sec[i], j);
+      if (!unit_modifier_types[j].must_use && sval[0] == '\0') {
+	modifiers[j] = U_LAST;
       } else {
-        u->paratroopers_range = 0;
-        u->paratroopers_mr_req = 0;
-        u->paratroopers_mr_sub = 0;
+	int ival = find_unit_modifier_by_name(sval);
+
+	if (ival == U_LAST) {
+	   freelog(LOG_FATAL,
+		   "for %s, couldn't match unit modifier \"%s\" (%s)",
+		   sec[i], sval, filename);
+	   exit(EXIT_FAILURE);
+	} else if (unit_modifiers[ival].type != unit_modifier_types[j].id) {
+	  freelog(LOG_FATAL, "for %s, unit modifier \"%s\" is not of "
+		  "required compound type %s (%s)",
+		  sec[i], sval, unit_modifier_types[j].name,
+		  filename);
+	  exit(EXIT_FAILURE);
+	} else {
+	   modifiers[j] = ival;
+	}
       }
     }
-    free(slist);
-  } unit_type_iterate_end;
-    
-  /* roles */
+    utype = &unit_types[form_compound_unit(name, basetype, modifiers)];
+    /* Allow override of flags and roles if specified */
+    if (secfile_lookup_str_default(file, NULL, "%s.roles", sec[i])) {
+      utype_read_member(utype, "roles", file, sec[i]);
+    }
+    if (secfile_lookup_str_default(file, NULL, "%s.flags", sec[i])) {
+      utype_read_member(utype, "flags", file, sec[i]);
+    }
+  }
+  free(sec);
+
+  sec = secfile_get_secnames_prefix(file, "unit_", &nval);
   unit_type_iterate(i) {
     u = &unit_types[i];
-    BV_CLR_ALL(u->roles);
-    
-    slist = secfile_lookup_str_vec(file, &nval, "%s.roles", sec[i] );
-    for(j=0; j<nval; j++) {
-      sval = slist[j];
-      if(strcmp(sval,"")==0) {
-	continue;
-      }
-      ival = unit_role_from_str(sval);
-      if (ival==L_LAST) {
-	freelog(LOG_ERROR, "for unit_type \"%s\": bad role name \"%s\" (%s)",
-	     u->name, sval, filename);
+    if (u->basetype == U_LAST) {
+      if (unit_type_exists(i)) {
+	u->obsoleted_by = lookup_unit_type(file, sec[i],
+					   "obsolete_by", FALSE, filename,
+					   u->name);
+      } else {
+	(void) section_file_lookup(file, "%s.obsolete_by", sec[i]); /* unused */
+	u->obsoleted_by = -1;
       }
-      BV_SET(u->roles, ival - L_FIRST);
-      assert(unit_has_role(i, ival));
     }
-    free(slist);
   } unit_type_iterate_end;
-
-  lookup_tech_list(file, "u_specials", "partisan_req",
-		   game.rtech.partisan_req, filename);
+  free(sec);
 
   /* Some more consistency checking: */
   unit_type_iterate(i) {
@@ -1170,7 +1245,6 @@
 
   update_simple_ai_types();
 
-  free(sec);
   section_file_check_unused(file, filename);
   section_file_free(file);
 }
@@ -1636,33 +1710,13 @@
 
   sec = secfile_get_secnames_prefix(file, "government_", &nval);
 
-  game.default_government
-    = lookup_government(file, "governments.default", filename);
-  
-  game.government_when_anarchy
-    = lookup_government(file, "governments.when_anarchy", filename);
-  
-  game.ai_goal_government
-    = lookup_government(file, "governments.ai_goal", filename);
-
-  freelog(LOG_DEBUG, "govs: def %d, anarchy %d, ai_goal %d",
-	  game.default_government, game.government_when_anarchy,
-	  game.ai_goal_government);
-  
-  /* Because player_init is called before rulesets are loaded we set
-   * all players governments here, if they have not been previously
-   * set (eg by loading game).
-   */
-  for(i=0; i<MAX_NUM_PLAYERS+MAX_NUM_BARBARIANS; i++) {
-    if (game.players[i].government == G_MAGIC) {
-      game.players[i].government = game.default_government;
-    }
-  }
+  read_modifier_types(file, gov_modifier_types, &game.num_gov_modifier_types);
 
   /* easy ones: */
   for(i=0; i<game.government_count; i++) {
     g = &governments[i];
     
+    g->basetype = G_MAGIC;
     g->required_tech
       = lookup_tech(file, sec[i], "tech_req", FALSE, filename, g->name);
     
@@ -1726,32 +1780,32 @@
       = secfile_lookup_int_default(file, 36, "%s.waste_max_distance_cap", sec[i]); 
 
     g->trade_bonus
-      = secfile_lookup_int(file, "%s.production_trade_bonus", sec[i]);
+      = secfile_lookup_int(file, "%s.trade_bonus", sec[i]);
     g->shield_bonus
-      = secfile_lookup_int(file, "%s.production_shield_bonus", sec[i]);
+      = secfile_lookup_int(file, "%s.shield_bonus", sec[i]);
     g->food_bonus
-      = secfile_lookup_int(file, "%s.production_food_bonus", sec[i]);
+      = secfile_lookup_int(file, "%s.food_bonus", sec[i]);
 
     g->celeb_trade_bonus
-      = secfile_lookup_int(file, "%s.production_trade_bonus,1", sec[i]);
+      = secfile_lookup_int(file, "%s.celeb_trade_bonus", sec[i]);
     g->celeb_shield_bonus
-      = secfile_lookup_int(file, "%s.production_shield_bonus,1", sec[i]);
+      = secfile_lookup_int(file, "%s.celeb_shield_bonus", sec[i]);
     g->celeb_food_bonus
-      = secfile_lookup_int(file, "%s.production_food_bonus,1", sec[i]);
+      = secfile_lookup_int(file, "%s.celeb_food_bonus", sec[i]);
 
     g->trade_before_penalty
-      = secfile_lookup_int(file, "%s.production_trade_penalty", sec[i]);
+      = secfile_lookup_int(file, "%s.trade_penalty", sec[i]);
     g->shields_before_penalty
-      = secfile_lookup_int(file, "%s.production_shield_penalty", sec[i]);
+      = secfile_lookup_int(file, "%s.shield_penalty", sec[i]);
     g->food_before_penalty
-      = secfile_lookup_int(file, "%s.production_food_penalty", sec[i]);
+      = secfile_lookup_int(file, "%s.food_penalty", sec[i]);
 
     g->celeb_trade_before_penalty
-      = secfile_lookup_int(file, "%s.production_trade_penalty,1", sec[i]);
+      = secfile_lookup_int(file, "%s.celeb_trade_penalty", sec[i]);
     g->celeb_shields_before_penalty
-      = secfile_lookup_int(file, "%s.production_shield_penalty,1", sec[i]);
+      = secfile_lookup_int(file, "%s.celeb_shield_penalty", sec[i]);
     g->celeb_food_before_penalty
-      = secfile_lookup_int(file, "%s.production_food_penalty,1", sec[i]);
+      = secfile_lookup_int(file, "%s.celeb_food_penalty", sec[i]);
 
     g->effect = load_effects(sec[i], g->name, file, FALSE, TRUE);
     
@@ -1836,6 +1890,72 @@
     }
     freelog(LOG_DEBUG, "%s subgoal %d", g->name, g->subgoal);
   }
+  free(sec);
+
+  gov_modifiers = read_modifiers(file, &game.num_gov_modifiers,
+      				 gov_modifier_types,
+				 game.num_gov_modifier_types);
+
+  sec = secfile_get_secnames_prefix(file, "compound_government_", &nval);
+  for (i = 0; i < nval; ++i) {
+    int modifiers[MAX_MODS];
+    char *name, *sval;
+    int basetype;
+
+    name = secfile_lookup_str(file, "%s.name", sec[i]);
+    basetype = lookup_government(file, sec[i], "base", filename);
+    freelog(LOG_DEBUG, "Forming compound gov %s", name);
+
+    for (j = 0; j < game.num_gov_modifier_types; ++j) {
+      sval = secfile_lookup_str(file, "%s.modifiers,%d", sec[i], j);
+      if (!gov_modifier_types[j].must_use && sval[0] == '\0') {
+	modifiers[j] = -1;
+      } else {
+	int ival = find_gov_modifier_by_name(sval);
+
+        if (ival < 0) {
+          freelog(LOG_FATAL,
+		  "for %s, couldn't match gov modifier \"%s\" (%s)",
+		  sec[i], sval, filename);
+	  exit(EXIT_FAILURE);
+	} else if (gov_modifiers[ival].type != gov_modifier_types[j].id) {
+	  freelog(LOG_FATAL, "for %s, gov modifier \"%s\" is not of "
+		  "required compound type %s (%s)",
+		  sec[i], sval, gov_modifier_types[j].name,
+		  filename);
+	  exit(EXIT_FAILURE);
+	} else {
+	  modifiers[j] = ival;
+	}
+      }
+    }
+    form_compound_gov(NULL, name, basetype, modifiers);
+  }
+  free(sec);
+
+  game.default_government
+    = lookup_government(file, "governments", "default", filename);
+  
+  game.government_when_anarchy
+    = lookup_government(file, "governments", "when_anarchy", filename);
+  
+  game.ai_goal_government
+    = lookup_government(file, "governments", "ai_goal", filename);
+
+  freelog(LOG_DEBUG, "govs: def %d, anarchy %d, ai_goal %d",
+	  game.default_government, game.government_when_anarchy,
+	  game.ai_goal_government);
+  
+  /* Because player_init is called before rulesets are loaded we set
+   * all players governments here, if they have not been previously
+   * set (eg by loading game).
+   */
+  for(i=0; i<MAX_NUM_PLAYERS+MAX_NUM_BARBARIANS; i++) {
+    if (game.players[i].government == G_MAGIC) {
+      game.players[i].government = game.default_government;
+    }
+  }
+
     
   /* ai tech_hints: */
   j = -1;
@@ -1872,7 +1992,6 @@
     ai_gov_tech_hints[j].tech = A_LAST;
   }
 
-  free(sec);
   section_file_check_unused(file, filename);
   section_file_free(file);
 }
@@ -1910,6 +2029,11 @@
   packet.num_impr_types = game.num_impr_types;
   packet.num_tech_types = game.num_tech_types;
 
+  packet.num_unit_modifier_types = game.num_unit_modifier_types;
+  packet.num_unit_modifiers = game.num_unit_modifiers;
+  packet.num_gov_modifier_types = game.num_gov_modifier_types;
+  packet.num_gov_modifiers = game.num_gov_modifiers;
+
   packet.nation_count = game.nation_count;
   packet.playable_nation_count = game.playable_nation_count;
   packet.style_count = game.styles_count;
@@ -2579,53 +2703,140 @@
   section_file_free(&file);
 }
 
+static void send_ruleset_modifier_types(struct conn_list *dest, bool is_gov)
+{
+  struct packet_ruleset_modifier_type packet;
+  int i, num_mods;
+  struct modifier_type *modifier_types;
+
+  if (is_gov) {
+    num_mods = game.num_gov_modifier_types;
+    modifier_types = gov_modifier_types;
+  } else {
+    num_mods = game.num_unit_modifier_types;
+    modifier_types = unit_modifier_types;
+  }
+
+  for (i = 0; i < num_mods; ++i) {
+    struct modifier_type *u = &modifier_types[i];
+
+    packet.id = i;
+    packet.is_gov = is_gov;
+    packet.basetype = u->id;
+    sz_strlcpy(packet.name, u->name);
+    packet.tech_requirement = u->tech_requirement;
+    packet.must_use = u->must_use;
+    lsend_packet_ruleset_modifier_type(dest, &packet);
+  }
+}
+
+static void send_ruleset_unit_modifier_types(struct conn_list *dest)
+{
+  send_ruleset_modifier_types(dest, FALSE);
+}
+
+static void send_ruleset_gov_modifier_types(struct conn_list *dest)
+{
+  send_ruleset_modifier_types(dest, TRUE);
+}
+
+static void send_ruleset_modifiers(struct conn_list *dest, bool is_gov)
+{
+  struct packet_ruleset_modifier packet;
+  int i, num_mods;
+  struct modifier *modifiers;
+
+  if (is_gov) {
+    num_mods = game.num_gov_modifiers;
+    modifiers = gov_modifiers;
+  } else {
+    num_mods = game.num_unit_modifiers;
+    modifiers = unit_modifiers;
+  }
+
+  for (i = 0; i < num_mods; ++i) {
+    struct modifier *u = &modifiers[i];
+
+    packet.id = i;
+    packet.is_gov = is_gov;
+    sz_strlcpy(packet.name, u->name);
+    packet.type = u->type;
+    packet.tech_requirement = u->tech_requirement;
+    packet.effect = u->effect;       /* pointer assignment */
+    lsend_packet_ruleset_modifier(dest, &packet);
+  }
+}
+
+static void send_ruleset_unit_modifiers(struct conn_list *dest)
+{
+  send_ruleset_modifiers(dest, FALSE);
+}
+
+static void send_ruleset_gov_modifiers(struct conn_list *dest)
+{
+  send_ruleset_modifiers(dest, TRUE);
+}
+
 /**************************************************************************
   Send the units ruleset information (all individual units) to the
   specified connections.
 **************************************************************************/
 static void send_ruleset_units(struct conn_list *dest)
 {
-  struct packet_ruleset_unit packet;
-
   unit_type_iterate(utype_id) {
-    struct unit_type *u = get_unit_type(utype_id);
+    send_single_unit(dest, utype_id);
+  } unit_type_iterate_end;
+}
 
-    packet.id = u-unit_types;
-    sz_strlcpy(packet.name, u->name_orig);
-    sz_strlcpy(packet.sound_move, u->sound_move);
-    sz_strlcpy(packet.sound_move_alt, u->sound_move_alt);
-    sz_strlcpy(packet.sound_fight, u->sound_fight);
-    sz_strlcpy(packet.sound_fight_alt, u->sound_fight_alt);
-    sz_strlcpy(packet.graphic_str, u->graphic_str);
-    sz_strlcpy(packet.graphic_alt, u->graphic_alt);
-    packet.move_type = u->move_type;
-    packet.build_cost = u->build_cost;
-    packet.pop_cost = u->pop_cost;
-    packet.attack_strength = u->attack_strength;
-    packet.defense_strength = u->defense_strength;
-    packet.move_rate = u->move_rate;
-    packet.tech_requirement = u->tech_requirement;
-    packet.impr_requirement = u->impr_requirement;
-    packet.vision_range = u->vision_range;
-    packet.transport_capacity = u->transport_capacity;
-    packet.hp = u->hp;
-    packet.firepower = u->firepower;
-    packet.obsoleted_by = u->obsoleted_by;
-    packet.fuel = u->fuel;
-    packet.flags = u->flags;
-    packet.roles = u->roles;
-    packet.happy_cost = u->happy_cost;
-    packet.shield_cost = u->shield_cost;
-    packet.food_cost = u->food_cost;
-    packet.gold_cost = u->gold_cost;
-    packet.paratroopers_range = u->paratroopers_range;
-    packet.paratroopers_mr_req = u->paratroopers_mr_req;
-    packet.paratroopers_mr_sub = u->paratroopers_mr_sub;
-    packet.effect = u->effect;       /* pointer assignment */
-    packet.helptext = u->helptext;   /* pointer assignment */
+/**************************************************************************
+  Sends a single unit type to the player, to allow individual unit types
+  to be resent later during the game if the details change
+**************************************************************************/
+void send_single_unit(struct conn_list *dest, Unit_Type_id utype_id)
+{
+  struct packet_ruleset_unit packet;
+  struct unit_type *u = get_unit_type(utype_id);
+  int i;
 
-    lsend_packet_ruleset_unit(dest, &packet);
-  } unit_type_iterate_end;
+  packet.id = u-unit_types;
+  sz_strlcpy(packet.name, u->name_orig);
+  sz_strlcpy(packet.sound_move, u->sound_move);
+  sz_strlcpy(packet.sound_move_alt, u->sound_move_alt);
+  sz_strlcpy(packet.sound_fight, u->sound_fight);
+  sz_strlcpy(packet.sound_fight_alt, u->sound_fight_alt);
+  sz_strlcpy(packet.graphic_str, u->graphic_str);
+  sz_strlcpy(packet.graphic_alt, u->graphic_alt);
+  packet.move_type = u->move_type;
+  packet.build_cost = u->build_cost;
+  packet.pop_cost = u->pop_cost;
+  packet.attack_strength = u->attack_strength;
+  packet.defense_strength = u->defense_strength;
+  packet.move_rate = u->move_rate;
+  packet.tech_requirement = u->tech_requirement;
+  packet.impr_requirement = u->impr_requirement;
+  packet.vision_range = u->vision_range;
+  packet.transport_capacity = u->transport_capacity;
+  packet.hp = u->hp;
+  packet.firepower = u->firepower;
+  packet.obsoleted_by = u->obsoleted_by;
+  packet.fuel = u->fuel;
+  packet.flags = u->flags;
+  packet.roles = u->roles;
+  packet.happy_cost = u->happy_cost;
+  packet.shield_cost = u->shield_cost;
+  packet.food_cost = u->food_cost;
+  packet.gold_cost = u->gold_cost;
+  packet.paratroopers_range = u->paratroopers_range;
+  packet.paratroopers_mr_req = u->paratroopers_mr_req;
+  packet.paratroopers_mr_sub = u->paratroopers_mr_sub;
+  packet.basetype = u->basetype;
+  for (i = 0; i < MAX_MODS; ++i) {
+    packet.modifiers[i] = u->modifiers[i];
+  }
+  packet.effect = u->effect;       /* pointer assignment */
+  packet.helptext = u->helptext;   /* pointer assignment */
+
+  lsend_packet_ruleset_unit(dest, &packet);
 }
 
 /**************************************************************************
@@ -2760,92 +2971,109 @@
 **************************************************************************/
 static void send_ruleset_governments(struct conn_list *dest)
 {
+  int i;
+
+  for (i = 0; i < game.government_count; ++i) {
+    send_single_government(dest, i);
+  }
+}
+
+/**************************************************************************
+  Send a single government type to the connections - this allows portions
+  of the government ruleset to be resent to the clients as they change
+  during the course of the game.
+**************************************************************************/
+void send_single_government(struct conn_list *dest, int id)
+{
   struct packet_ruleset_government gov;
   struct packet_ruleset_government_ruler_title title;
   struct ruler_title *p_title;
   struct government *g;
-  int i, j;
+  int i;
 
-  for (i = 0; i < game.government_count; i++) {
-    g = &governments[i];
+  g = &governments[id];
 
-    /* send one packet_government */
-    gov.id                 = i;
+  /* send one packet_government */
+  gov.id                 = id;
 
-    gov.required_tech    = g->required_tech;
-    gov.max_rate         = g->max_rate;
-    gov.civil_war        = g->civil_war;
-    gov.martial_law_max  = g->martial_law_max;
-    gov.martial_law_per  = g->martial_law_per;
-    gov.empire_size_mod  = g->empire_size_mod;
-    gov.empire_size_inc  = g->empire_size_inc;
-    gov.rapture_size     = g->rapture_size;
-    
-    gov.unit_happy_cost_factor  = g->unit_happy_cost_factor;
-    gov.unit_shield_cost_factor = g->unit_shield_cost_factor;
-    gov.unit_food_cost_factor   = g->unit_food_cost_factor;
-    gov.unit_gold_cost_factor   = g->unit_gold_cost_factor;
-    
-    gov.free_happy  = g->free_happy;
-    gov.free_shield = g->free_shield;
-    gov.free_food   = g->free_food;
-    gov.free_gold   = g->free_gold;
-
-    gov.trade_before_penalty = g->trade_before_penalty;
-    gov.shields_before_penalty = g->shields_before_penalty;
-    gov.food_before_penalty = g->food_before_penalty;
-
-    gov.celeb_trade_before_penalty = g->celeb_trade_before_penalty;
-    gov.celeb_shields_before_penalty = g->celeb_shields_before_penalty;
-    gov.celeb_food_before_penalty = g->celeb_food_before_penalty;
-
-    gov.trade_bonus = g->trade_bonus;
-    gov.shield_bonus = g->shield_bonus;
-    gov.food_bonus = g->food_bonus;
-
-    gov.celeb_trade_bonus = g->celeb_trade_bonus;
-    gov.celeb_shield_bonus = g->celeb_shield_bonus;
-    gov.celeb_food_bonus = g->celeb_food_bonus;
-
-    gov.corruption_level = g->corruption_level;
-    gov.corruption_modifier = g->corruption_modifier;
-    gov.fixed_corruption_distance = g->fixed_corruption_distance;
-    gov.corruption_distance_factor = g->corruption_distance_factor;
-    gov.extra_corruption_distance = g->extra_corruption_distance;
-    gov.corruption_max_distance_cap = g->corruption_max_distance_cap;
-    
-    gov.waste_level = g->waste_level;
-    gov.waste_modifier = g->waste_modifier;
-    gov.fixed_waste_distance = g->fixed_waste_distance;
-    gov.waste_distance_factor = g->waste_distance_factor;
-    gov.extra_waste_distance = g->extra_waste_distance;
-    gov.waste_max_distance_cap = g->waste_max_distance_cap;
+  gov.required_tech    = g->required_tech;
+  gov.max_rate         = g->max_rate;
+  gov.civil_war        = g->civil_war;
+  gov.martial_law_max  = g->martial_law_max;
+  gov.martial_law_per  = g->martial_law_per;
+  gov.empire_size_mod  = g->empire_size_mod;
+  gov.empire_size_inc  = g->empire_size_inc;
+  gov.rapture_size     = g->rapture_size;
+
+  gov.basetype         = g->basetype;
+  for (i = 0; i < MAX_MODS; ++i) {
+    gov.modifiers[i]   = g->modifiers[i];
+  }
+    
+  gov.unit_happy_cost_factor  = g->unit_happy_cost_factor;
+  gov.unit_shield_cost_factor = g->unit_shield_cost_factor;
+  gov.unit_food_cost_factor   = g->unit_food_cost_factor;
+  gov.unit_gold_cost_factor   = g->unit_gold_cost_factor;
+    
+  gov.free_happy  = g->free_happy;
+  gov.free_shield = g->free_shield;
+  gov.free_food   = g->free_food;
+  gov.free_gold   = g->free_gold;
+
+  gov.trade_before_penalty = g->trade_before_penalty;
+  gov.shields_before_penalty = g->shields_before_penalty;
+  gov.food_before_penalty = g->food_before_penalty;
+
+  gov.celeb_trade_before_penalty = g->celeb_trade_before_penalty;
+  gov.celeb_shields_before_penalty = g->celeb_shields_before_penalty;
+  gov.celeb_food_before_penalty = g->celeb_food_before_penalty;
+
+  gov.trade_bonus = g->trade_bonus;
+  gov.shield_bonus = g->shield_bonus;
+  gov.food_bonus = g->food_bonus;
+
+  gov.celeb_trade_bonus = g->celeb_trade_bonus;
+  gov.celeb_shield_bonus = g->celeb_shield_bonus;
+  gov.celeb_food_bonus = g->celeb_food_bonus;
+
+  gov.corruption_level = g->corruption_level;
+  gov.corruption_modifier = g->corruption_modifier;
+  gov.fixed_corruption_distance = g->fixed_corruption_distance;
+  gov.corruption_distance_factor = g->corruption_distance_factor;
+  gov.extra_corruption_distance = g->extra_corruption_distance;
+  gov.corruption_max_distance_cap = g->corruption_max_distance_cap;
+    
+  gov.waste_level = g->waste_level;
+  gov.waste_modifier = g->waste_modifier;
+  gov.fixed_waste_distance = g->fixed_waste_distance;
+  gov.waste_distance_factor = g->waste_distance_factor;
+  gov.extra_waste_distance = g->extra_waste_distance;
+  gov.waste_max_distance_cap = g->waste_max_distance_cap;
         
-    gov.flags = g->flags;
-    gov.hints = g->hints;
-    gov.num_ruler_titles = g->num_ruler_titles;
-
-    sz_strlcpy(gov.name, g->name_orig);
-    sz_strlcpy(gov.graphic_str, g->graphic_str);
-    sz_strlcpy(gov.graphic_alt, g->graphic_alt);
+  gov.flags = g->flags;
+  gov.hints = g->hints;
+  gov.num_ruler_titles = g->num_ruler_titles;
+
+  sz_strlcpy(gov.name, g->name_orig);
+  sz_strlcpy(gov.graphic_str, g->graphic_str);
+  sz_strlcpy(gov.graphic_alt, g->graphic_alt);
     
-    gov.effect = g->effect;       /* pointer assignment */
-    gov.helptext = g->helptext;   /* pointer assignment */
+  gov.effect = g->effect;       /* pointer assignment */
+  gov.helptext = g->helptext;   /* pointer assignment */
       
-    lsend_packet_ruleset_government(dest, &gov);
+  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];
-
-      title.gov = i;
-      title.id = j;
-      title.nation = p_title->nation;
-      sz_strlcpy(title.male_title, p_title->male_title);
-      sz_strlcpy(title.female_title, p_title->female_title);
+  /* send one packet_government_ruler_title per ruler title */
+  for(i = 0; i < g->num_ruler_titles; i++) {
+    p_title = &g->ruler_titles[i];
+
+    title.gov = id;
+    title.id = i;
+    title.nation = p_title->nation;
+    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);
-    }
+    lsend_packet_ruleset_government_ruler_title(dest, &title);
   }
 }
 
@@ -2995,7 +3223,11 @@
   send_ruleset_game(dest);
   send_ruleset_techs(dest);
   send_ruleset_governments(dest);
+  send_ruleset_gov_modifier_types(dest);
+  send_ruleset_gov_modifiers(dest);
   send_ruleset_units(dest);
+  send_ruleset_unit_modifier_types(dest);
+  send_ruleset_unit_modifiers(dest);
   send_ruleset_terrain(dest);
   send_ruleset_buildings(dest);
   send_ruleset_nations(dest);
diff -Nur -Xfreecivdiff.ignore freeciv-cvs/server/ruleset.h freeciv-patched/server/ruleset.h
--- freeciv-cvs/server/ruleset.h	2003-01-03 16:09:50.000000000 +0000
+++ freeciv-patched/server/ruleset.h	2003-06-30 12:33:10.000000000 +0100
@@ -13,9 +13,14 @@
 #ifndef FC__RULESET_H
 #define FC__RULESET_H
 
+#include "unittype.h"
+
 struct conn_list;
 
 void load_rulesets(void);
 void send_rulesets(struct conn_list *dest);
 
+void send_single_unit(struct conn_list *dest, Unit_Type_id utype_id);
+void send_single_government(struct conn_list *dest, int id);
+
 #endif  /* FC__RULESET_H */
diff -Nur -Xfreecivdiff.ignore freeciv-cvs/server/savegame.c freeciv-patched/server/savegame.c
--- freeciv-cvs/server/savegame.c	2003-06-30 12:33:44.000000000 +0100
+++ freeciv-patched/server/savegame.c	2003-06-30 12:33:10.000000000 +0100
@@ -24,6 +24,7 @@
 #include "city.h"
 #include "fcintl.h"
 #include "game.h"
+#include "government.h"
 #include "idex.h"
 #include "log.h"
 #include "map.h"
@@ -1813,6 +1814,72 @@
 /***************************************************************
 ...
 ***************************************************************/
+static void govs_load(struct section_file *file)
+{
+  int numcomps, compind, govind;
+
+  numcomps = secfile_lookup_int_default(file, 0, "game.num_compound_govs");
+
+  /* Truncate unit list at the first generated compound gov */
+  for (govind = 0; govind < game.government_count; ++govind) {
+    if (governments[govind].basetype != G_MAGIC) {
+      game.government_count = govind;
+      freelog(LOG_DEBUG, "govs truncated to %d", govind);
+      governments = (struct government *)fc_realloc(governments,
+			game.government_count * sizeof(struct government));
+      break;
+    }
+  }
+  for (compind = 0; compind < numcomps; ++compind) {
+    int modind, modifiers[MAX_MODS];
+    int basetype;
+    char *name;
+
+    name = secfile_lookup_str(file, "game.cgov%d.name", compind);
+    basetype = secfile_lookup_int(file, "game.cgov%d.basetype", compind);
+    for (modind = 0; modind < game.num_gov_modifier_types; ++modind) {
+      modifiers[modind] = secfile_lookup_int(file, "game.cgov%d.modifiers%d",
+	  				     compind, modind);
+    }
+    form_compound_gov(NULL, name, basetype, modifiers);
+  }
+}
+
+/***************************************************************
+...
+***************************************************************/
+static void units_load(struct section_file *file)
+{
+  int numcomps, compind;
+
+  numcomps = secfile_lookup_int_default(file, 0, "game.num_compound_units");
+
+  /* Truncate unit list at the first generated compound unit */
+  unit_type_iterate(uid) {
+    if (unit_types[uid].basetype != U_LAST) {
+      game.num_unit_types = uid;
+      freelog(LOG_DEBUG, "units truncated to %d", uid);
+      break;
+    }
+  } unit_type_iterate_end;
+  for (compind = 0; compind < numcomps; ++compind) {
+    int modind, modifiers[MAX_MODS];
+    Unit_Type_id basetype;
+    char *name;
+
+    name = secfile_lookup_str(file, "game.cunit%d.name", compind);
+    basetype = secfile_lookup_int(file, "game.cunit%d.basetype", compind);
+    for (modind = 0; modind < game.num_unit_modifier_types; ++modind) {
+      modifiers[modind] = secfile_lookup_int(file, "game.cunit%d.modifiers%d",
+	  				     compind, modind);
+    }
+    form_compound_unit(name, basetype, modifiers);
+  }
+}
+
+/***************************************************************
+...
+***************************************************************/
 void game_load(struct section_file *file)
 {
   int i;
@@ -2041,6 +2108,11 @@
     game.cooling=0;
 
     load_rulesets();
+
+    /* This needs to be after rulesets are loaded so that we can access
+     * the base types for generating compound units */
+    units_load(file);
+    govs_load(file);
   }
 
   {
@@ -2229,6 +2301,70 @@
 /***************************************************************
 ...
 ***************************************************************/
+static void govs_save(struct section_file *file)
+{
+  bool write = FALSE;
+  int govind, compind = -1;
+
+  for (govind = 0; govind < game.government_count; ++govind) {
+    struct government *g = &governments[govind];
+
+    if (!write && g->basetype != G_MAGIC) {
+      write = TRUE;
+      secfile_insert_int(file, game.government_count - govind,
+	  		 "game.num_compound_govs");
+    }
+    if (write) {
+      int modind;
+
+      compind++;
+      secfile_insert_str(file, g->name, "game.cgov%d.name", compind);
+      secfile_insert_int(file, g->basetype, "game.cgov%d.basetype",
+	  		 compind);
+      for (modind = 0; modind < game.num_gov_modifier_types; ++modind) {
+	secfile_insert_int(file, g->modifiers[modind],
+	    		   "game.cgov%d.modifiers%d",
+	    		   compind, modind);
+      }
+    }
+  }
+}
+
+/***************************************************************
+...
+***************************************************************/
+static void units_save(struct section_file *file)
+{
+  bool write = FALSE;
+  int compind = -1;
+
+  unit_type_iterate(uid) {
+    struct unit_type *u = &unit_types[uid];
+
+    if (!write && u->basetype != U_LAST) {
+      write = TRUE;
+      secfile_insert_int(file, game.num_unit_types - uid,
+	  		 "game.num_compound_units");
+    }
+    if (write) {
+      int modind;
+
+      compind++;
+      secfile_insert_str(file, u->name, "game.cunit%d.name", compind);
+      secfile_insert_int(file, u->basetype, "game.cunit%d.basetype",
+	  		 compind);
+      for (modind = 0; modind < game.num_unit_modifier_types; ++modind) {
+	secfile_insert_int(file, u->modifiers[modind],
+	    		   "game.cunit%d.modifiers%d",
+	    		   compind, modind);
+      }
+    }
+  } unit_type_iterate_end;
+}
+
+/***************************************************************
+...
+***************************************************************/
 void game_save(struct section_file *file)
 {
   int i;
@@ -2320,6 +2456,9 @@
   secfile_insert_int(file, game.watchtower_extra_vision, "game.watchtower_extra_vision");
   secfile_insert_int(file, game.allowed_city_names, "game.allowed_city_names");
 
+  units_save(file);
+  govs_save(file);
+
   if (TRUE) {
     /* Now always save these, so the server options reflect the
      * actual values used at the start of the game.
diff -Nur -Xfreecivdiff.ignore freeciv-cvs/server/srv_main.c freeciv-patched/server/srv_main.c
--- freeciv-cvs/server/srv_main.c	2003-06-30 12:33:44.000000000 +0100
+++ freeciv-patched/server/srv_main.c	2003-06-30 12:33:10.000000000 +0100
@@ -833,6 +833,10 @@
     handle_player_tech_goal(pplayer, (struct packet_player_request *)packet);
     break;
 
+  case PACKET_REQUEST_COMPOUND:
+    handle_request_compound(pplayer, (struct packet_request_compound *)packet);
+    break;
+
   case PACKET_UNIT_BUILD_CITY:
     handle_unit_build_city(pplayer, (struct packet_unit_request *)packet);
     break;
