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-07-07 18:21:48.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-07-07 18:21:48.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-07-07 18:21:48.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-07-01 12:34:18.000000000 +0100
+++ freeciv-patched/client/gui-gtk-2.0/dialogs.c	2003-07-07 18:21:48.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-07-07 18:21:48.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/helpdata.c freeciv-patched/client/helpdata.c
--- freeciv-cvs/client/helpdata.c	2003-07-07 18:20:26.000000000 +0100
+++ freeciv-patched/client/helpdata.c	2003-07-07 18:22:00.000000000 +0100
@@ -714,9 +714,13 @@
 	strcat(astr.str, deli_str);
       }
     }
-    
-    assert(num_allowed_units > 0);
 
+    if (num_allowed_units == 0) {
+      char *deli_str = _("carrier unit");
+      astr_minsize(&astr, strlen(deli_str));
+      strcpy(astr.str, deli_str);
+    }
+    
     sprintf(buf + strlen(buf),
 	    PL_("* Unit has to be in a city, or on a %s"
 		" after %d turn.\n",
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-07-07 18:21:48.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/Makefile.am freeciv-patched/client/Makefile.am
--- freeciv-cvs/client/Makefile.am	2003-06-07 19:58:19.000000000 +0100
+++ freeciv-patched/client/Makefile.am	2003-07-07 18:21:48.000000000 +0100
@@ -185,4 +185,4 @@
 civclient_LDADD        = @gui_sources@/libguiclient.a \
 	@INTLLIBS@ ../common/aicore/libaicore.a \
         ../common/libcivcommon.a agents/libagents.a \
-	@CLIENT_LIBS@ @SOUND_LIBS@
+	@CLIENT_LIBS@ @SOUND_LIBS@ -lslang-utf8
diff -Nur -Xfreecivdiff.ignore freeciv-cvs/client/packhand.c freeciv-patched/client/packhand.c
--- freeciv-cvs/client/packhand.c	2003-07-07 18:20:26.000000000 +0100
+++ freeciv-patched/client/packhand.c	2003-07-07 18:21:48.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-07-07 18:21:49.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-07-07 18:20:26.000000000 +0100
+++ freeciv-patched/common/capstr.c	2003-07-07 18:21:49.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-07-07 18:20:26.000000000 +0100
+++ freeciv-patched/common/game.h	2003-07-07 18:21:49.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-07-07 18:20:26.000000000 +0100
+++ freeciv-patched/common/government.c	2003-07-07 18:21:49.000000000 +0100
@@ -19,10 +19,12 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include "fcintl.h"
 #include "game.h"
 #include "log.h"
 #include "mem.h"
 #include "player.h"
+#include "script.h"
 #include "shared.h"
 #include "support.h"
 #include "tech.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"
@@ -142,6 +146,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;
+}
+
 /***************************************************************
 ...
 ***************************************************************/
@@ -324,3 +347,95 @@
   governments = NULL;
   game.government_count = 0;
 }
+
+/***************************************************************
+...
+***************************************************************/
+static void copy_government(struct government *dest, struct government *src)
+{
+  int i;
+
+  memcpy(dest, src, sizeof(struct government));
+
+  dest->effect = NULL;
+  append_effects(&dest->effect, src->effect);
+
+  dest->num_ruler_titles = src->num_ruler_titles;
+  dest->ruler_titles = fc_calloc(dest->num_ruler_titles,
+      				 sizeof(struct ruler_title));
+  for (i = 0; i < dest->num_ruler_titles; i++) {
+    struct ruler_title *fromtitle = &(src->ruler_titles[i]);
+    struct ruler_title *totitle = &(dest->ruler_titles[i]);
+
+    totitle->nation = fromtitle->nation;
+    sz_strlcpy(totitle->male_title, fromtitle->male_title);
+    sz_strlcpy(totitle->female_title, fromtitle->female_title);
+  }
+
+  dest->helptext = src->helptext ? mystrdup(src->helptext) : NULL;
+}
+
+/***************************************************************
+...
+***************************************************************/
+int form_compound_gov(struct player *pplayer, const char *name,
+    		      const int basetype, const int modifiers[MAX_MODS])
+{
+  struct government *gov = NULL, *base;
+  struct modifier *mod;
+  char buffer[200];
+  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];
+  } else if (pplayer) {
+    government_free(gov);
+  }
+  base = &governments[basetype];
+  copy_government(gov, base);
+  gov->index = id;
+  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';
+  my_snprintf(buffer, sizeof(buffer),
+	      "variable gt = get_gov(%d);", id);
+  script_execute(buffer);
+  for (i = 0; i < game.num_gov_modifier_types; ++i) {
+    if (gov->modifiers[i] < U_LAST) {
+      mod = &gov_modifiers[gov->modifiers[i]];
+      script_execute(mod->action);
+    }
+  }
+  for (i = 0; i < game.num_gov_modifier_types; ++i) {
+    if (gov->modifiers[i] < U_LAST) {
+      mod = &gov_modifiers[gov->modifiers[i]];
+      script_execute(mod->final_action);
+      append_effects(&gov->effect, mod->effect);
+    }
+  }
+  my_snprintf(buffer, sizeof(buffer),
+	      "set_gov(%d, gt);", id);
+  script_execute(buffer);
+  if (name && name[0]) {
+    sz_strlcpy(gov->name_orig, name);
+    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-07-07 18:20:26.000000000 +0100
+++ freeciv-patched/common/government.h	2003-07-07 18:21:49.000000000 +0100
@@ -14,6 +14,7 @@
 #define FC__GOVERNMENT_H
 
 #include "shared.h"
+#include "modifier.h"
 
 struct city;
 struct player;
@@ -39,6 +40,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 +158,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 +177,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 +189,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,6 +210,8 @@
                      const char *male, const char *female);
 void governments_alloc(int num);
 void governments_free(void);
+int form_compound_gov(struct player *pplayer, const char *name,
+    		      const int basetype, const int modifiers[MAX_MODS]);
 
 #define government_iterate(gov)                                             \
 {                                                                           \
diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/improvement.c freeciv-patched/common/improvement.c
--- freeciv-cvs/common/improvement.c	2003-07-07 18:20:26.000000000 +0100
+++ freeciv-patched/common/improvement.c	2003-07-07 18:21:49.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-07-07 18:20:26.000000000 +0100
+++ freeciv-patched/common/improvement.h	2003-07-07 18:21:49.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-07-07 18:21:49.000000000 +0100
@@ -63,12 +63,16 @@
 		pqueue.h	\
 		mem.c           \
 		mem.h		\
+		modifier.c	\
+		modifier.h	\
 		rand.c		\
 		rand.h		\
 		registry.c	\
 		registry.h	\
 		sbuffer.c	\
 		sbuffer.h	\
+		script.c	\
+		script.h	\
 		shared.c	\
 		shared.h	\
 		spaceship.c	\
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-07-07 18:21:49.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-07-07 18:21:49.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-07-07 18:20:26.000000000 +0100
+++ freeciv-patched/common/packets.c	2003-07-07 18:21:49.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-07-07 18:20:26.000000000 +0100
+++ freeciv-patched/common/packets.h	2003-07-07 18:21:49.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-07-07 18:21:49.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-07-07 18:21:49.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/script.c freeciv-patched/common/script.c
--- freeciv-cvs/common/script.c	1970-01-01 01:00:00.000000000 +0100
+++ freeciv-patched/common/script.c	2003-07-07 18:21:49.000000000 +0100
@@ -0,0 +1,346 @@
+/********************************************************************** 
+ 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 <assert.h>
+#include <string.h>
+#include <slang/slang.h>
+
+#include "fcintl.h"
+#include "game.h"
+#include "government.h"
+#include "log.h"
+#include "shared.h"
+#include "support.h"
+#include "unit.h"
+
+#include "script.h"
+
+static void ut_set_name(int *id, char *name)
+{
+  struct unit_type *ut = get_unit_type(*id);
+  sz_strlcpy(ut->name_orig, name);
+  sz_strlcpy(ut->name, _(name));
+}
+
+static void ut_set_graphic(int *id, char *name, char *name_alt)
+{
+  struct unit_type *ut = get_unit_type(*id);
+  sz_strlcpy(ut->graphic_str, name);
+  sz_strlcpy(ut->graphic_alt, name_alt);
+}
+
+static void ut_set_move_rate(int *id, int *move_rate)
+{
+  struct unit_type *ut = get_unit_type(*id);
+  ut->move_rate = *move_rate * SINGLE_MOVE;
+}
+
+struct members {
+  char *name;
+  unsigned long offset;
+};
+
+#define FC_UT_MEMBER(member, name) \
+  {#name, ((unsigned long) ((char *) &((struct unit_type *)0)->member))}
+
+#define FC_MEMBER_P(struct_p, offset) \
+  ((int *) ((char *)(struct_p) + offset))
+
+static struct members ut_members[] =
+{
+  FC_UT_MEMBER(move_type, move_type),
+  FC_UT_MEMBER(build_cost, build_cost),
+  FC_UT_MEMBER(pop_cost, pop_cost),
+  FC_UT_MEMBER(attack_strength, attack),
+  FC_UT_MEMBER(defense_strength, defense),
+  FC_UT_MEMBER(vision_range, vision_range),
+  FC_UT_MEMBER(transport_capacity, transport_cap),
+  FC_UT_MEMBER(hp, hitpoints),
+  FC_UT_MEMBER(firepower, firepower),
+  FC_UT_MEMBER(fuel, fuel),
+  FC_UT_MEMBER(happy_cost, uk_happy),
+  FC_UT_MEMBER(shield_cost, uk_shield),
+  FC_UT_MEMBER(food_cost, uk_food),
+  FC_UT_MEMBER(gold_cost, uk_gold)
+};
+#define num_ut_members (sizeof(ut_members) / sizeof(ut_members[0]))
+
+static void ut_get(int *id)
+{
+  char *field_names[num_ut_members + 6];
+  unsigned char field_types[num_ut_members + 6];
+  VOID_STAR field_values[num_ut_members + 6];
+  struct unit_type *ut = get_unit_type(*id);
+  int move_rate = ut->move_rate / SINGLE_MOVE;
+  char *name = ut->name_orig;
+  char *graphic = ut->graphic_str, *graphic_alt = ut->graphic_alt;
+  int i;
+
+  for (i = 0; i < num_ut_members; i++) {
+    field_names[i] = ut_members[i].name;
+    field_types[i] = SLANG_INT_TYPE;
+    field_values[i] = FC_MEMBER_P(ut, ut_members[i].offset);
+  }
+
+  field_names[i] = "name";
+  field_types[i] = SLANG_STRING_TYPE;
+  field_values[i++] = &name;
+
+  field_names[i] = "graphic";
+  field_types[i] = SLANG_STRING_TYPE;
+  field_values[i++] = &graphic;
+
+  field_names[i] = "graphic_alt";
+  field_types[i] = SLANG_STRING_TYPE;
+  field_values[i++] = &graphic_alt;
+
+  field_names[i] = "move_rate";
+  field_types[i] = SLANG_INT_TYPE;
+  field_values[i++] = &move_rate;
+
+  field_names[i] = "flags";
+  field_types[i] = SLANG_NULL_TYPE;
+  field_values[i++] = NULL;
+
+  field_names[i] = "roles";
+  field_types[i] = SLANG_NULL_TYPE;
+  field_values[i++] = NULL;
+
+  SLstruct_create_struct(i, field_names, field_types, field_values);
+}
+
+static void ut_set_ints(void)
+{
+  int i, id;
+  unsigned int num = SLang_Num_Function_Args;
+  struct unit_type *ut;
+
+  assert(num == num_ut_members + 1);
+
+  SLang_pop_integer(&id);
+  ut = get_unit_type(id);
+
+  for (i = num_ut_members - 1; i >= 0; i--) {
+    int val, *pt;
+    SLang_pop_integer(&val);
+    pt = FC_MEMBER_P(ut, ut_members[i].offset);
+    *pt = val;
+  }
+}
+
+static void gt_set_name(int *id, char *name)
+{
+  struct government *gov = get_government(*id);
+  sz_strlcpy(gov->name_orig, name);
+  sz_strlcpy(gov->name, _(name));
+}
+
+#define FC_GT_MEMBER(member, name) \
+  {#name, ((unsigned long) ((char *) &((struct government *)0)->member))}
+
+static struct members gt_members[] =
+{
+  FC_GT_MEMBER(max_rate, max_single_rate),
+  FC_GT_MEMBER(civil_war, civil_war_chance),
+  FC_GT_MEMBER(free_happy, unit_free_unhappy),
+  FC_GT_MEMBER(free_shield, unit_free_shield),
+  FC_GT_MEMBER(free_food, unit_free_food),
+  FC_GT_MEMBER(free_gold, unit_free_gold)
+};
+#define num_gt_members (sizeof(gt_members) / sizeof(gt_members[0]))
+
+static void gt_get(int *id)
+{
+  char *field_names[num_gt_members + 3];
+  unsigned char field_types[num_gt_members + 3];
+  VOID_STAR field_values[num_gt_members + 3];
+  struct government *gov = get_government(*id);
+  char *name = gov->name_orig;
+  char *graphic = gov->graphic_str, *graphic_alt = gov->graphic_alt;
+  int i;
+
+  for (i = 0; i < num_gt_members; i++) {
+    field_names[i] = gt_members[i].name;
+    field_types[i] = SLANG_INT_TYPE;
+    field_values[i] = FC_MEMBER_P(gov, gt_members[i].offset);
+  }
+
+  field_names[i] = "name";
+  field_types[i] = SLANG_STRING_TYPE;
+  field_values[i++] = &name;
+
+  field_names[i] = "graphic";
+  field_types[i] = SLANG_STRING_TYPE;
+  field_values[i++] = &graphic;
+
+  field_names[i] = "graphic_alt";
+  field_types[i] = SLANG_STRING_TYPE;
+  field_values[i++] = &graphic_alt;
+
+  SLstruct_create_struct(i, field_names, field_types, field_values);
+}
+
+static void sl_freelog(char *str)
+{
+  freelog(LOG_NORMAL, "%s", str);
+}
+
+static void ut_get_flags(int *id)
+{
+  int i, dims[1] = {F_LAST};
+  SLang_Array_Type *arr;
+
+  arr = SLang_create_array(SLANG_INT_TYPE, 0, NULL, dims, 1);
+
+  for (i = 0; i < F_LAST; i++) {
+    int val = unit_type_flag(*id, i) ? 1 : 0;
+    dims[0] = i;
+    SLang_set_array_element(arr, dims, &val);
+  }
+
+  SLang_push_array(arr, 1);
+}
+
+static void ut_set_flags(void)
+{
+  SLang_Array_Type *arr;
+  int id, i;
+  struct unit_type *ut;
+
+  SLang_pop_array_of_type(&arr, SLANG_INT_TYPE);
+  assert(arr->num_dims == 1 && arr->dims[0] == F_LAST);
+
+  SLang_pop_integer(&id);
+  ut = get_unit_type(id);
+  BV_CLR_ALL(ut->flags);
+  for (i = 0; i < F_LAST; i++) {
+    int val;
+    SLang_get_array_element(arr, &i, &val);
+    if (val) {
+      BV_SET(ut->flags, i);
+    }
+  }
+  SLang_free_array(arr);
+}
+
+static void ut_get_roles(int *id)
+{
+  int i, dims[1] = {L_LAST - L_FIRST};
+  SLang_Array_Type *arr;
+
+  arr = SLang_create_array(SLANG_INT_TYPE, 0, NULL, dims, 1);
+
+  for (i = L_FIRST; i < L_LAST; i++) {
+    int val = unit_has_role(*id, i) ? 1 : 0;
+    dims[0] = i - L_FIRST;
+    SLang_set_array_element(arr, dims, &val);
+  }
+
+  SLang_push_array(arr, 1);
+}
+
+static void ut_set_roles(void)
+{
+  SLang_Array_Type *arr;
+  int id, i;
+  struct unit_type *ut;
+
+  SLang_pop_array_of_type(&arr, SLANG_INT_TYPE);
+  assert(arr->num_dims == 1 && arr->dims[0] == L_LAST - L_FIRST);
+
+  SLang_pop_integer(&id);
+  ut = get_unit_type(id);
+  BV_CLR_ALL(ut->roles);
+  for (i = 0; i < L_LAST - L_FIRST; i++) {
+    int val;
+    SLang_get_array_element(arr, &i, &val);
+    if (val) {
+      BV_SET(ut->roles, i);
+    }
+  }
+  SLang_free_array(arr);
+}
+
+static void register_intrinsics(void)
+{
+  char buffer[1024];
+  static SLang_Intrin_Fun_Type intrins[] =
+  {
+    MAKE_INTRINSIC_I("ut_get", ut_get, SLANG_VOID_TYPE),
+    MAKE_INTRINSIC_IS("ut_set_name", ut_set_name, SLANG_VOID_TYPE),
+    MAKE_INTRINSIC_ISS("ut_set_graphic", ut_set_graphic, SLANG_VOID_TYPE),
+    MAKE_INTRINSIC_II("ut_set_move_rate", ut_set_move_rate, SLANG_VOID_TYPE),
+    MAKE_INTRINSIC_0("ut_set_flags", ut_set_flags, SLANG_VOID_TYPE),
+    MAKE_INTRINSIC_I("ut_get_flags", ut_get_flags, SLANG_VOID_TYPE),
+    MAKE_INTRINSIC_0("ut_set_roles", ut_set_roles, SLANG_VOID_TYPE),
+    MAKE_INTRINSIC_I("ut_get_roles", ut_get_roles, SLANG_VOID_TYPE),
+    MAKE_INTRINSIC_0("ut_set_ints", ut_set_ints, SLANG_VOID_TYPE),
+    MAKE_INTRINSIC_S("unit_flag_from_str", unit_flag_from_str, SLANG_INT_TYPE),
+    MAKE_INTRINSIC_S("unit_role_from_str", unit_role_from_str, SLANG_INT_TYPE),
+    MAKE_INTRINSIC_I("gt_get", gt_get, SLANG_VOID_TYPE),
+    MAKE_INTRINSIC_IS("gt_set_name", gt_set_name, SLANG_VOID_TYPE),
+    MAKE_INTRINSIC_S("freelog", sl_freelog, SLANG_VOID_TYPE),
+    SLANG_END_INTRIN_FUN_TABLE
+  };
+  SLadd_intrin_fun_table(intrins, NULL);
+  my_snprintf(buffer, sizeof(buffer),
+              "variable Land = %d, Sea = %d, Heli = %d, Air = %d;",
+	      LAND_MOVING, SEA_MOVING, HELI_MOVING, AIR_MOVING);
+  SLang_load_string(buffer);
+  my_snprintf(buffer, sizeof(buffer),
+              "variable F_LAST = %d, L_LAST = %d, L_FIRST = %d;",
+	      F_LAST, L_LAST, L_FIRST);
+  SLang_load_string(buffer);
+}
+
+void script_test(void)
+{
+  SLang_load_file("test.sl");
+}
+
+void script_execute(char *cmds)
+{
+  if (cmds) {
+    if (SLang_load_string(cmds) == -1) {
+      SLang_restart(1);
+      SLang_Error = 0;
+    }
+  }
+}
+
+void script_load_library(void)
+{
+  char filename[512], *dfilename;
+
+  my_snprintf(filename, sizeof(filename), "%s/library.sl", game.rulesetdir);
+  dfilename = datafilename(filename);
+  if (dfilename) {
+    if (SLang_load_file(dfilename) == -1) {
+      SLang_restart(1);
+      SLang_Error = 0;
+    }
+  }
+}
+
+void script_init(void)
+{
+  if (SLang_init_slang() == -1) {
+    freelog(LOG_ERROR, "SLang init error");
+  } else {
+    register_intrinsics();
+  }
+}
diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/script.h freeciv-patched/common/script.h
--- freeciv-cvs/common/script.h	1970-01-01 01:00:00.000000000 +0100
+++ freeciv-patched/common/script.h	2003-07-07 18:21:49.000000000 +0100
@@ -0,0 +1,21 @@
+/********************************************************************** 
+ 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__SCRIPT_H
+#define FC__SCRIPT_H
+
+void script_init(void);
+void script_load_library(void);
+void script_test(void);
+void script_execute(char *cmds);
+
+#endif /* FC__SCRIPT_H */
diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/unittype.c freeciv-patched/common/unittype.c
--- freeciv-cvs/common/unittype.c	2003-07-07 18:20:26.000000000 +0100
+++ freeciv-patched/common/unittype.c	2003-07-07 18:21:49.000000000 +0100
@@ -22,8 +22,11 @@
 #include "fcintl.h"
 #include "game.h"
 #include "government.h"
+#include "log.h"
 #include "mem.h"
+#include "modifier.h"
 #include "player.h"
+#include "script.h"
 #include "shared.h"
 #include "support.h"
 #include "tech.h"
@@ -31,6 +34,8 @@
 #include "unittype.h"
 
 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,7 +57,7 @@
   "DefendOk", "DefendGood", "AttackFast", "AttackStrong",
   "Ferryboat", "Barbarian", "BarbarianTech", "BarbarianBoat",
   "BarbarianBuild", "BarbarianBuildTech", "BarbarianLeader",
-  "BarbarianSea", "BarbarianSeaTech"
+  "BarbarianSea", "BarbarianSeaTech", "CanModify"
 };
 
 /**************************************************************************
@@ -379,6 +384,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 +475,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. */
@@ -643,6 +683,12 @@
 
   free(p->helptext);
   p->helptext = NULL;
+
+  free(p->action);
+  p->action = NULL;
+
+  free(p->final_action);
+  p->final_action = NULL;
 }
 
 /***************************************************************
@@ -654,3 +700,132 @@
     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;
+  char newname[MAX_LEN_NAME];
+  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';
+  }
+
+  sz_strlcpy(newname, utype->name);
+  utype->name[0] = '\0'; /* Prevent this unittype from matching */
+  while (find_unit_type_by_name(newname) != U_LAST) {
+    my_snprintf(newname, sizeof(newname), _("%s Mk%d"), tmpname, mark);
+    my_snprintf(utype->name_orig, sizeof(utype->name_orig), "%s Mk%d",
+                tmporig, mark);
+    assert(mark < 100);
+    mark++;
+  };
+  sz_strlcpy(utype->name, newname);
+
+  free(tmpname);
+  free(tmporig);
+}
+
+/**************************************************************************
+  ...
+**************************************************************************/
+static void copy_unit_type(struct unit_type *dest, struct unit_type *src)
+{
+  memcpy(dest, src, sizeof(struct unit_type));
+  dest->effect = NULL;
+  append_effects(&dest->effect, src->effect);
+  dest->helptext = src->helptext ? mystrdup(src->helptext) : NULL;
+  dest->action = dest->final_action = NULL;
+}
+
+/**************************************************************************
+  ...
+**************************************************************************/
+Unit_Type_id form_compound_unit(const char *name,
+    				const Unit_Type_id basetype,
+				const int modifiers[MAX_MODS],
+				char *final_action)
+{
+  Unit_Type_id id;
+  struct unit_type *u, *base;
+  struct modifier *mod;
+  char buffer[200];
+  int i;
+
+  id = game.num_unit_types;
+  if (id >= U_LAST) {
+    freelog(LOG_FATAL, "form_compound_unit: out of unit types");
+    exit(EXIT_FAILURE);
+  }
+  game.num_unit_types++;
+  u = &unit_types[id];
+  base = &unit_types[basetype];
+  copy_unit_type(u, base);
+  u->basetype = basetype;
+  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';
+  my_snprintf(buffer, sizeof(buffer),
+	      "variable ut = get_unit_type(%d);", id);
+  script_execute(buffer);
+  script_execute(base->action);
+  for (i = 0; i < game.num_unit_modifier_types; ++i) {
+    if (u->modifiers[i] < U_LAST) {
+      mod = &unit_modifiers[u->modifiers[i]];
+      script_execute(mod->action);
+    }
+  }
+  for (i = 0; i < game.num_unit_modifier_types; ++i) {
+    if (u->modifiers[i] < U_LAST) {
+      mod = &unit_modifiers[u->modifiers[i]];
+      script_execute(mod->final_action);
+      append_effects(&u->effect, mod->effect);
+    }
+  }
+  script_execute(base->final_action);
+  if (final_action) {
+    script_execute(final_action);
+  }
+  my_snprintf(buffer, sizeof(buffer),
+	      "set_unit_type(%d, ut);", id);
+  script_execute(buffer);
+  /* 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);
+  return id;
+}
diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/unittype.h freeciv-patched/common/unittype.h
--- freeciv-cvs/common/unittype.h	2003-07-07 18:20:26.000000000 +0100
+++ freeciv-patched/common/unittype.h	2003-07-07 18:21:49.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,10 @@
 
   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 *action, *final_action;
+
   char *helptext;
 };
 
@@ -188,6 +197,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 +235,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 +262,11 @@
   }                                                                           \
 }
 
+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],
+				char *final_action);
 #endif  /* FC__UNITTYPE_H */
diff -Nur -Xfreecivdiff.ignore freeciv-cvs/server/Makefile.am freeciv-patched/server/Makefile.am
--- freeciv-cvs/server/Makefile.am	2003-06-07 19:58:48.000000000 +0100
+++ freeciv-patched/server/Makefile.am	2003-07-07 18:21:49.000000000 +0100
@@ -72,4 +72,4 @@
 
 
 civserver_DEPENDENCIES = ../common/libcivcommon.a ../ai/libcivai.a ./libcivserver.a ../common/aicore/libaicore.a
-civserver_LDADD        = ../common/libcivcommon.a ../ai/libcivai.a ./libcivserver.a @INTLLIBS@ ../common/libcivcommon.a ../ai/libcivai.a ./libcivserver.a ../common/aicore/libaicore.a $(SERVER_LIBS)
+civserver_LDADD        = ../common/libcivcommon.a ../ai/libcivai.a ./libcivserver.a @INTLLIBS@ ../common/libcivcommon.a ../ai/libcivai.a ./libcivserver.a ../common/aicore/libaicore.a $(SERVER_LIBS) -lslang-utf8
diff -Nur -Xfreecivdiff.ignore freeciv-cvs/server/plrhand.c freeciv-patched/server/plrhand.c
--- freeciv-cvs/server/plrhand.c	2003-07-07 18:20:27.000000000 +0100
+++ freeciv-patched/server/plrhand.c	2003-07-07 18:21:49.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"
@@ -744,26 +745,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 */
@@ -785,6 +776,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) &&
@@ -846,6 +855,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, NULL);
+    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-07-07 18:21:49.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-07-07 18:20:27.000000000 +0100
+++ freeciv-patched/server/ruleset.c	2003-07-07 18:21:49.000000000 +0100
@@ -31,10 +31,12 @@
 #include "nation.h"
 #include "packets.h"
 #include "registry.h"
+#include "script.h"
 #include "shared.h"
 #include "support.h"
 #include "tech.h"
 #include "unit.h"
+#include "unittype.h"
 
 #include "citytools.h"
 
@@ -63,8 +65,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 +98,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 +353,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 +790,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,6 +923,92 @@
 /**************************************************************************
 ...  
 **************************************************************************/
+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;
@@ -911,6 +1020,8 @@
   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,20 +1034,10 @@
 				      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;
 
     u->impr_requirement =
       find_improvement_by_name(secfile_lookup_str_default(file, "None", 
@@ -1003,6 +1104,10 @@
     u->effect = load_effects(sec[i], u->name, file, TRUE, FALSE);
 
     u->helptext = lookup_helptext(file, sec[i]);
+    u->action = secfile_lookup_str_default(file, NULL, "%s.action",
+					   sec[i]);
+    u->final_action = secfile_lookup_str_default(file, NULL,
+						 "%s.final_action", sec[i]);
   } unit_type_iterate_end;
   
   /* flags */
@@ -1062,10 +1167,67 @@
     }
     free(slist);
   } unit_type_iterate_end;
+  free(sec);
 
   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;
+
+    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 {
+	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;
+	}
+      }
+    }
+    sval = secfile_lookup_str(file, "%s.action", sec[i]);
+    form_compound_unit(name, basetype, modifiers, sval);
+  }
+  free(sec);
+
+  sec = secfile_get_secnames_prefix(file, "unit_", &nval);
+  for (i = 0; i < nval; 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;
+    }
+  }
+  free(sec);
+
   /* Some more consistency checking: */
   unit_type_iterate(i) {
     if (unit_type_exists(i)) {
@@ -1170,7 +1332,6 @@
 
   update_simple_ai_types();
 
-  free(sec);
   section_file_check_unused(file, filename);
   section_file_free(file);
 }
@@ -1636,33 +1797,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: */
   government_iterate(g) {
     int i = g->index;
     
+    g->basetype = G_MAGIC;
     g->required_tech
       = lookup_tech(file, sec[i], "tech_req", FALSE, filename, g->name);
     
@@ -1832,6 +1973,71 @@
     }
     freelog(LOG_DEBUG, "%s subgoal %d", g->name, g->subgoal);
   } government_iterate_end;
+  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 based on %d", name, basetype);
+
+    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;
@@ -1868,7 +2074,6 @@
     ai_gov_tech_hints[j].tech = A_LAST;
   }
 
-  free(sec);
   section_file_check_unused(file, filename);
   section_file_free(file);
 }
@@ -1906,6 +2111,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;
@@ -2575,53 +2785,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);
 }
 
 /**************************************************************************
@@ -2756,90 +3053,110 @@
 **************************************************************************/
 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;
-  int j;
+  struct government *g;
+  int i;
 
-  government_iterate(g) {
-    /* send one packet_government */
-    gov.id                 = g->index;
+  g = &governments[id];
+
+  /* send one packet_government */
+  gov.id                 = g->index;
 
-    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);
-    
-    /* 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 = g->index;
-      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);
+  lsend_packet_ruleset_government(dest, &gov);
     
-      lsend_packet_ruleset_government_ruler_title(dest, &title);
-    }
-  } government_iterate_end;
+  /* 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);
+  }
 }
 
 /**************************************************************************
@@ -2944,6 +3261,8 @@
 
   freelog(LOG_NORMAL, _("Loading rulesets"));
 
+  script_load_library();
+
   openload_ruleset_file(&techfile, "techs");
   load_tech_names(&techfile);
 
@@ -2988,7 +3307,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-07-07 18:21:49.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-07-07 18:20:27.000000000 +0100
+++ freeciv-patched/server/savegame.c	2003-07-07 18:21:49.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, NULL);
+  }
+}
+
+/***************************************************************
+...
+***************************************************************/
 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-07-07 18:20:27.000000000 +0100
+++ freeciv-patched/server/srv_main.c	2003-07-07 18:21:49.000000000 +0100
@@ -59,6 +59,7 @@
 #include "player.h"
 #include "rand.h"
 #include "registry.h"
+#include "script.h"
 #include "shared.h"
 #include "support.h"
 #include "tech.h"
@@ -176,6 +177,9 @@
   /* initialize teams */
   team_init();
 
+  /* set up script interpreter */
+  script_init();
+
   /* mark as initialized */
   has_been_srv_init = TRUE;
 
@@ -833,6 +837,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;
