Complete.Org: Mailing Lists: Archives: freeciv-dev: October 2005:
[Freeciv-Dev] Re: (PR#14365) battlegroups...
Home

[Freeciv-Dev] Re: (PR#14365) battlegroups...

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
Subject: [Freeciv-Dev] Re: (PR#14365) battlegroups...
From: "Jason Short" <jdorje@xxxxxxxxxxxxxxxxxxxxx>
Date: Fri, 21 Oct 2005 17:00:38 -0700
Reply-to: bugs@xxxxxxxxxxx

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

This version adds drag-and-select clicking.

There is a monolithic amount of work left to do to make all operations 
work the way you'd expect, and integrtate the code fully.  However I 
think it should now be quite playable and possibly ready for committing.

-jason


Index: utility/genlist.c
===================================================================
--- utility/genlist.c   (revision 11168)
+++ utility/genlist.c   (working copy)
@@ -39,6 +39,24 @@
 }
 
 /************************************************************************
+  Returns a new genlist that's a copy of the existing one.
+************************************************************************/
+struct genlist *genlist_copy(struct genlist *pgenlist)
+{
+  struct genlist *pcopy = genlist_new();
+
+  if (pgenlist) {
+    struct genlist_link *plink;
+
+    for (plink = pgenlist->head_link; plink; plink = plink->next) {
+      genlist_append(pcopy, plink->dataptr);
+    }
+  }
+
+  return pcopy;
+}
+
+/************************************************************************
   Remove a genlist.  The list must be empty first!
 ************************************************************************/
 void genlist_free(struct genlist *pgenlist)
Index: utility/genlist.h
===================================================================
--- utility/genlist.h   (revision 11168)
+++ utility/genlist.h   (working copy)
@@ -73,6 +73,7 @@
 int genlist_size(const struct genlist *pgenlist);
 void *genlist_get(const struct genlist *pgenlist, int idx);
 struct genlist *genlist_new(void);
+struct genlist *genlist_copy(struct genlist *pgenlist);
 void genlist_unlink_all(struct genlist *pgenlist);
 void genlist_free(struct genlist *pgenlist);
 void genlist_append(struct genlist *pgenlist, void *data);
Index: utility/speclist.h
===================================================================
--- utility/speclist.h  (revision 11168)
+++ utility/speclist.h  (working copy)
@@ -86,6 +86,15 @@
   return speclist;
 }
 
+static inline SPECLIST_LIST *SPECLIST_FOO(_list_copy) (SPECLIST_LIST *plist)
+{
+  SPECLIST_LIST *newlist = (SPECLIST_LIST *)fc_malloc(sizeof(*newlist));
+
+  newlist->list = genlist_copy(plist ? plist->list : NULL);
+
+  return newlist;
+}
+
 static inline void SPECLIST_FOO(_list_prepend) (SPECLIST_LIST *tthis, 
SPECLIST_TYPE *pfoo)
 {
   genlist_prepend(tthis->list, pfoo);
Index: utility/shared.h
===================================================================
--- utility/shared.h    (revision 11168)
+++ utility/shared.h    (working copy)
@@ -46,7 +46,8 @@
      : ((value) >= (range) ? (value) % (range) : (value)))
 
 #define BOOL_VAL(x) ((x) != 0)
-#define XOR(p, q) (!(p) != !(q))
+#define XOR(p, q) (BOOL_VAL(p) != BOOL_VAL(q))
+#define EQ(p, q) (BOOL_VAL(p) == BOOL_VAL(q))
 
 /*
  * DIVIDE() divides and rounds down, rather than just divides and
Index: server/unittools.c
===================================================================
--- server/unittools.c  (revision 11168)
+++ server/unittools.c  (working copy)
@@ -1766,6 +1766,7 @@
     packet->transported_by = punit->transported_by;
   }
   packet->occupy = get_transporter_occupancy(punit);
+  packet->battlegroup = punit->battlegroup;
   packet->has_orders = punit->has_orders;
   if (punit->has_orders) {
     int i;
Index: server/unithand.c
===================================================================
--- server/unithand.c   (revision 11168)
+++ server/unithand.c   (working copy)
@@ -1423,6 +1423,22 @@
 }
 
 /**************************************************************************
+  Assign the unit to the battlegroup.
+
+  Battlegroups are handled entirely by the client, so all we have to
+  do here is save the battlegroup ID so that it'll be persistent.
+**************************************************************************/
+void handle_unit_battlegroup(struct player *pplayer,
+                            int unit_id, int battlegroup)
+{
+  struct unit *punit = player_find_unit_by_id(pplayer, unit_id);
+
+  if (punit) {
+    punit->battlegroup = CLIP(-1, battlegroup, MAX_NUM_BATTLEGROUPS);
+  }
+}
+
+/**************************************************************************
 ...
 **************************************************************************/
 void handle_unit_autosettlers(struct player *pplayer, int unit_id)
Index: server/savegame.c
===================================================================
--- server/savegame.c   (revision 11168)
+++ server/savegame.c   (working copy)
@@ -1468,6 +1468,10 @@
     punit->done_moving = secfile_lookup_bool_default(file,
        (punit->moves_left == 0), "player%d.u%d.done_moving", plrno, i);
 
+    punit->battlegroup
+      = secfile_lookup_int_default(file, BATTLEGROUP_NONE,
+                                  "player%d.u%d.battlegroup", plrno, i);
+
     /* Load the goto information.  Older savegames will not have the
      * "go" field, so we just load the goto destination by default. */
     if (secfile_lookup_bool_default(file, TRUE,
@@ -2637,6 +2641,8 @@
                                plrno, i);
     secfile_insert_int(file, punit->fuel, "player%d.u%d.fuel",
                                plrno, i);
+    secfile_insert_int(file, punit->battlegroup,
+                      "player%d.u%d.battlegroup", plrno, i);
 
     if (punit->goto_tile) {
       secfile_insert_bool(file, TRUE, "player%d.u%d.go", plrno, i);
Index: common/unit.c
===================================================================
--- common/unit.c       (revision 11168)
+++ common/unit.c       (working copy)
@@ -1498,6 +1498,7 @@
   punit->ord_city = 0;
   set_unit_activity(punit, ACTIVITY_IDLE);
   punit->occupy = 0;
+  punit->battlegroup = BATTLEGROUP_NONE;
   punit->client.colored = FALSE;
   punit->server.vision = NULL; /* No vision. */
   punit->has_orders = FALSE;
Index: common/unit.h
===================================================================
--- common/unit.h       (revision 11168)
+++ common/unit.h       (working copy)
@@ -164,6 +164,12 @@
 
   int transported_by;
   int occupy; /* number of units that occupy transporter */
+
+  /* The battlegroup ID: defined by the client but stored by the server. */
+#define MAX_NUM_BATTLEGROUPS (4)
+#define BATTLEGROUP_NONE (-1)
+  int battlegroup;
+
   struct {
     /* Equivalent to pcity->client.color.  Only for F_CITIES units. */
     bool colored;
Index: common/packets.def
===================================================================
--- common/packets.def  (revision 11168)
+++ common/packets.def  (working copy)
@@ -241,7 +241,7 @@
   Spaceship
   Ruleset
 
-The last used packet number is 116.
+The last used packet number is 117.
 ****************************************************/
 
 
@@ -727,6 +727,8 @@
   ACTIVITY activity;
   SPECIAL activity_target;
 
+  SINT8 battlegroup;
+
   BOOL has_orders;
   UINT16 orders_length, orders_index;
   BOOL orders_repeat, orders_vigilant;
@@ -786,6 +788,11 @@
   UNIT unit_id;
 end
 
+PACKET_UNIT_BATTLEGROUP=117;cs,dsend
+  UNIT unit_id;
+  SINT8 battlegroup;
+end
+
 PACKET_UNIT_HELP_BUILD_WONDER=57;cs,dsend
   UNIT unit_id;
 end
Index: client/control.c
===================================================================
--- client/control.c    (revision 11168)
+++ client/control.c    (working copy)
@@ -47,19 +47,21 @@
 int num_units_below = MAX_NUM_UNITS_BELOW;
 
 /* unit_focus points to the current unit in focus */
-static struct unit *punit_focus = NULL;
+static struct unit_list *pfocus_units;
 
 /* The previously focused unit.  Focus can generally be recalled on this
  * unit with keypad 5.  FIXME: this is not reset when the client
  * disconnects. */
-static int previous_focus_id = -1;
+static struct unit_list *previous_focus;
 
 /* These should be set via set_hover_state() */
-int hover_unit = 0; /* id of unit hover_state applies to */
+struct unit_list *hover_units;
 enum cursor_hover_state hover_state = HOVER_NONE;
 enum unit_activity connect_activity;
 enum unit_orders goto_last_order; /* Last order for goto */
 
+static struct unit_list *battlegroups[MAX_NUM_BATTLEGROUPS];
+
 /* units involved in current combat */
 static struct unit *punit_attacking = NULL;
 static struct unit *punit_defending = NULL;
@@ -86,8 +88,16 @@
 **************************************************************************/
 void control_init(void)
 {
+  int i;
+
   caravan_arrival_queue = genlist_new();
   diplomat_arrival_queue = genlist_new();
+  hover_units = unit_list_new();
+  pfocus_units = unit_list_new();
+  previous_focus = unit_list_new();
+  for (i = 0; i < MAX_NUM_BATTLEGROUPS; i++) {
+    battlegroups[i] = unit_list_new();
+  }
 }
 
 /**************************************************************************
@@ -95,41 +105,138 @@
 **************************************************************************/
 void control_done(void)
 {
+  int i;
+
   genlist_free(caravan_arrival_queue);
   genlist_free(diplomat_arrival_queue);
+  unit_list_free(hover_units);
+  unit_list_free(pfocus_units);
+  unit_list_free(previous_focus);
+  for (i = 0; i < MAX_NUM_BATTLEGROUPS; i++) {
+    unit_list_free(battlegroups[i]);
+  }
 }
 
 /**************************************************************************
+  Called when a unit is killed; this removes it from the control lists.
+**************************************************************************/
+void control_unit_killed(struct unit *punit)
+{
+  int i;
+
+  unit_list_unlink(get_units_in_focus(), punit);
+  unit_list_unlink(hover_units, punit);
+  unit_list_unlink(previous_focus, punit);
+  for (i = 0; i < MAX_NUM_BATTLEGROUPS; i++) {
+    unit_list_unlink(battlegroups[i], punit);
+  }
+}
+
+/**************************************************************************
+  Change the battlegroup for this unit.
+**************************************************************************/
+void unit_change_battlegroup(struct unit *punit, int battlegroup)
+{
+  if (battlegroup < 0 || battlegroup >= MAX_NUM_BATTLEGROUPS) {
+    battlegroup = BATTLEGROUP_NONE;
+  }
+
+  if (punit->battlegroup != battlegroup) {
+    if (battlegroup != BATTLEGROUP_NONE) {
+      unit_list_append(battlegroups[battlegroup], punit);
+    }
+    if (punit->battlegroup != BATTLEGROUP_NONE) {
+      unit_list_unlink(battlegroups[punit->battlegroup], punit);
+    }
+    punit->battlegroup = battlegroup;
+  }
+}
+
+/**************************************************************************
+  Call this on new units to enter them in the battlegroup lists.
+**************************************************************************/
+void unit_register_battlegroup(struct unit *punit)
+{
+  if (punit->battlegroup < 0 || punit->battlegroup >= MAX_NUM_BATTLEGROUPS) {
+    punit->battlegroup = BATTLEGROUP_NONE;
+  } else {
+    unit_list_append(battlegroups[punit->battlegroup], punit);
+  }
+}
+
+/**************************************************************************
   Enter the given hover state.
 
     activity => The connect activity (ACTIVITY_ROAD, etc.)
     order => The last order (ORDER_BUILD_CITY, ORDER_LAST, etc.)
 **************************************************************************/
-void set_hover_state(struct unit *punit, enum cursor_hover_state state,
+void set_hover_state(struct unit_list *punits, enum cursor_hover_state state,
                     enum unit_activity activity,
                     enum unit_orders order)
 {
-  assert(punit != NULL || state == HOVER_NONE);
+  assert((punits && unit_list_size(punits) > 0) || state == HOVER_NONE);
   assert(state == HOVER_CONNECT || activity == ACTIVITY_LAST);
   assert(state == HOVER_GOTO || order == ORDER_LAST);
-  if (punit)
-    hover_unit = punit->id;
-  else
-    hover_unit = 0;
+  unit_list_unlink_all(hover_units);
+  if (punits) {
+    unit_list_iterate(punits, punit) {
+      unit_list_append(hover_units, punit);
+    } unit_list_iterate_end;
+  }
   hover_state = state;
   connect_activity = activity;
   goto_last_order = order;
   exit_goto_state();
 }
 
+/****************************************************************************
+  Return TRUE iff this unit is in focus.
+****************************************************************************/
+bool unit_is_in_focus(const struct unit *punit)
+{
+  return unit_list_search(get_units_in_focus(), punit);
+}
+
+/****************************************************************************
+  Return TRUE iff a unit on this tile is in focus.
+****************************************************************************/
+struct unit *get_focus_unit_on_tile(const struct tile *ptile)
+{
+  unit_list_iterate(get_units_in_focus(), punit) {
+    if (punit->tile == ptile) {
+      return punit;
+    }
+  } unit_list_iterate_end;
+
+  return NULL;
+}
+
+/****************************************************************************
+  Finds a single focus unit that we can center on.  May return NULL.
+****************************************************************************/
+static struct tile *find_a_focus_unit_tile_to_center_on(void)
+{
+  struct unit *punit;
+
+  if ((punit = get_focus_unit_on_tile(get_center_tile_mapcanvas()))) {
+    return punit->tile;
+  } else if (get_num_units_in_focus() > 0) {
+    return unit_list_get(get_units_in_focus(), 0)->tile;
+  } else {
+    return NULL;
+  }
+}
+
 /**************************************************************************
 Center on the focus unit, if off-screen and auto_center_on_unit is true.
 **************************************************************************/
 void auto_center_on_focus_unit(void)
 {
-  if (punit_focus && auto_center_on_unit &&
-      !tile_visible_and_not_on_border_mapcanvas(punit_focus->tile)) {
-    center_tile_mapcanvas(punit_focus->tile);
+  struct tile *ptile = find_a_focus_unit_tile_to_center_on();
+
+  if (ptile && auto_center_on_unit &&
+      !tile_visible_and_not_on_border_mapcanvas(ptile)) {
+    center_tile_mapcanvas(ptile);
   }
 }
 
@@ -143,21 +250,28 @@
 **************************************************************************/
 void set_unit_focus(struct unit *punit)
 {
-  struct unit *punit_old_focus = punit_focus;
-
   if (punit && game.player_ptr && punit->owner != game.player_ptr) {
     /* Callers should make sure this never happens. */
     return;
   }
 
-  if (punit != punit_focus) {
+  /* FIXME: this won't work quite right; for instance activating a
+   * battlegroup twice in a row will store the focus erronously.  The only
+   * solution would be a set_units_focus() */
+  if (!(get_num_units_in_focus() == 1
+       && punit == unit_list_get(get_units_in_focus(), 0))) {
     store_focus();
   }
 
-  /*
-   *  This should be the ONLY place we _modify_ punit_focus.
-   */
-  punit_focus = punit;
+  /* Redraw the old focus unit (to fix blinking or remove the selection
+   * circle). */
+  unit_list_iterate(pfocus_units, punit_old) {
+    refresh_unit_mapcanvas(punit_old, punit_old->tile, TRUE, FALSE);
+  } unit_list_iterate_end;
+  unit_list_unlink_all(pfocus_units);
+  if (punit) {
+    unit_list_append(pfocus_units, punit);
+  }
 
   if (!can_client_change_view()) {
     /* This function can be called to set the focus to NULL when
@@ -182,16 +296,40 @@
       request_new_unit_activity(punit, ACTIVITY_IDLE);
     }
   }
-  
-  /* Redraw the old focus unit (to fix blinking or remove the selection
-   * circle). */
-  if (punit_old_focus
-      && (!punit || !same_pos(punit_old_focus->tile, punit->tile))) {
-    refresh_unit_mapcanvas(punit_old_focus, punit_old_focus->tile,
-                          TRUE, FALSE);
+
+  update_unit_info_label(pfocus_units);
+  update_menus();
+}
+
+/**************************************************************************
+  Adds this unit to the list of units in focus.
+**************************************************************************/
+void add_unit_focus(struct unit *punit)
+{
+  if (punit && game.player_ptr && punit->owner != game.player_ptr) {
+    /* Callers should make sure this never happens. */
+    return;
   }
+  if (!punit || !can_client_change_view()) {
+    return;
+  }
+  if (unit_is_in_focus(punit)) {
+    return;
+  }
 
-  update_unit_info_label(punit);
+  unit_list_append(pfocus_units, punit);
+  punit->focus_status = FOCUS_AVAIL;
+  refresh_unit_mapcanvas(punit, punit->tile, TRUE, FALSE);
+  if (unit_has_orders(punit)) {
+    /* Clear the focus unit's orders. */
+    request_orders_cleared(punit);
+  }
+  if (punit->activity != ACTIVITY_IDLE || punit->ai.control)  {
+    punit->ai.control = FALSE;
+    refresh_unit_city_dialogs(punit);
+    request_new_unit_activity(punit, ACTIVITY_IDLE);
+  }
+  update_unit_info_label(pfocus_units);
   update_menus();
 }
 
@@ -212,8 +350,11 @@
 **************************************************************************/
 static void store_focus(void)
 {
-  if (punit_focus) {
-    previous_focus_id = punit_focus->id;
+  if (get_num_units_in_focus() > 0) {
+    unit_list_unlink_all(previous_focus);
+    unit_list_iterate(get_units_in_focus(), punit) {
+      unit_list_append(previous_focus, punit);
+    } unit_list_iterate_end;
   }
 }
 
@@ -228,25 +369,46 @@
   if (!game.player_ptr || !can_client_change_view()) {
     return;
   }
-  if (!punit_focus
-      || (punit_focus->activity != ACTIVITY_IDLE
-         && !unit_has_orders(punit_focus)
-         && punit_focus->activity != ACTIVITY_GOTO)
-      || punit_focus->done_moving
-      || punit_focus->moves_left == 0 
-      || punit_focus->ai.control) {
+  if (get_num_units_in_focus() == 0) {
     advance_unit_focus();
+  } else {
+    bool alldone = TRUE;
+
+    unit_list_iterate(get_units_in_focus(), punit) {
+      if ((punit->activity != ACTIVITY_IDLE
+          && !unit_has_orders(punit)
+          && punit->activity != ACTIVITY_GOTO)
+         || punit->done_moving
+         || punit->moves_left == 0 
+         || punit->ai.control) {
+
+      } else {
+       alldone = FALSE;
+       break;
+      }
+    } unit_list_iterate_end;
+    if (alldone) {
+      advance_unit_focus();
+    }
   }
 }
 
 /**************************************************************************
 ...
 **************************************************************************/
-struct unit *get_unit_in_focus(void)
+struct unit_list *get_units_in_focus(void)
 {
-  return punit_focus;
+  return pfocus_units;
 }
 
+/****************************************************************************
+  Return the number of units currently in focus (0 or more).
+****************************************************************************/
+int get_num_units_in_focus(void)
+{
+  return unit_list_size(pfocus_units);
+}
+
 /**************************************************************************
  This function may be called from packhand.c, via update_unit_focus(),
  as a result of packets indicating change in activity for a unit. Also
@@ -256,7 +418,7 @@
 **************************************************************************/
 void advance_unit_focus(void)
 {
-  struct unit *punit_old_focus = punit_focus;
+  const int num_units_in_old_focus = get_num_units_in_focus();
   struct unit *candidate = find_best_focus_candidate(FALSE);
 
   if (!game.player_ptr || !can_client_change_view()) {
@@ -265,10 +427,18 @@
   }
 
   set_hover_state(NULL, HOVER_NONE, ACTIVITY_LAST, ORDER_LAST);
-  if (!can_client_change_view()) {
-    return;
-  }
 
+  unit_list_iterate(get_units_in_focus(), punit) {
+    /* 
+     * Is the unit which just lost focus a non-AI unit? If yes this
+     * enables the auto end turn. 
+     */
+    if (!punit->ai.control) {
+      non_ai_unit_focus = TRUE;
+      break;
+    }
+  } unit_list_iterate_end;
+
   if(!candidate) {
     /* First try for "waiting" units. */
     unit_list_iterate(game.player_ptr->units, punit) {
@@ -287,20 +457,15 @@
   set_unit_focus(candidate);
 
   /* 
-   * Is the unit which just lost focus a non-AI unit? If yes this
-   * enables the auto end turn. 
-   */
-  if (punit_old_focus && !punit_old_focus->ai.control) {
-    non_ai_unit_focus = TRUE;
-  }
-
-  /* 
    * Handle auto-turn-done mode: If a unit was in focus (did move),
    * but now none are (no more to move) and there was at least one
    * non-AI unit this turn which was focused, then fake a Turn Done
    * keypress.
    */
-  if (auto_turn_done && punit_old_focus && !punit_focus && non_ai_unit_focus) {
+  if (auto_turn_done
+      && num_units_in_old_focus > 0
+      && get_num_units_in_focus() == 0
+      && non_ai_unit_focus) {
     key_end_turn();
   }
 }
@@ -312,9 +477,7 @@
 **************************************************************************/
 static struct unit *find_best_focus_candidate(bool accept_current)
 {
-  struct unit *best_candidate;
-  int best_dist = 99999;
-  struct tile *ptile;
+  struct tile *ptile = get_center_tile_mapcanvas();
 
   if (!game.player_ptr
       || !is_player_phase(game.player_ptr, game.info.phase)) {
@@ -322,29 +485,30 @@
     return NULL;
   }
 
-  if (punit_focus)  {
-    ptile = punit_focus->tile;
-  } else {
-    ptile = get_center_tile_mapcanvas();
+  if (!get_focus_unit_on_tile(ptile)) {
+    struct unit *pfirst = unit_list_get(get_units_in_focus(), 0);
+
+    if (pfirst) {
+      ptile = pfirst->tile;
+    }
   }
 
-  best_candidate = NULL;
-  unit_list_iterate(game.player_ptr->units, punit) {
-    if ((punit != punit_focus || accept_current)
-      && punit->focus_status == FOCUS_AVAIL
-      && punit->activity == ACTIVITY_IDLE
-       && !unit_has_orders(punit)
-      && punit->moves_left > 0
-      && !punit->done_moving
-      && !punit->ai.control) {
-        int d = sq_map_distance(punit->tile, ptile);
-        if (d < best_dist) {
-          best_candidate = punit;
-          best_dist = d;
-        }
-    }
-  } unit_list_iterate_end;
-  return best_candidate;
+  iterate_outward(ptile, FC_INFINITY, ptile2) {
+    unit_list_iterate(ptile2->units, punit) {
+      if ((!unit_is_in_focus(punit) || accept_current)
+         && punit->owner == game.player_ptr
+         && punit->focus_status == FOCUS_AVAIL
+         && punit->activity == ACTIVITY_IDLE
+         && !unit_has_orders(punit)
+         && punit->moves_left > 0
+         && !punit->done_moving
+         && !punit->ai.control) {
+       return punit;
+      }
+    } unit_list_iterate_end;
+  } iterate_outward_end;
+
+  return NULL;
 }
 
 /**************************************************************************
@@ -353,6 +517,7 @@
 struct unit *find_visible_unit(struct tile *ptile)
 {
   struct unit *panyowned = NULL, *panyother = NULL, *ptptother = NULL;
+  struct unit *pfocus;
 
   /* If no units here, return nothing. */
   if (unit_list_size(ptile->units)==0) {
@@ -374,10 +539,8 @@
   }
 
   /* If the unit in focus is at this tile, show that on top */
-  if (punit_focus && same_pos(punit_focus->tile, ptile)) {
-    unit_list_iterate(ptile->units, punit)
-      if(punit == punit_focus) return punit;
-    unit_list_iterate_end;
+  if ((pfocus = get_focus_unit_on_tile(ptile))) {
+    return pfocus;
   }
 
   /* If a city is here, return nothing (unit hidden by city). */
@@ -418,30 +581,23 @@
 **************************************************************************/
 double blink_active_unit(void)
 {
-  static struct unit *pblinking_unit;
   static struct timer *blink_timer = NULL;
-
   const double blink_time = get_focus_unit_toggle_timeout(tileset);
-  struct unit *punit = punit_focus;
-  bool need_update = FALSE;
 
-  if (punit) {
-    if (punit != pblinking_unit) {
-      pblinking_unit = punit;
-      reset_focus_unit_state(tileset);
-      need_update = TRUE;
-    } else {
-      if (read_timer_seconds(blink_timer) > blink_time) {
-       toggle_focus_unit_state(tileset);
-       need_update = TRUE;
-      }
-    }
-    if (need_update) {
+  if (get_num_units_in_focus() > 0) {
+    if (!blink_timer || read_timer_seconds(blink_timer) > blink_time) {
+      toggle_focus_unit_state(tileset);
+
       /* If we lag, we don't try to catch up.  Instead we just start a
        * new blink_time on every update. */
       blink_timer = renew_timer_start(blink_timer, TIMER_USER, TIMER_ACTIVE);
 
-      refresh_unit_mapcanvas(punit, punit->tile, FALSE, TRUE);
+      unit_list_iterate(get_units_in_focus(), punit) {
+       /* We flush to screen directly here.  This is most likely faster
+        * since these drawing operations are all small but may be spread
+        * out widely. */
+       refresh_unit_mapcanvas(punit, punit->tile, FALSE, TRUE);
+      } unit_list_iterate_end;
     }
 
     return blink_time - read_timer_seconds(blink_timer);
@@ -501,18 +657,21 @@
   be enough information to know whether to redraw -- instead redraw every
   time.  (Could store enough info to know, but is it worth it?)
 **************************************************************************/
-void update_unit_pix_label(struct unit *punit)
+void update_unit_pix_label(struct unit_list *punitlist)
 {
   int i;
 
   /* Check for any change in the unit's state.  This assumes that a unit's
    * orders cannot be changed directly but must be removed and then reset. */
-  if (punit && get_client_state() != CLIENT_GAME_OVER_STATE) {
+  if (unit_list_size(punitlist) > 0
+      && get_client_state() != CLIENT_GAME_OVER_STATE) {
     /* There used to be a complicated and bug-prone check here to see if
      * the unit had actually changed.  This was misguided since the stacked
      * units (below) are redrawn in any case.  Unless we write a general
      * system for unit updates here we might as well just redraw it every
      * time. */
+    struct unit *punit = unit_list_get(punitlist, 0);
+
     set_unit_icon(-1, punit);
 
     i = 0;                     /* index into unit_below_canvas */
@@ -550,7 +709,8 @@
   punit_attacking = pattacker;
   punit_defending = pdefender;
 
-  if (punit_attacking == punit_focus || punit_defending == punit_focus) {
+  if (unit_is_in_focus(punit_attacking)
+      || unit_is_in_focus(punit_defending)) {
     /* If one of the units is the focus unit, make sure hidden-focus is
      * disabled.  We don't just do this as a check later because then
      * with a blinking unit it would just disappear again right after the
@@ -675,15 +835,15 @@
 **************************************************************************/
 void request_unit_goto(enum unit_orders last_order)
 {
-  struct unit *punit = punit_focus;
+  struct unit_list *punits = get_units_in_focus();
 
-  if (!punit)
+  if (unit_list_size(punits) == 0)
     return;
 
   if (hover_state != HOVER_GOTO) {
-    set_hover_state(punit, HOVER_GOTO, ACTIVITY_LAST, last_order);
-    update_unit_info_label(punit);
-    enter_goto_state(punit);
+    set_hover_state(punits, HOVER_GOTO, ACTIVITY_LAST, last_order);
+    update_unit_info_label(punits);
+    enter_goto_state(punits);
     create_line_at_mouse_pos();
   } else {
     assert(goto_is_active());
@@ -761,16 +921,17 @@
 **************************************************************************/
 void request_unit_connect(enum unit_activity activity)
 {
-  if (!punit_focus || !can_unit_do_connect(punit_focus, activity)) {
+  if (!can_units_do_connect(get_units_in_focus(), activity)) {
     return;
   }
 
   if (hover_state != HOVER_CONNECT || connect_activity != activity) {
     /* Enter or change the hover connect state. */
-    set_hover_state(punit_focus, HOVER_CONNECT, activity, ORDER_LAST);
-    update_unit_info_label(punit_focus);
+    set_hover_state(get_units_in_focus(), HOVER_CONNECT,
+                   activity, ORDER_LAST);
+    update_unit_info_label(get_units_in_focus());
 
-    enter_goto_state(punit_focus);
+    enter_goto_state(get_units_in_focus());
     create_line_at_mouse_pos();
   } else {
     assert(goto_is_active());
@@ -806,6 +967,7 @@
     }
   } unit_list_iterate_end;
 
+
   if (plast) {
     /* Unfocus the ship, and advance the focus to the last unloaded unit.
      * If there is no unit unloaded (which shouldn't happen, but could if
@@ -857,6 +1019,25 @@
   wakeup_sentried_units(punit->tile);
 }
 
+/****************************************************************************
+  Select all units of the same type as the given unit.
+****************************************************************************/
+void request_unit_select_same_type(struct unit_list *punits)
+{
+  if (can_client_change_view()) {
+    unit_list_iterate(punits, punit) {
+      unit_list_iterate(unit_owner(punit)->units, punit2) {
+       if (punit2->type == punit->type
+           && !unit_list_search(punits, punit2)
+           && punit2->activity == ACTIVITY_IDLE
+           && !unit_has_orders(punit2)) {
+         add_unit_focus(punit2);
+       }
+      } unit_list_iterate_end;
+    } unit_list_iterate_end;
+  }
+}
+
 /**************************************************************************
   Request a diplomat to do a specific action.
   - action : The action to be requested.
@@ -1065,36 +1246,59 @@
 /**************************************************************************
  Explode nuclear at a tile without enemy units
 **************************************************************************/
-void request_unit_nuke(struct unit *punit)
+void request_unit_nuke(struct unit_list *punits)
 {
-  if(!unit_flag(punit, F_NUCLEAR)) {
-    create_event(punit->tile, E_BAD_COMMAND,
-                _("Only nuclear units can do this."));
+  bool can = FALSE;
+  struct tile *offender = NULL;
+
+  if (unit_list_size(punits) == 0) {
     return;
   }
-  if(punit->moves_left == 0)
-    do_unit_nuke(punit);
-  else {
-    set_hover_state(punit, HOVER_NUKE, ACTIVITY_LAST, ORDER_LAST);
-    update_unit_info_label(punit);
+  unit_list_iterate(punits, punit) {
+    if (unit_flag(punit, F_NUCLEAR)) {
+      can = TRUE;
+      break;
+    }
+    if (!offender) { /* Take first offender tile/unit */
+      offender = punit->tile;
+    }
+  } unit_list_iterate_end;
+  if (can) {
+    set_hover_state(punits, HOVER_NUKE, ACTIVITY_LAST, ORDER_LAST);
+    update_unit_info_label(punits);
+  } else {
+    create_event(offender, E_BAD_COMMAND,
+                _("Only nuclear units can do this."));
   }
 }
 
 /**************************************************************************
 ...
 **************************************************************************/
-void request_unit_paradrop(struct unit *punit)
+void request_unit_paradrop(struct unit_list *punits)
 {
-  if(!unit_flag(punit, F_PARATROOPERS)) {
-    create_event(punit->tile, E_BAD_COMMAND,
-                _("Only paratrooper units can do this."));
+  bool can = FALSE;
+  struct tile *offender = NULL;
+
+  if (unit_list_size(punits) == 0) {
     return;
   }
-  if(!can_unit_paradrop(punit))
-    return;
-
-  set_hover_state(punit, HOVER_PARADROP, ACTIVITY_LAST, ORDER_LAST);
-  update_unit_info_label(punit);
+  unit_list_iterate(punits, punit) {
+    if (can_unit_paradrop(punit)) {
+      can = TRUE;
+      break;
+    }
+    if (!offender) { /* Take first offender tile/unit */
+      offender = punit->tile;
+    }
+  } unit_list_iterate_end;
+  if (can) {
+    set_hover_state(punits, HOVER_PARADROP, ACTIVITY_LAST, ORDER_LAST);
+    update_unit_info_label(punits);
+  } else {
+    create_event(offender, E_BAD_COMMAND,
+                _("Only paratrooper units can do this."));
+  }
 }
 
 /**************************************************************************
@@ -1102,15 +1306,16 @@
 **************************************************************************/
 void request_unit_patrol(void)
 {
-  struct unit *punit = punit_focus;
+  struct unit_list *punits = get_units_in_focus();
 
-  if (!punit)
+  if (unit_list_size(punits) == 0) {
     return;
+  }
 
   if (hover_state != HOVER_PATROL) {
-    set_hover_state(punit, HOVER_PATROL, ACTIVITY_LAST, ORDER_LAST);
-    update_unit_info_label(punit);
-    enter_goto_state(punit);
+    set_hover_state(punits, HOVER_PATROL, ACTIVITY_LAST, ORDER_LAST);
+    update_unit_info_label(punits);
+    enter_goto_state(punits);
     create_line_at_mouse_pos();
   } else {
     assert(goto_is_active());
@@ -1407,20 +1612,23 @@
 **************************************************************************/
 void request_center_focus_unit(void)
 {
-  if (punit_focus) {
-    center_tile_mapcanvas(punit_focus->tile);
+  struct tile *ptile = find_a_focus_unit_tile_to_center_on();
+
+  if (ptile) {
+    center_tile_mapcanvas(ptile);
   }
 }
 
 /**************************************************************************
 ...
 **************************************************************************/
-void request_unit_wait(struct unit *punit)
+void request_units_wait(struct unit_list *punits)
 {
-  punit->focus_status=FOCUS_WAIT;
-  if (punit == punit_focus) {
+  unit_list_iterate(punits, punit) {
+    punit->focus_status = FOCUS_WAIT;
+  } unit_list_iterate_end;
+  if (punits == get_units_in_focus()) {
     advance_unit_focus();
-    /* set_unit_focus(punit_focus); */  /* done in advance_unit_focus */
   }
 }
 
@@ -1429,10 +1637,11 @@
 **************************************************************************/
 void request_unit_move_done(void)
 {
-  if (punit_focus) {
-    punit_focus->focus_status = FOCUS_DONE;
+  if (get_num_units_in_focus() > 0) {
+    unit_list_iterate(get_units_in_focus(), punit) {
+      punit->focus_status = FOCUS_DONE;
+    } unit_list_iterate_end;
     advance_unit_focus();
-    /* set_unit_focus(punit_focus); */  /* done in advance_unit_focus */
   }
 }
 
@@ -1504,7 +1713,9 @@
     update_city_description(dst_tile->city);
   }
 
-  if (punit_focus == punit) update_menus();
+  if (unit_is_in_focus(punit)) {
+    update_menus();
+  }
 }
 
 /**************************************************************************
@@ -1513,10 +1724,12 @@
 void do_map_click(struct tile *ptile, enum quickselect_type qtype)
 {
   struct city *pcity = tile_get_city(ptile);
-  struct unit *punit = player_find_unit_by_id(game.player_ptr, hover_unit);
+  struct unit_list *punits = hover_units;
   bool maybe_goto = FALSE;
+  bool possible = FALSE;
+  struct tile *offender = NULL;
 
-  if (punit && hover_state != HOVER_NONE) {
+  if (unit_list_size(punits) > 0 && hover_state != HOVER_NONE) {
     switch (hover_state) {
     case HOVER_NONE:
       die("well; shouldn't get here :)");
@@ -1524,32 +1737,44 @@
       do_unit_goto(ptile);
       break;
     case HOVER_NUKE:
-      if (SINGLE_MOVE * real_map_distance(punit->tile, ptile)
-         > punit->moves_left) {
-        create_event(punit->tile, E_BAD_COMMAND, _("Too far for this unit."));
+      unit_list_iterate(punits, punit) {
+       if (SINGLE_MOVE * real_map_distance(punit->tile, ptile)
+           <= punit->moves_left) {
+         possible = TRUE;
+         break;
+       }
+       offender = punit->tile;
+      } unit_list_iterate_end;
+      if (!possible) {
+       create_event(offender, E_BAD_COMMAND, _("Too far for this unit."));
       } else {
        do_unit_goto(ptile);
-       /* note that this will be executed by the server after the goto */
-       if (!pcity)
-         do_unit_nuke(punit);
+       if (!pcity) {
+         unit_list_iterate(punits, punit) {
+           /* note that this will be executed by the server after the goto */
+           do_unit_nuke(punit);
+         } unit_list_iterate_end;
+       }
       }
       break;
     case HOVER_PARADROP:
-      do_unit_paradrop_to(punit, ptile);
+      unit_list_iterate(punits, punit) {
+       do_unit_paradrop_to(punit, ptile);
+      } unit_list_iterate_end;
       break;
     case HOVER_CONNECT:
-      do_unit_connect(punit, ptile, connect_activity);
+      do_unit_connect(ptile, connect_activity);
       break;
     case HOVER_PATROL:
-      do_unit_patrol_to(punit, ptile);
+      do_unit_patrol_to(ptile);
       break;   
     }
     set_hover_state(NULL, HOVER_NONE, ACTIVITY_LAST, ORDER_LAST);
-    update_unit_info_label(punit);
+    update_unit_info_label(get_units_in_focus());
   }
 
   /* Bypass stack or city popup if quickselect is specified. */
-  else if (qtype) {
+  else if (qtype != SELECT_POPUP && qtype != SELECT_APPEND) {
     struct unit *qunit = quickselect(ptile, qtype);
     if (qunit) {
       set_unit_focus_and_select(qunit);
@@ -1561,7 +1786,7 @@
     popup_city_dialog(pcity);
   }
   else if (unit_list_size(ptile->units) == 0 && !pcity
-           && punit_focus) {
+           && get_num_units_in_focus() > 0) {
     maybe_goto = keyboardless_goto;
   }
   else if (unit_list_size(ptile->units) == 1
@@ -1571,7 +1796,11 @@
     if (game.player_ptr == punit->owner) {
       if(can_unit_do_activity(punit, ACTIVITY_IDLE)) {
         maybe_goto = keyboardless_goto;
-       set_unit_focus_and_select(punit);
+       if (qtype == SELECT_APPEND) {
+         add_unit_focus(punit);
+       } else {
+         set_unit_focus_and_select(punit);
+       }
       }
     } else if (pcity) {
       /* Don't hide the unit in the city. */
@@ -1630,10 +1859,10 @@
    *          Any unit
    */
 
-    unit_list_iterate(ptile->units, punit)  {
-  if (game.player_ptr != punit->owner || punit == punit_focus) {
-    continue;
-  }
+  unit_list_iterate(ptile->units, punit)  {
+    if (game.player_ptr != punit->owner || unit_is_in_focus(punit)) {
+      continue;
+    }
   if (qtype == SELECT_SEA) {
     /* Transporter. */
     if (get_transporter_capacity(punit)) {
@@ -1718,22 +1947,19 @@
 **************************************************************************/
 void do_unit_goto(struct tile *ptile)
 {
-  struct unit *punit = player_find_unit_by_id(game.player_ptr, hover_unit);
+  struct tile *dest_tile;
 
-  if (hover_unit == 0 || hover_state != HOVER_GOTO)
+  if (hover_state != HOVER_GOTO) {
     return;
+  }
 
-  if (punit) {
-    struct tile *dest_tile;
-
-    draw_line(ptile);
-    dest_tile = get_line_dest();
-    if (ptile == dest_tile) {
-      send_goto_route(punit);
-    } else {
-      create_event(punit->tile, E_BAD_COMMAND,
-                  _("Didn't find a route to the destination!"));
-    }
+  draw_line(ptile);
+  dest_tile = get_line_dest();
+  if (ptile == dest_tile) {
+    send_goto_route();
+  } else {
+    create_event(ptile, E_BAD_COMMAND,
+                _("Didn't find a route to the destination!"));
   }
 
   set_hover_state(NULL, HOVER_NONE, ACTIVITY_LAST, ORDER_LAST);
@@ -1758,17 +1984,17 @@
 /**************************************************************************
   Patrol to a location.
 **************************************************************************/
-void do_unit_patrol_to(struct unit *punit, struct tile *ptile)
+void do_unit_patrol_to(struct tile *ptile)
 {
   struct tile *dest_tile;
 
   draw_line(ptile);
   dest_tile = get_line_dest();
   if (ptile == dest_tile
-      && !is_non_allied_unit_tile(ptile, unit_owner(punit))) {
-    send_patrol_route(punit);
+      && !is_non_allied_unit_tile(ptile, game.player_ptr)) {
+    send_patrol_route();
   } else {
-    create_event(punit->tile, E_BAD_COMMAND,
+    create_event(dest_tile, E_BAD_COMMAND,
                 _("Didn't find a route to the destination!"));
   }
 
@@ -1778,23 +2004,18 @@
 /**************************************************************************
   "Connect" to the given location.
 **************************************************************************/
-void do_unit_connect(struct unit *punit, struct tile *ptile,
+void do_unit_connect(struct tile *ptile,
                     enum unit_activity activity)
 {
-  if (is_air_unit(punit)) {
-    create_event(punit->tile, E_BAD_COMMAND,
-                _("Sorry, airunit connect not yet implemented."));
+  struct tile *dest_tile;
+
+  draw_line(ptile);
+  dest_tile = get_line_dest();
+  if (same_pos(dest_tile, ptile)) {
+    send_connect_route(activity);
   } else {
-    struct tile *dest_tile;
-
-    draw_line(ptile);
-    dest_tile = get_line_dest();
-    if (same_pos(dest_tile, ptile)) {
-      send_connect_route(punit, activity);
-    } else {
-      create_event(punit->tile, E_BAD_COMMAND,
-                  _("Didn't find a route to the destination!"));
-    }
+    create_event(ptile, E_BAD_COMMAND,
+                _("Didn't find a route to the destination!"));
   }
 
   set_hover_state(NULL, HOVER_NONE, ACTIVITY_LAST, ORDER_LAST);
@@ -1814,10 +2035,8 @@
   }
 
   if (hover_state != HOVER_NONE && !popped) {
-    struct unit *punit = player_find_unit_by_id(game.player_ptr, hover_unit);
-
     set_hover_state(NULL, HOVER_NONE, ACTIVITY_LAST, ORDER_LAST);
-    update_unit_info_label(punit);
+    update_unit_info_label(hover_units);
 
     keyboardless_goto_button_down = FALSE;
     keyboardless_goto_active = FALSE;
@@ -1855,11 +2074,19 @@
 **************************************************************************/
 void key_recall_previous_focus_unit(void)
 {
-  struct unit *punit = player_find_unit_by_id(game.player_ptr,
-                                              previous_focus_id);
-  if (punit) {
-    set_unit_focus_and_select(punit);
-  }
+  int i = 0;
+
+  /* Could use unit_list_copy here instead. Just having safe genlists
+   * wouldn't be sufficient since we don't want to skip units already
+   * removed from focus... */
+  unit_list_iterate_safe(previous_focus, punit) {
+    if (i == 0) {
+      set_unit_focus(punit);
+    } else {
+      add_unit_focus(punit);
+    }
+    i++;
+  } unit_list_iterate_safe_end;
 }
 
 /**************************************************************************
@@ -1868,10 +2095,11 @@
 **************************************************************************/
 void key_unit_move(enum direction8 gui_dir)
 {
-  if (punit_focus) {
+  unit_list_iterate(get_units_in_focus(), punit) {
     enum direction8 map_dir = gui_to_map_dir(gui_dir);
-    request_move_unit_direction(punit_focus, map_dir);
-  }
+
+    request_move_unit_direction(punit, map_dir);
+  } unit_list_iterate_end;
 }
 
 /**************************************************************************
@@ -1879,9 +2107,9 @@
 **************************************************************************/
 void key_unit_build_city(void)
 {
-  if (punit_focus) {
-    request_unit_build_city(punit_focus);
-  }
+  unit_list_iterate(get_units_in_focus(), punit) {
+    request_unit_build_city(punit);
+  } unit_list_iterate_end;
 }
 
 /**************************************************************************
@@ -1889,9 +2117,11 @@
 **************************************************************************/
 void key_unit_build_wonder(void)
 {
-  if (punit_focus && unit_flag(punit_focus, F_HELP_WONDER)) {
-    request_unit_caravan_action(punit_focus, PACKET_UNIT_HELP_BUILD_WONDER);
-  }
+  unit_list_iterate(get_units_in_focus(), punit) {
+    if (unit_flag(punit, F_HELP_WONDER)) {
+      request_unit_caravan_action(punit, PACKET_UNIT_HELP_BUILD_WONDER);
+    }
+  } unit_list_iterate_end;
 }
 
 /**************************************************************************
@@ -1899,9 +2129,7 @@
 **************************************************************************/
 void key_unit_connect(enum unit_activity activity)
 {
-  if (punit_focus) {
-    request_unit_connect(activity);
-  }
+  request_unit_connect(activity);
 }
 
 /**************************************************************************
@@ -1910,13 +2138,17 @@
 void key_unit_diplomat_actions(void)
 {
   struct city *pcity;          /* need pcity->id */
-  if (punit_focus
-     && is_diplomat_unit(punit_focus)
-     && (pcity = tile_get_city(punit_focus->tile))
-     && diplomat_handled_in_diplomat_dialog() != -1    /* confusing otherwise? 
*/
-     && diplomat_can_do_action(punit_focus, DIPLOMAT_ANY_ACTION,
-                              punit_focus->tile))
-     process_diplomat_arrival(punit_focus, pcity->id);
+  unit_list_iterate(get_units_in_focus(), punit) {
+    if (is_diplomat_unit(punit)
+       && (pcity = tile_get_city(punit->tile))
+       && diplomat_handled_in_diplomat_dialog() != -1    /* confusing 
otherwise? */
+       && diplomat_can_do_action(punit, DIPLOMAT_ANY_ACTION,
+                                 punit->tile)) {
+      process_diplomat_arrival(punit, pcity->id);
+      return;
+      /* FIXME: diplomat dialog for more than one unit at a time. */
+    }
+  } unit_list_iterate_end;
 }
 
 /**************************************************************************
@@ -1924,9 +2156,7 @@
 **************************************************************************/
 void key_unit_done(void)
 {
-  if (punit_focus) {
-    request_unit_move_done();
-  }
+  request_unit_move_done();
 }
 
 /**************************************************************************
@@ -1934,9 +2164,7 @@
 **************************************************************************/
 void key_unit_goto(void)
 {
-  if (punit_focus) {
-    request_unit_goto(ORDER_LAST);
-  }
+  request_unit_goto(ORDER_LAST);
 }
 
 /**************************************************************************
@@ -1944,9 +2172,7 @@
 **************************************************************************/
 void key_unit_nuke(void)
 {
-  if (punit_focus) {
-    request_unit_nuke(punit_focus);
-  }
+  request_unit_nuke(get_units_in_focus());
 }
 
 /**************************************************************************
@@ -1954,9 +2180,7 @@
 **************************************************************************/
 void key_unit_paradrop(void)
 {
-  if (punit_focus && can_unit_paradrop(punit_focus)) {
-    request_unit_paradrop(punit_focus);
-  }
+  request_unit_paradrop(get_units_in_focus());
 }
 
 /**************************************************************************
@@ -1964,9 +2188,7 @@
 **************************************************************************/
 void key_unit_patrol(void)
 {
-  if (punit_focus) {
-    request_unit_patrol();
-  }
+  request_unit_patrol();
 }
 
 /**************************************************************************
@@ -1974,9 +2196,11 @@
 **************************************************************************/
 void key_unit_traderoute(void)
 {
-  if (punit_focus && unit_flag(punit_focus, F_TRADE_ROUTE)) {
-    request_unit_caravan_action(punit_focus, PACKET_UNIT_ESTABLISH_TRADE);
-  }
+  unit_list_iterate(get_units_in_focus(), punit) {
+    if (unit_flag(punit, F_TRADE_ROUTE)) {
+      request_unit_caravan_action(punit, PACKET_UNIT_ESTABLISH_TRADE);
+    }
+  } unit_list_iterate_end;
 }
 
 /**************************************************************************
@@ -1984,9 +2208,9 @@
 **************************************************************************/
 void key_unit_unload_all(void)
 {
-  if (punit_focus) {
-    request_unit_unload_all(punit_focus);
-  }
+  unit_list_iterate(get_units_in_focus(), punit) {
+    request_unit_unload_all(punit);
+  } unit_list_iterate_end;
 }
 
 /**************************************************************************
@@ -1994,9 +2218,7 @@
 **************************************************************************/
 void key_unit_wait(void)
 {
-  if (punit_focus) {
-    request_unit_wait(punit_focus);
-  }
+  request_units_wait(get_units_in_focus());
 }
 
 /**************************************************************************
@@ -2004,9 +2226,9 @@
 ***************************************************************************/
 void key_unit_wakeup_others(void)
 {
-  if (punit_focus) {
-    request_unit_wakeup(punit_focus);
-  }
+  unit_list_iterate(get_units_in_focus(), punit) {
+    request_unit_wakeup(punit);
+  } unit_list_iterate_end;
 }
 
 /**************************************************************************
@@ -2014,10 +2236,11 @@
 **************************************************************************/
 void key_unit_airbase(void)
 {
-  if (punit_focus &&
-      can_unit_do_activity(punit_focus, ACTIVITY_AIRBASE)) {
-    request_new_unit_activity(punit_focus, ACTIVITY_AIRBASE);
-  }
+  unit_list_iterate(get_units_in_focus(), punit) {
+    if (can_unit_do_activity(punit, ACTIVITY_AIRBASE)) {
+      request_new_unit_activity(punit, ACTIVITY_AIRBASE);
+    }
+  } unit_list_iterate_end;
 }
 
 /**************************************************************************
@@ -2025,10 +2248,11 @@
 **************************************************************************/
 void key_unit_auto_explore(void)
 {
-  if (punit_focus &&
-      can_unit_do_activity(punit_focus, ACTIVITY_EXPLORE)) {
-    request_new_unit_activity(punit_focus, ACTIVITY_EXPLORE);
-  }
+  unit_list_iterate(get_units_in_focus(), punit) {
+    if (can_unit_do_activity(punit, ACTIVITY_EXPLORE)) {
+      request_new_unit_activity(punit, ACTIVITY_EXPLORE);
+    }
+  } unit_list_iterate_end;
 }
 
 /**************************************************************************
@@ -2037,9 +2261,11 @@
 **************************************************************************/
 void key_unit_auto_settle(void)
 {
-  if (punit_focus && can_unit_do_autosettlers(punit_focus)) {
-    request_unit_autosettlers(punit_focus);
-  }
+  unit_list_iterate(get_units_in_focus(), punit) {
+    if (can_unit_do_autosettlers(punit)) {
+      request_unit_autosettlers(punit);
+    }
+  } unit_list_iterate_end;
 }
 
 /**************************************************************************
@@ -2047,9 +2273,9 @@
 **************************************************************************/
 void key_unit_disband(void)
 {
-  if (punit_focus) {
-    request_unit_disband(punit_focus);
-  }
+  unit_list_iterate(get_units_in_focus(), punit) {
+    request_unit_disband(punit);
+  } unit_list_iterate_end;
 }
 
 /**************************************************************************
@@ -2057,10 +2283,11 @@
 **************************************************************************/
 void key_unit_fallout(void)
 {
-  if (punit_focus &&
-      can_unit_do_activity(punit_focus, ACTIVITY_FALLOUT)) {
-    request_new_unit_activity(punit_focus, ACTIVITY_FALLOUT);
-  }
+  unit_list_iterate(get_units_in_focus(), punit) {
+    if (can_unit_do_activity(punit, ACTIVITY_FALLOUT)) {
+      request_new_unit_activity(punit, ACTIVITY_FALLOUT);
+    }
+  } unit_list_iterate_end;
 }
 
 /**************************************************************************
@@ -2068,10 +2295,11 @@
 **************************************************************************/
 void key_unit_fortify(void)
 {
-  if (punit_focus &&
-      can_unit_do_activity(punit_focus, ACTIVITY_FORTIFYING)) {
-    request_new_unit_activity(punit_focus, ACTIVITY_FORTIFYING);
-  }
+  unit_list_iterate(get_units_in_focus(), punit) {
+    if (can_unit_do_activity(punit, ACTIVITY_FORTIFYING)) {
+      request_new_unit_activity(punit, ACTIVITY_FORTIFYING);
+    }
+  } unit_list_iterate_end;
 }
 
 /**************************************************************************
@@ -2079,10 +2307,11 @@
 **************************************************************************/
 void key_unit_fortress(void)
 {
-  if (punit_focus &&
-      can_unit_do_activity(punit_focus, ACTIVITY_FORTRESS)) {
-    request_new_unit_activity(punit_focus, ACTIVITY_FORTRESS);
-  }
+  unit_list_iterate(get_units_in_focus(), punit) {
+    if (can_unit_do_activity(punit, ACTIVITY_FORTRESS)) {
+      request_new_unit_activity(punit, ACTIVITY_FORTRESS);
+    }
+  } unit_list_iterate_end;
 }
 
 /**************************************************************************
@@ -2090,9 +2319,9 @@
 **************************************************************************/
 void key_unit_homecity(void)
 {
-  if (punit_focus) {
-    request_unit_change_homecity(punit_focus);
-  }
+  unit_list_iterate(get_units_in_focus(), punit) {
+    request_unit_change_homecity(punit);
+  } unit_list_iterate_end;
 }
 
 /**************************************************************************
@@ -2100,10 +2329,11 @@
 **************************************************************************/
 void key_unit_irrigate(void)
 {
-  if (punit_focus &&
-      can_unit_do_activity(punit_focus, ACTIVITY_IRRIGATE)) {
-    request_new_unit_activity(punit_focus, ACTIVITY_IRRIGATE);
-  }
+  unit_list_iterate(get_units_in_focus(), punit) {
+    if (can_unit_do_activity(punit, ACTIVITY_IRRIGATE)) {
+      request_new_unit_activity(punit, ACTIVITY_IRRIGATE);
+    }
+  } unit_list_iterate_end;
 }
 
 /**************************************************************************
@@ -2111,10 +2341,11 @@
 **************************************************************************/
 void key_unit_mine(void)
 {
-  if (punit_focus &&
-      can_unit_do_activity(punit_focus, ACTIVITY_MINE)) {
-    request_new_unit_activity(punit_focus, ACTIVITY_MINE);
-  }
+  unit_list_iterate(get_units_in_focus(), punit) {
+    if (can_unit_do_activity(punit, ACTIVITY_MINE)) {
+      request_new_unit_activity(punit, ACTIVITY_MINE);
+    }
+  } unit_list_iterate_end;
 }
 
 /**************************************************************************
@@ -2122,10 +2353,11 @@
 **************************************************************************/
 void key_unit_pillage(void)
 {
-  if (punit_focus &&
-      can_unit_do_activity(punit_focus, ACTIVITY_PILLAGE)) {
-    request_unit_pillage(punit_focus);
-  }
+  unit_list_iterate(get_units_in_focus(), punit) {
+    if (can_unit_do_activity(punit, ACTIVITY_PILLAGE)) {
+      request_unit_pillage(punit);
+    }
+  } unit_list_iterate_end;
 }
 
 /**************************************************************************
@@ -2133,10 +2365,11 @@
 **************************************************************************/
 void key_unit_pollution(void)
 {
-  if (punit_focus &&
-      can_unit_do_activity(punit_focus, ACTIVITY_POLLUTION)) {
-    request_new_unit_activity(punit_focus, ACTIVITY_POLLUTION);
-  }
+  unit_list_iterate(get_units_in_focus(), punit) {
+    if (can_unit_do_activity(punit, ACTIVITY_POLLUTION)) {
+      request_new_unit_activity(punit, ACTIVITY_POLLUTION);
+    }
+  } unit_list_iterate_end;
 }
 
 /**************************************************************************
@@ -2144,12 +2377,13 @@
 **************************************************************************/
 void key_unit_road(void)
 {
-  if (punit_focus) {
-    if(can_unit_do_activity(punit_focus, ACTIVITY_ROAD))
-      request_new_unit_activity(punit_focus, ACTIVITY_ROAD);
-    else if(can_unit_do_activity(punit_focus, ACTIVITY_RAILROAD))
-      request_new_unit_activity(punit_focus, ACTIVITY_RAILROAD);
-  }
+  unit_list_iterate(get_units_in_focus(), punit) {
+    if (can_unit_do_activity(punit, ACTIVITY_ROAD)) {
+      request_new_unit_activity(punit, ACTIVITY_ROAD);
+    } else if (can_unit_do_activity(punit, ACTIVITY_RAILROAD)) {
+      request_new_unit_activity(punit, ACTIVITY_RAILROAD);
+    }
+  } unit_list_iterate_end;
 }
 
 /**************************************************************************
@@ -2157,10 +2391,11 @@
 **************************************************************************/
 void key_unit_sentry(void)
 {
-  if (punit_focus &&
-      can_unit_do_activity(punit_focus, ACTIVITY_SENTRY)) {
-    request_new_unit_activity(punit_focus, ACTIVITY_SENTRY);
-  }
+  unit_list_iterate(get_units_in_focus(), punit) {
+    if (can_unit_do_activity(punit, ACTIVITY_SENTRY)) {
+      request_new_unit_activity(punit, ACTIVITY_SENTRY);
+    }
+  } unit_list_iterate_end;
 }
 
 /**************************************************************************
@@ -2168,12 +2403,76 @@
 **************************************************************************/
 void key_unit_transform(void)
 {
-  if (punit_focus &&
-      can_unit_do_activity(punit_focus, ACTIVITY_TRANSFORM)) {
-    request_new_unit_activity(punit_focus, ACTIVITY_TRANSFORM);
+  unit_list_iterate(get_units_in_focus(), punit) {
+    if (can_unit_do_activity(punit, ACTIVITY_TRANSFORM)) {
+      request_new_unit_activity(punit, ACTIVITY_TRANSFORM);
+    }
+  } unit_list_iterate_end;
+}
+
+/****************************************************************************
+  Assign all focus units to this battlegroup.
+****************************************************************************/
+void key_unit_assign_battlegroup(int battlegroup, bool append)
+{
+  if (game.player_ptr && can_client_issue_orders()
+      && battlegroups >= 0 && battlegroup < MAX_NUM_BATTLEGROUPS) {
+    if (!append) {
+      unit_list_iterate_safe(battlegroups[battlegroup], punit) {
+       if (!unit_is_in_focus(punit)) {
+         punit->battlegroup = BATTLEGROUP_NONE;
+         dsend_packet_unit_battlegroup(&aconnection,
+                                       punit->id, BATTLEGROUP_NONE);
+         refresh_unit_mapcanvas(punit, punit->tile, TRUE, FALSE);
+         unit_list_unlink(battlegroups[battlegroup], punit);
+       }
+      } unit_list_iterate_safe_end;
+    }
+    unit_list_iterate(get_units_in_focus(), punit) {
+      if (punit->battlegroup != battlegroup) {
+       if (punit->battlegroup >= 0
+           && punit->battlegroup < MAX_NUM_BATTLEGROUPS) {
+         unit_list_unlink(battlegroups[punit->battlegroup], punit);
+       }
+       punit->battlegroup = battlegroup;
+       dsend_packet_unit_battlegroup(&aconnection,
+                                     punit->id, battlegroup);
+       unit_list_append(battlegroups[battlegroup], punit);
+       refresh_unit_mapcanvas(punit, punit->tile, TRUE, FALSE);
+      }
+    } unit_list_iterate_end;
+    unit_list_iterate(battlegroups[battlegroup], punit) {
+      add_unit_focus(punit);
+    } unit_list_iterate_end;
   }
 }
 
+/****************************************************************************
+  Bring this battlegroup into focus.
+****************************************************************************/
+void key_unit_select_battlegroup(int battlegroup, bool append)
+{
+  if (game.player_ptr && can_client_change_view()
+      && battlegroups >= 0 && battlegroup < MAX_NUM_BATTLEGROUPS) {
+    int i = 0;
+
+    if (unit_list_size(battlegroups[battlegroup]) == 0 && !append) {
+      set_unit_focus(NULL);
+      return;
+    }
+
+    /* FIXME: this is very inefficient and can be improved. */
+    unit_list_iterate(battlegroups[battlegroup], punit) {
+      if (i == 0 && !append) {
+       set_unit_focus(punit);
+      } else {
+       add_unit_focus(punit);
+      }
+      i++;
+    } unit_list_iterate_end;
+  }
+}
+
 /**************************************************************************
   Toggle drawing of city outlines.
 **************************************************************************/
@@ -2324,10 +2623,9 @@
 **************************************************************************/
 void key_quickselect(enum quickselect_type qtype)
 {
-  struct unit *punit;
+  unit_list_iterate(get_units_in_focus(), punit) {
+    struct unit *punit2 = quickselect(punit->tile, qtype);
 
-  if(punit_focus) {
-    punit = quickselect(punit_focus->tile, qtype);
-    set_unit_focus_and_select(punit);
-  }
+    set_unit_focus_and_select(punit2);
+  } unit_list_iterate_end;
 }
Index: client/gui-gtk-2.0/gotodlg.c
===================================================================
--- client/gui-gtk-2.0/gotodlg.c        (revision 11168)
+++ client/gui-gtk-2.0/gotodlg.c        (working copy)
@@ -82,11 +82,9 @@
       struct city *pdestcity = get_selected_city();
 
       if (pdestcity) {
-        struct unit *punit = get_unit_in_focus();
-
-        if (punit) {
+       unit_list_iterate(get_units_in_focus(), punit) {
           request_unit_airlift(punit, pdestcity);
-        }
+        } unit_list_iterate_end;
       }
     }
     break;
@@ -96,11 +94,9 @@
       struct city *pdestcity = get_selected_city();
 
       if (pdestcity) {
-        struct unit *punit = get_unit_in_focus();
-
-        if (punit) {
+       unit_list_iterate(get_units_in_focus(), punit) {
           send_goto_tile(punit, pdestcity->tile);
-        }
+        } unit_list_iterate_end;
       }
     }
     break;
@@ -206,7 +202,7 @@
 *****************************************************************/
 void popup_goto_dialog(void)
 {
-  if (!can_client_issue_orders() || !get_unit_in_focus()) {
+  if (!can_client_issue_orders() || get_num_units_in_focus() == 0) {
     return;
   }
 
@@ -270,9 +266,17 @@
   struct city *pdestcity;
 
   if((pdestcity = get_selected_city())) {
-    struct unit *punit = get_unit_in_focus();
+    bool can_airlift = FALSE;
+
+    unit_list_iterate(get_units_in_focus(), punit) {
+      if (unit_can_airlift_to(punit, pdestcity)) {
+       can_airlift = TRUE;
+       break;
+      }
+    } unit_list_iterate_end;
+
     center_tile_mapcanvas(pdestcity->tile);
-    if(punit && unit_can_airlift_to(punit, pdestcity)) {
+    if (can_airlift) {
       gtk_dialog_set_response_sensitive(GTK_DIALOG(dshell), CMD_AIRLIFT, TRUE);
       return;
     }
Index: client/gui-gtk-2.0/mapview.c
===================================================================
--- client/gui-gtk-2.0/mapview.c        (revision 11168)
+++ client/gui-gtk-2.0/mapview.c        (working copy)
@@ -166,21 +166,35 @@
   Also calls update_unit_pix_label() to update the icons for units on this
   square.
 **************************************************************************/
-void update_unit_info_label(struct unit *punit)
+void update_unit_info_label(struct unit_list *punitlist)
 {
   GtkWidget *label;
 
   label = gtk_frame_get_label_widget(GTK_FRAME(unit_info_frame));
   gtk_label_set_text(GTK_LABEL(label),
-                    get_unit_info_label_text1(punit));
+                    get_unit_info_label_text1(punitlist));
 
   gtk_label_set_text(GTK_LABEL(unit_info_label),
-                    get_unit_info_label_text2(punit));
+                    get_unit_info_label_text2(punitlist));
 
-  if(punit) {
-    if (hover_unit != punit->id)
-      set_hover_state(NULL, HOVER_NONE, ACTIVITY_LAST, ORDER_LAST);
+  if (unit_list_size(punitlist) > 0) {
+#if 0 /* I sure as hell hope this hack isn't needed */
+    if (unit_list_size(hover_units) > 0) {
+      bool is_hover = FALSE;
 
+      unit_list_iterate(hover_units, punit2) {
+       if (punit2 == punit) {
+         is_hover = TRUE;
+         break;
+       }
+      } unit_list_iterate_end;
+
+      if (!is_hover) {
+       set_hover_state(NULL, HOVER_NONE, ACTIVITY_LAST, ORDER_LAST);
+      }
+    }
+#endif
+
     switch (hover_state) {
     case HOVER_NONE:
       gdk_window_set_cursor (root_window, NULL);
@@ -202,7 +216,7 @@
   } else {
     gdk_window_set_cursor(root_window, NULL);
   }
-  update_unit_pix_label(punit);
+  update_unit_pix_label(punitlist);
 }
 
 
Index: client/gui-gtk-2.0/gui_main.c
===================================================================
--- client/gui-gtk-2.0/gui_main.c       (revision 11168)
+++ client/gui-gtk-2.0/gui_main.c       (working copy)
@@ -440,7 +440,57 @@
       }
     }
 
+    assert(MAX_NUM_BATTLEGROUPS == 4);
+#define BATTLEGROUP_CASE(num)                                              \
+  if (ev->state & GDK_CONTROL_MASK) {                                      \
+    key_unit_assign_battlegroup((num), (ev->state & GDK_SHIFT_MASK) != 0);  \
+  } else {                                                                 \
+    key_unit_select_battlegroup((num), (ev->state & GDK_SHIFT_MASK) != 0);  \
+  }
+#define BATTLEGROUP_SHIFT_CASE(num)                                        \
+  if (ev->state & GDK_CONTROL_MASK) {                                      \
+    key_unit_assign_battlegroup((num), TRUE);                              \
+  } else {                                                                 \
+    key_unit_select_battlegroup((num), TRUE);                              \
+  }
+    
     switch (ev->keyval) {
+    case GDK_1:
+      BATTLEGROUP_CASE(0);
+      break;
+
+    case GDK_2:
+      BATTLEGROUP_CASE(1);
+      break;
+
+    case GDK_3:
+      BATTLEGROUP_CASE(2);
+      break;
+
+    case GDK_4:
+      BATTLEGROUP_CASE(3);
+      break;
+
+    case GDK_exclam:
+      /* Shift + 1 */
+      BATTLEGROUP_SHIFT_CASE(0);
+      break;
+
+    case GDK_at:
+      /* Shift + 2 */
+      BATTLEGROUP_SHIFT_CASE(1);
+      break;
+
+    case GDK_numbersign:
+      /* Shift + 3 */
+      BATTLEGROUP_SHIFT_CASE(2);
+      break;
+
+    case GDK_dollar:
+      /* Shift + 4 */
+      BATTLEGROUP_SHIFT_CASE(3);
+      break;
+
       case GDK_KP_Up:
       case GDK_8:
       case GDK_KP_8:
@@ -460,25 +510,21 @@
        break;
 
       case GDK_KP_Page_Down:
-      case GDK_3:
       case GDK_KP_3:
        key_unit_move(DIR8_SOUTHEAST);
        break;
 
       case GDK_KP_Down:
-      case GDK_2:
       case GDK_KP_2:
        key_unit_move(DIR8_SOUTH);
        break;
 
       case GDK_KP_End:
-      case GDK_1:
       case GDK_KP_1:
        key_unit_move(DIR8_SOUTHWEST);
        break;
 
       case GDK_KP_Left:
-      case GDK_4:
       case GDK_KP_4:
        key_unit_move(DIR8_WEST);
        break;
@@ -691,8 +737,12 @@
    * but we have to make the *internal* state consistent). */
   gtk_widget_hide(more_arrow_pixmap);
   set_unit_icons_more_arrow(FALSE);
-  set_unit_icon(-1, get_unit_in_focus());
-  update_unit_pix_label(get_unit_in_focus());
+  if (get_num_units_in_focus() == 1) {
+    set_unit_icon(-1, unit_list_get(get_units_in_focus(), 0));
+  } else {
+    set_unit_icon(-1, NULL);
+  }
+  update_unit_pix_label(get_units_in_focus());
 }
 
 /**************************************************************************
@@ -1488,7 +1538,7 @@
   struct unit *punit;
 
   if (i == -1) {
-    if ((punit = get_unit_in_focus())) {
+    if (unit_is_in_focus(punit)) {
       /* Clicking on the currently selected unit will center it. */
       center_tile_mapcanvas(punit->tile);
     }
Index: client/gui-gtk-2.0/menu.c
===================================================================
--- client/gui-gtk-2.0/menu.c   (revision 11168)
+++ client/gui-gtk-2.0/menu.c   (working copy)
@@ -143,6 +143,7 @@
   MENU_ORDER_DISBAND,
   MENU_ORDER_DIPLOMAT_DLG,
   MENU_ORDER_NUKE,
+  MENU_ORDER_SELECT_SAME_TYPE,
   MENU_ORDER_WAIT,
   MENU_ORDER_DONE,
 
@@ -395,25 +396,31 @@
                                 guint callback_action, GtkWidget *widget)
 {
   switch(callback_action) {
-   case MENU_ORDER_BUILD_CITY:
-    if (get_unit_in_focus()) {
-      struct unit *punit = get_unit_in_focus();
+  case MENU_ORDER_SELECT_SAME_TYPE:
+    request_unit_select_same_type(get_units_in_focus());
+    break;
+  case MENU_ORDER_BUILD_CITY:
+    unit_list_iterate(get_units_in_focus(), punit) {
+      /* FIXME: this can provide different actions for different units...
+       * not good! */
       /* Enable the button for adding to a city in all cases, so we
         get an eventual error message from the server if we try. */
       if (can_unit_add_or_build_city(punit)) {
-       key_unit_build_city();
+       request_unit_build_city(punit);
       } else {
-       key_unit_build_wonder();
+       request_unit_caravan_action(punit, PACKET_UNIT_HELP_BUILD_WONDER);
       }
-    }
+    } unit_list_iterate_end;
     break;
-   case MENU_ORDER_ROAD:
-    if (get_unit_in_focus()) {
-      if (unit_can_est_traderoute_here(get_unit_in_focus()))
+  case MENU_ORDER_ROAD:
+    unit_list_iterate(get_units_in_focus(), punit) {
+      /* FIXME: this can provide different actions for different units...
+       * not good! */
+      if (unit_can_est_traderoute_here(punit))
        key_unit_traderoute();
       else
        key_unit_road();
-    }
+    } unit_list_iterate_end;
     break;
    case MENU_ORDER_IRRIGATE:
     key_unit_irrigate();
@@ -424,24 +431,28 @@
    case MENU_ORDER_TRANSFORM:
     key_unit_transform();
     break;
-   case MENU_ORDER_FORTRESS:
-    if (get_unit_in_focus()) {
-      if (can_unit_do_activity(get_unit_in_focus(), ACTIVITY_FORTRESS))
+  case MENU_ORDER_FORTRESS:
+    unit_list_iterate(get_units_in_focus(), punit) {
+      /* FIXME: this can provide different actions for different units...
+       * not good! */
+      if (can_unit_do_activity(punit, ACTIVITY_FORTRESS))
        key_unit_fortress();
       else
        key_unit_fortify();
-    }
+    } unit_list_iterate_end;
     break;
    case MENU_ORDER_AIRBASE:
     key_unit_airbase(); 
     break;
    case MENU_ORDER_POLLUTION:
-    if (get_unit_in_focus()) {
-      if (can_unit_paradrop(get_unit_in_focus()))
+    unit_list_iterate(get_units_in_focus(), punit) {
+      /* FIXME: this can provide different actions for different units...
+       * not good! */
+      if (can_unit_paradrop(punit))
        key_unit_paradrop();
       else
        key_unit_pollution();
-    }
+    } unit_list_iterate_end;
     break;
    case MENU_ORDER_FALLOUT:
     key_unit_fallout();
@@ -459,16 +470,22 @@
     key_unit_unload_all();
     break;
   case MENU_ORDER_LOAD:
-    request_unit_load(get_unit_in_focus(), NULL);
+    unit_list_iterate(get_units_in_focus(), punit) {
+      request_unit_load(punit, NULL);
+    } unit_list_iterate_end;
     break;
   case MENU_ORDER_UNLOAD:
-    request_unit_unload(get_unit_in_focus());
+    unit_list_iterate(get_units_in_focus(), punit) {
+      request_unit_unload(punit);
+    } unit_list_iterate_end;
     break;
    case MENU_ORDER_WAKEUP_OTHERS:
     key_unit_wakeup_others();
     break;
-   case MENU_ORDER_AUTO_SETTLER:
-     request_unit_autosettlers(get_unit_in_focus());
+  case MENU_ORDER_AUTO_SETTLER:
+    unit_list_iterate(get_units_in_focus(), punit) {
+      request_unit_autosettlers(punit);
+    } unit_list_iterate_end;
     break;
    case MENU_ORDER_AUTO_EXPLORE:
     key_unit_auto_explore();
@@ -491,15 +508,16 @@
    case MENU_ORDER_GOTO:
     key_unit_goto();
     break;
-   case MENU_ORDER_GOTO_CITY:
-    if(get_unit_in_focus())
+  case MENU_ORDER_GOTO_CITY:
+    if (get_num_units_in_focus() > 0) {
       popup_goto_dialog();
-    break;
-   case MENU_ORDER_RETURN:
-    if (get_unit_in_focus()) {
-      request_unit_return(get_unit_in_focus());
     }
     break;
+  case MENU_ORDER_RETURN:
+    unit_list_iterate(get_units_in_focus(), punit) {
+      request_unit_return(punit);
+    } unit_list_iterate_end;
+    break;
    case MENU_ORDER_DISBAND:
     key_unit_disband();
     break;
@@ -853,6 +871,8 @@
        orders_menu_callback,   MENU_ORDER_NUKE                                 
        },
   { "/" N_("Orders") "/sep5",                          NULL,
        NULL,                   0,                                      
"<Separator>"   },
+  { "/" N_("Orders") "/" N_("Select same type"), "y",
+    orders_menu_callback, MENU_ORDER_SELECT_SAME_TYPE },
   { "/" N_("Orders") "/" N_("_Wait"),                  "w",
        orders_menu_callback,   MENU_ORDER_WAIT                                 
        },
   { "/" N_("Orders") "/" N_("Done"),                   "space",
@@ -1162,6 +1182,131 @@
   return tile_get_info_text(&newtile);
 }
 
+/****************************************************************************
+  Return TRUE if the function returns true for any of the units.
+****************************************************************************/
+static bool can_units_do(struct unit_list *punits,
+                        bool (can_fn)(const struct unit *punit))
+{
+  unit_list_iterate(punits, punit) {
+    if (can_fn(punit)) {
+      return TRUE;
+    }
+  } unit_list_iterate_end;
+
+  return FALSE;
+}
+
+/****************************************************************************
+  Returns TRUE if any of the units can do the activity.
+****************************************************************************/
+static bool can_units_do_activity(struct unit_list *punits,
+                                 enum unit_activity activity)
+{
+  unit_list_iterate(punits, punit) {
+    if (can_unit_do_activity(punit, activity)) {
+      return TRUE;
+    }
+  } unit_list_iterate_end;
+
+  return FALSE;
+}
+
+/****************************************************************************
+  Returns TRUE if any of the units can do the activity.
+****************************************************************************/
+static bool can_units_do_diplomat_action(struct unit_list *punits,
+                                        enum diplomat_actions action)
+{
+  unit_list_iterate(punits, punit) {
+    if (is_diplomat_unit(punit)
+       && diplomat_can_do_action(punit, action, punit->tile)) {
+      return TRUE;
+    }
+  } unit_list_iterate_end;
+
+  return FALSE;
+}
+
+/****************************************************************************
+  If has_flag is true, returns true iff any of the units have the flag.
+
+  If has_flag is false, returns true iff any of the units don't have the
+  flag.
+****************************************************************************/
+static bool units_have_flag(struct unit_list *punits,
+                           enum unit_flag_id flag,
+                           bool has_flag)
+{
+  unit_list_iterate(punits, punit) {
+    if (EQ(has_flag, unit_flag(punit, flag))) {
+      return TRUE;
+    }
+  } unit_list_iterate_end;
+
+  return FALSE;
+}
+
+/****************************************************************************
+
+****************************************************************************/
+static bool units_are_occupied(struct unit_list *punits)
+{
+  unit_list_iterate(punits, punit) {
+    if (get_transporter_occupancy(punit) > 0) {
+      return TRUE;
+    }
+  } unit_list_iterate_end;
+
+  return FALSE;
+}
+
+/****************************************************************************
+  Return TRUE iff any of these units can load.
+****************************************************************************/
+static bool units_can_load(struct unit_list *punits)
+{
+  unit_list_iterate(punits, punit) {
+    if (can_unit_load(punit,
+                     find_transporter_for_unit(punit, punit->tile))) {
+      return TRUE;
+    }
+  } unit_list_iterate_end;
+
+  return FALSE;
+}
+
+/****************************************************************************
+  Return TRUE iff any of these units can unload.
+****************************************************************************/
+static bool units_can_unload(struct unit_list *punits)
+{
+  unit_list_iterate(punits, punit) {
+    if (can_unit_unload(punit, find_unit_by_id(punit->transported_by))
+       && can_unit_exist_at_tile(punit, punit->tile)) {
+      return TRUE;
+    }
+  } unit_list_iterate_end;
+
+  return FALSE;
+}
+
+/****************************************************************************
+  Return TRUE iff any of the units' tiles have the activity running
+  on them.
+****************************************************************************/
+static bool units_have_activity_on_tile(struct unit_list *punits,
+                                       enum unit_activity activity)
+{
+  unit_list_iterate(punits, punit) {
+    if (is_unit_activity_on_tile(activity, punit->tile)) {
+      return TRUE;
+    }
+  } unit_list_iterate_end;
+
+  return FALSE;
+}
+
 /****************************************************************
 Note: the menu strings should contain underscores as in the
 menu_items struct. The underscores will be removed elsewhere if
@@ -1190,7 +1335,6 @@
     menus_set_sensitive("<main>/_View", FALSE);
     menus_set_sensitive("<main>/_Orders", FALSE);
   } else {
-    struct unit *punit;
     const char *path =
       menu_path_remove_uline("<main>/_Government/_Change Government");
     GtkWidget *parent = gtk_item_factory_get_widget(item_factory, path);
@@ -1282,13 +1426,14 @@
       return;
     }
 
-    if((punit=get_unit_in_focus())) {
+    if (get_num_units_in_focus() > 0) {
       const char *irrfmt = _("Change to %s (_I)");
       const char *minfmt = _("Change to %s (_M)");
       const char *transfmt = _("Transf_orm to %s");
       char irrtext[128], mintext[128], transtext[128];
       const char *roadtext;
       struct terrain *pterrain;
+      struct unit_list *punits = get_units_in_focus();
 
       sz_strlcpy(irrtext, _("Build _Irrigation"));
       sz_strlcpy(mintext, _("Build _Mine"));
@@ -1297,67 +1442,77 @@
       /* Enable the button for adding to a city in all cases, so we
         get an eventual error message from the server if we try. */
       menus_set_sensitive("<main>/_Orders/_Build City",
-                         can_unit_add_or_build_city(punit) ||
-                         unit_can_help_build_wonder_here(punit));
+               (can_units_do(punits, can_unit_add_or_build_city)
+                || can_units_do(punits, unit_can_help_build_wonder_here)));
       menus_set_sensitive("<main>/_Orders/Build _Road",
-                          (can_unit_do_activity(punit, ACTIVITY_ROAD) ||
-                           can_unit_do_activity(punit, ACTIVITY_RAILROAD) ||
-                           unit_can_est_traderoute_here(punit)));
+                          (can_units_do_activity(punits, ACTIVITY_ROAD)
+                           || can_units_do_activity(punits, ACTIVITY_RAILROAD)
+                           || can_units_do(punits,
+                                          unit_can_est_traderoute_here)));
       menus_set_sensitive("<main>/_Orders/Build _Irrigation",
-                          can_unit_do_activity(punit, ACTIVITY_IRRIGATE));
+                          can_units_do_activity(punits, ACTIVITY_IRRIGATE));
       menus_set_sensitive("<main>/_Orders/Build _Mine",
-                          can_unit_do_activity(punit, ACTIVITY_MINE));
+                          can_units_do_activity(punits, ACTIVITY_MINE));
       menus_set_sensitive("<main>/_Orders/Transf_orm Terrain",
-                         can_unit_do_activity(punit, ACTIVITY_TRANSFORM));
+                         can_units_do_activity(punits, ACTIVITY_TRANSFORM));
       menus_set_sensitive("<main>/_Orders/Build _Fortress",
-                          (can_unit_do_activity(punit, ACTIVITY_FORTRESS) ||
-                           can_unit_do_activity(punit, ACTIVITY_FORTIFYING)));
+                          (can_units_do_activity(punits, ACTIVITY_FORTRESS)
+                           || can_units_do_activity(punits,
+                                                   ACTIVITY_FORTIFYING)));
       menus_set_sensitive("<main>/_Orders/Build Airbas_e",
-                         can_unit_do_activity(punit, ACTIVITY_AIRBASE));
+                         can_units_do_activity(punits, ACTIVITY_AIRBASE));
       menus_set_sensitive("<main>/_Orders/Clean _Pollution",
-                          (can_unit_do_activity(punit, ACTIVITY_POLLUTION) ||
-                           can_unit_paradrop(punit)));
+                          (can_units_do_activity(punits, ACTIVITY_POLLUTION)
+                           || can_units_do(punits, can_unit_paradrop)));
       menus_set_sensitive("<main>/_Orders/Clean _Nuclear Fallout",
-                         can_unit_do_activity(punit, ACTIVITY_FALLOUT));
+                         can_units_do_activity(punits, ACTIVITY_FALLOUT));
       menus_set_sensitive("<main>/_Orders/_Sentry",
-                         can_unit_do_activity(punit, ACTIVITY_SENTRY));
+                         can_units_do_activity(punits, ACTIVITY_SENTRY));
       menus_set_sensitive("<main>/_Orders/Pillage",
-                         can_unit_do_activity(punit, ACTIVITY_PILLAGE));
+                         can_units_do_activity(punits, ACTIVITY_PILLAGE));
       menus_set_sensitive("<main>/_Orders/_Disband Unit",
-                         !unit_flag(punit, F_UNDISBANDABLE));
+                         units_have_flag(punits, F_UNDISBANDABLE, FALSE));
       menus_set_sensitive("<main>/_Orders/Make _Homecity",
-                         can_unit_change_homecity(punit));
+                         can_units_do(punits, can_unit_change_homecity));
       menus_set_sensitive("<main>/_Orders/_Unload Transporter",
-                         get_transporter_occupancy(punit) > 0);
+                         units_are_occupied(punits));
       menus_set_sensitive("<main>/_Orders/_Load",
-       can_unit_load(punit, find_transporter_for_unit(punit,
-                                                      punit->tile)));
+                         units_can_load(punits));
       menus_set_sensitive("<main>/_Orders/_Unload",
-       (can_unit_unload(punit, find_unit_by_id(punit->transported_by))
-        && can_unit_exist_at_tile(punit, punit->tile)));
+                         units_can_unload(punits));
       menus_set_sensitive("<main>/_Orders/Wake up o_thers", 
-                         is_unit_activity_on_tile(ACTIVITY_SENTRY,
-                                                   punit->tile));
+                         units_have_activity_on_tile(punits,
+                                                     ACTIVITY_SENTRY));
       menus_set_sensitive("<main>/_Orders/_Auto Settler",
-                          can_unit_do_autosettlers(punit));
+                          can_units_do(punits, can_unit_do_autosettlers));
       menus_set_sensitive("<main>/_Orders/Auto E_xplore",
-                          can_unit_do_activity(punit, ACTIVITY_EXPLORE));
+                          can_units_do_activity(punits, ACTIVITY_EXPLORE));
       menus_set_sensitive("<main>/_Orders/_Connect/_Road",
-                          can_unit_do_connect(punit, ACTIVITY_ROAD));
+                          can_units_do_connect(punits, ACTIVITY_ROAD));
       menus_set_sensitive("<main>/_Orders/_Connect/_Rail",
-                          can_unit_do_connect(punit, ACTIVITY_RAILROAD));
+                          can_units_do_connect(punits, ACTIVITY_RAILROAD));
       menus_set_sensitive("<main>/_Orders/_Connect/_Irrigate",
-                          can_unit_do_connect(punit, ACTIVITY_IRRIGATE));
+                          can_units_do_connect(punits, ACTIVITY_IRRIGATE));
       menus_set_sensitive("<main>/_Orders/Diplomat\\/Spy Actions",
-                          (is_diplomat_unit(punit)
-                           && diplomat_can_do_action(punit, 
DIPLOMAT_ANY_ACTION,
-                                                    punit->tile)));
+                         can_units_do_diplomat_action(punits,
+                                                      DIPLOMAT_ANY_ACTION));
       menus_set_sensitive("<main>/_Orders/Explode Nuclear",
-                         unit_flag(punit, F_NUCLEAR));
-      if (unit_flag(punit, F_HELP_WONDER))
+                         units_have_flag(punits, F_NUCLEAR, TRUE));
+      if (units_have_flag(punits, F_HELP_WONDER, TRUE))
        menus_rename("<main>/_Orders/_Build City", _("Help _Build Wonder"));
-      else if (unit_flag(punit, F_CITIES)) {
-       if (tile_get_city(punit->tile))
+      else if (units_have_flag(punits, F_CITIES, TRUE)) {
+       bool can_build = FALSE;
+
+       /* FIXME: this overloading doesn't work well with multiple focus
+        * units. */
+       unit_list_iterate(punits, punit) {
+         if (!punit->tile->city) {
+           can_build = TRUE;
+           break;
+         }
+       } unit_list_iterate_end;
+
+       if (can_build)
          menus_rename("<main>/_Orders/_Build City", _("Add to City (_B)"));
        else
          menus_rename("<main>/_Orders/_Build City", _("_Build City"));
@@ -1365,14 +1520,24 @@
       else 
        menus_rename("<main>/_Orders/_Build City", _("_Build City"));
  
-      if (unit_flag(punit, F_TRADE_ROUTE))
+      if (units_have_flag(punits, F_TRADE_ROUTE, TRUE))
        menus_rename("<main>/_Orders/Build _Road", _("Make Trade _Route"));
-      else if (unit_flag(punit, F_SETTLERS)) {
-       if (tile_has_special(punit->tile, S_ROAD)) {
+      else if (units_have_flag(punits, F_SETTLERS, TRUE)) {
+       bool has_road = FALSE;
+
+       /* FIXME: this overloading doesn't work well with multiple focus
+        * units. */
+       unit_list_iterate(punits, punit) {
+         if (tile_has_special(punit->tile, S_ROAD)) {
+           has_road = TRUE;
+           break;
+         }
+       } unit_list_iterate_end;
+
+       if (has_road) {
          roadtext = _("Build _Railroad");
          road_activity=ACTIVITY_RAILROAD;  
-       } 
-       else {
+       } else {
          roadtext = _("Build _Road");
          road_activity=ACTIVITY_ROAD;  
        }
@@ -1381,40 +1546,44 @@
       else
        menus_rename("<main>/_Orders/Build _Road", _("Build _Road"));
 
-      pterrain = punit->tile->terrain;
-      if (pterrain->irrigation_result != T_NONE
-         && pterrain->irrigation_result != pterrain) {
-       my_snprintf(irrtext, sizeof(irrtext), irrfmt,
-                   get_tile_change_menu_text(punit->tile,
-                                             ACTIVITY_IRRIGATE));
-      } else if (tile_has_special(punit->tile, S_IRRIGATION)
-                && player_knows_techs_with_flag(punit->owner,
-                                                TF_FARMLAND)) {
-       sz_strlcpy(irrtext, _("Bu_ild Farmland"));
+      if (unit_list_size(punits) == 1) {
+       struct unit *punit = unit_list_get(punits, 0);
+
+       pterrain = punit->tile->terrain;
+       if (pterrain->irrigation_result != T_NONE
+           && pterrain->irrigation_result != pterrain) {
+         my_snprintf(irrtext, sizeof(irrtext), irrfmt,
+                     get_tile_change_menu_text(punit->tile,
+                                               ACTIVITY_IRRIGATE));
+       } else if (tile_has_special(punit->tile, S_IRRIGATION)
+                  && player_knows_techs_with_flag(punit->owner,
+                                                  TF_FARMLAND)) {
+         sz_strlcpy(irrtext, _("Bu_ild Farmland"));
+       }
+       if (pterrain->mining_result != T_NONE
+           && pterrain->mining_result != pterrain) {
+         my_snprintf(mintext, sizeof(mintext), minfmt,
+                     get_tile_change_menu_text(punit->tile, ACTIVITY_MINE));
+       }
+       if (pterrain->transform_result != T_NONE
+           && pterrain->transform_result != pterrain) {
+         my_snprintf(transtext, sizeof(transtext), transfmt,
+                     get_tile_change_menu_text(punit->tile,
+                                               ACTIVITY_TRANSFORM));
+       }
       }
-      if (pterrain->mining_result != T_NONE
-         && pterrain->mining_result != pterrain) {
-       my_snprintf(mintext, sizeof(mintext), minfmt,
-                   get_tile_change_menu_text(punit->tile, ACTIVITY_MINE));
-      }
-      if (pterrain->transform_result != T_NONE
-         && pterrain->transform_result != pterrain) {
-       my_snprintf(transtext, sizeof(transtext), transfmt,
-                   get_tile_change_menu_text(punit->tile,
-                                             ACTIVITY_TRANSFORM));
-      }
 
       menus_rename("<main>/_Orders/Build _Irrigation", irrtext);
       menus_rename("<main>/_Orders/Build _Mine", mintext);
       menus_rename("<main>/_Orders/Transf_orm Terrain", transtext);
 
-      if (can_unit_do_activity(punit, ACTIVITY_FORTIFYING)) {
+      if (can_units_do_activity(punits, ACTIVITY_FORTIFYING)) {
        menus_rename("<main>/_Orders/Build _Fortress", _("_Fortify"));
       } else {
        menus_rename("<main>/_Orders/Build _Fortress", _("Build _Fortress"));
       }
 
-      if (unit_flag(punit, F_PARATROOPERS)) {
+      if (units_have_flag(punits, F_PARATROOPERS, TRUE)) {
        menus_rename("<main>/_Orders/Clean _Pollution", _("_Paradrop"));
       } else {
        menus_rename("<main>/_Orders/Clean _Pollution", _("Clean _Pollution"));
Index: client/gui-gtk-2.0/dialogs.c
===================================================================
--- client/gui-gtk-2.0/dialogs.c        (revision 11168)
+++ client/gui-gtk-2.0/dialogs.c        (working copy)
@@ -628,7 +628,7 @@
       -1);
   g_object_unref(pix);
 
-  if (punit == get_unit_in_focus()) {
+  if (unit_is_in_focus(punit)) {
     unit_select_path =
       gtk_tree_model_get_path(GTK_TREE_MODEL(unit_select_store), it);
   }
Index: client/gui-gtk-2.0/mapctrl.c
===================================================================
--- client/gui-gtk-2.0/mapctrl.c        (revision 11168)
+++ client/gui-gtk-2.0/mapctrl.c        (working copy)
@@ -253,9 +253,9 @@
     else if (ev->state & GDK_CONTROL_MASK) {
       action_button_pressed(ev->x, ev->y, SELECT_SEA);
     }
-    /* <SHIFT> + LMB: Copy Production. */
+    /* <SHIFT> + LMB: Append focus unit Production. */
     else if (ptile && (ev->state & GDK_SHIFT_MASK)) {
-      clipboard_copy_production(ptile);
+      action_button_pressed(ev->x, ev->y, SELECT_APPEND);
     }
     /* <ALT> + LMB: popit (same as middle-click) */
     /* FIXME: we need a general mechanism for letting freeciv work with
@@ -310,7 +310,8 @@
       }
       cancel_tile_hiliting();
       if (hover_state == HOVER_NONE) {
-        anchor_selection_rectangle(ev->x, ev->y);
+        anchor_selection_rectangle(ev->x, ev->y,
+                                  (ev->state & GDK_SHIFT_MASK) != 0);
         rbutton_down = TRUE; /* causes rectangle updates */
       }
     }
@@ -403,7 +404,8 @@
   if (can_client_change_view() && (ev->button == 3)) {
     center_tile_mapcanvas(map_pos_to_tile(xtile, ytile));
   } else if (can_client_issue_orders() && (ev->button == 1)) {
-    do_map_click(map_pos_to_tile(xtile, ytile), SELECT_POPUP);
+    do_map_click(map_pos_to_tile(xtile, ytile),
+                (ev->state & GDK_SHIFT_MASK) ? SELECT_POPUP : SELECT_APPEND);
   }
 
   return TRUE;
Index: client/text.c
===================================================================
--- client/text.c       (revision 11168)
+++ client/text.c       (working copy)
@@ -73,7 +73,7 @@
   const char *activity_text;
   struct city *pcity = ptile->city;
   struct unit *punit = find_visible_unit(ptile);
-  struct unit *pfocus_unit = get_unit_in_focus();
+  struct unit *pfocus_unit = get_focus_unit_on_tile(ptile);
   const char *diplo_nation_plural_adjectives[DS_LAST] =
     {Q_("?nation:Neutral"), Q_("?nation:Hostile"),
      "" /* unused, DS_CEASEFIRE*/,
@@ -623,16 +623,17 @@
 
   FIXME: this should be renamed.
 ****************************************************************************/
-const char *get_unit_info_label_text1(struct unit *punit)
+const char *get_unit_info_label_text1(struct unit_list *punits)
 {
   static struct astring str = ASTRING_INIT;
+  int count = unit_list_size(punits);
 
   astr_clear(&str);
 
-  if (punit) {
-    struct unit_type *ptype = unit_type(punit);
-
-    astr_add(&str, "%s", ptype->name);
+  if (count == 1) {
+    astr_add(&str, "%s", unit_list_get(punits, 0)->type->name);
+  } else {
+    astr_add(&str, PL_("%d unit", "%d units", count), count);
   }
   return str.str;
 }
@@ -642,27 +643,47 @@
 
   FIXME: this should be renamed.
 ****************************************************************************/
-const char *get_unit_info_label_text2(struct unit *punit)
+const char *get_unit_info_label_text2(struct unit_list *punits)
 {
   static struct astring str = ASTRING_INIT;
+  int count = unit_list_size(punits);
 
   astr_clear(&str);
 
   /* This text should always have the same number of lines.  Otherwise the
    * GUI widgets may be confused and try to resize themselves. */
-  if (punit) {
+
+  /* Line 1. Goto or activity text. */
+  if (count > 0 && unit_list_size(hover_units) > 0) {
+    int min, max;
+
+    goto_get_turns(&min, &max);
+    if (min == max) {
+      astr_add_line(&str, _("Turns to target: %d"), max);
+    } else {
+      astr_add_line(&str, _("Turns to target: %d to %d"), min, max);
+    }
+  } else if (count == 1) {
+    astr_add_line(&str, "%s",
+                 unit_activity_text(unit_list_get(punits, 0)));
+  } else if (count > 1) {
+    astr_add_line(&str, PL_("%d unit selected",
+                           "%d units selected",
+                           count),
+                 count);
+  } else {
+    astr_add_line(&str, _("No units selected."));
+  }
+
+  /* Lines 2, 3, and 4 vary. */
+  if (count == 1) {
+    struct unit *punit = unit_list_get(punits, 0);
     struct city *pcity =
        player_find_city_by_id(punit->owner, punit->homecity);
     int infracount;
     bv_special infrastructure =
       get_tile_infrastructure_set(punit->tile, &infracount);
 
-    if (hover_unit == punit->id) {
-      astr_add_line(&str, _("Turns to target: %d"), get_goto_turns());
-    } else {
-      astr_add_line(&str, "%s", unit_activity_text(punit));
-    }
-
     astr_add_line(&str, "%s", tile_get_info_text(punit->tile));
     if (infracount > 0) {
       astr_add_line(&str, "%s", get_infrastructure_text(infrastructure));
@@ -674,12 +695,79 @@
     } else {
       astr_add_line(&str, " ");
     }
+  } else if (count > 1) {
+    int mil = 0, nonmil = 0;
+    int types_count[U_LAST], i;
+    struct unit_type *top[3];
+
+    memset(types_count, 0, sizeof(types_count));
+    unit_list_iterate(punits, punit) {
+      if (unit_flag(punit, F_NONMIL)) {
+       nonmil++;
+      } else {
+       mil++;
+      }
+      types_count[punit->type->index]++;
+    } unit_list_iterate_end;
+
+    top[0] = top[1] = top[2] = NULL;
+    unit_type_iterate(utype) {
+      if (!top[2]
+         || types_count[top[2]->index] < types_count[utype->index]) {
+       top[2] = utype;
+
+       if (!top[1]
+           || types_count[top[1]->index] < types_count[top[2]->index]) {
+         top[2] = top[1];
+         top[1] = utype;
+
+         if (!top[0]
+             || types_count[top[0]->index] < types_count[utype->index]) {
+           top[1] = top[0];
+           top[0] = utype;
+         }
+       }
+      }
+    } unit_type_iterate_end;
+
+    for (i = 0; i < 3; i++) {
+      if (top[i] && types_count[top[i]->index] > 0) {
+       if (unit_type_flag(top[i], F_NONMIL)) {
+         nonmil -= types_count[top[i]->index];
+       } else {
+         mil -= types_count[top[i]->index];
+       }
+       astr_add_line(&str, "%d: %s",
+                     types_count[top[i]->index], top[i]->name);
+      } else {
+       astr_add_line(&str, " ");
+      }
+    }
+
+    if (nonmil > 0 && mil > 0) {
+      astr_add_line(&str, _("Others: %d civil; %d military"), nonmil, mil);
+    } else if (nonmil > 0) {
+      astr_add_line(&str, _("Others: %d civilian"), nonmil);
+    } else if (mil > 0) {
+      astr_add_line(&str, _("Others: %d military"), mil);
+    } else {
+      astr_add_line(&str, " ");
+    }
+  } else {
+    astr_add_line(&str, " ");
+    astr_add_line(&str, " ");
+    astr_add_line(&str, " ");
+  }
+
+  /* Line 5. Debug text. */
 #ifdef DEBUG
-    astr_add_line(&str, "(Unit ID %d)", punit->id);
-#endif
+  if (count == 1) {
+    astr_add_line(&str, "(Unit ID %d)", unit_list_get(punits, 0)->id);
   } else {
-    astr_add(&str, "\n\n\n");
+    astr_add_line(&str, " ");
   }
+#endif
+
   return str.str;
 }
 
Index: client/control.h
===================================================================
--- client/control.h    (revision 11168)
+++ client/control.h    (working copy)
@@ -26,13 +26,17 @@
 
 /* Selecting unit from a stack without popup. */
 enum quickselect_type {
-  SELECT_POPUP = 0, SELECT_SEA, SELECT_LAND
+  SELECT_POPUP = 0, SELECT_SEA, SELECT_LAND, SELECT_APPEND
 };
 
 void control_init(void);
 void control_done(void);
+void control_unit_killed(struct unit *punit);
 
-extern int hover_unit; /* unit hover_state applies to */
+void unit_change_battlegroup(struct unit *punit, int battlegroup);
+void unit_register_battlegroup(struct unit *punit);
+
+extern struct unit_list *hover_units; /* unit hover_state applies to */
 extern enum cursor_hover_state hover_state;
 extern enum unit_activity connect_activity;
 extern enum unit_orders goto_last_order;
@@ -44,12 +48,12 @@
 void do_unit_goto(struct tile *ptile);
 void do_unit_nuke(struct unit *punit);
 void do_unit_paradrop_to(struct unit *punit, struct tile *ptile);
-void do_unit_patrol_to(struct unit *punit, struct tile *ptile);
-void do_unit_connect(struct unit *punit, struct tile *ptile,
+void do_unit_patrol_to(struct tile *ptile);
+void do_unit_connect(struct tile *ptile,
                     enum unit_activity activity);
 void do_map_click(struct tile *ptile, enum quickselect_type qtype);
 
-void set_hover_state(struct unit *punit, enum cursor_hover_state state,
+void set_hover_state(struct unit_list *punits, enum cursor_hover_state state,
                     enum unit_activity connect_activity,
                     enum unit_orders goto_last_order);
 void request_center_focus_unit(void);
@@ -69,8 +73,8 @@
 void request_unit_fortify(struct unit *punit);
 void request_unit_goto(enum unit_orders last_order);
 void request_unit_move_done(void);
-void request_unit_nuke(struct unit *punit);
-void request_unit_paradrop(struct unit *punit);
+void request_unit_nuke(struct unit_list *punits);
+void request_unit_paradrop(struct unit_list *punits);
 void request_unit_patrol(void);
 void request_unit_pillage(struct unit *punit);
 void request_unit_sentry(struct unit *punit);
@@ -78,8 +82,9 @@
 void request_unit_airlift(struct unit *punit, struct city *pcity);
 void request_unit_return(struct unit *punit);
 void request_unit_upgrade(struct unit *punit);
-void request_unit_wait(struct unit *punit);
+void request_units_wait(struct unit_list *punits);
 void request_unit_wakeup(struct unit *punit);
+void request_unit_select_same_type(struct unit_list *punits);
 void request_diplomat_action(enum diplomat_actions action, int dipl_id,
                             int target_id, int value);
 void request_toggle_city_outlines(void);
@@ -103,17 +108,21 @@
 
 void wakeup_sentried_units(struct tile *ptile);
 
+bool unit_is_in_focus(const struct unit *punit);
+struct unit *get_focus_unit_on_tile(const struct tile *ptile);
 void auto_center_on_focus_unit(void);
 void advance_unit_focus(void);
-struct unit *get_unit_in_focus(void);
+struct unit_list *get_units_in_focus(void);
+int get_num_units_in_focus(void);
 void set_unit_focus(struct unit *punit);
+void add_unit_focus(struct unit *punit);
 void set_unit_focus_and_select(struct unit *punit);
 void update_unit_focus(void);
 struct unit *find_visible_unit(struct tile *ptile);
 void set_units_in_combat(struct unit *pattacker, struct unit *pdefender);
 double blink_active_unit(void);
 double blink_turn_done_button(void);
-void update_unit_pix_label(struct unit *punit);
+void update_unit_pix_label(struct unit_list *punitlist);
 
 void process_caravan_arrival(struct unit *punit);
 void process_diplomat_arrival(struct unit *pdiplomat, int victim_id);
@@ -170,6 +179,8 @@
 void key_unit_unload_all(void);
 void key_unit_wait(void);
 void key_unit_wakeup_others(void);
+void key_unit_assign_battlegroup(int battlegroup, bool append);
+void key_unit_select_battlegroup(int battlegroup, bool append);
 
 /* don't change this unless you also put more entries in data/Freeciv */
 #define MAX_NUM_UNITS_BELOW 4
Index: client/text.h
===================================================================
--- client/text.h       (revision 11168)
+++ client/text.h       (working copy)
@@ -35,8 +35,8 @@
 const char *get_global_warming_tooltip(void);
 const char *get_nuclear_winter_tooltip(void);
 const char *get_government_tooltip(void);
-const char *get_unit_info_label_text1(struct unit *punit);
-const char *get_unit_info_label_text2(struct unit *punit);
+const char *get_unit_info_label_text1(struct unit_list *punits);
+const char *get_unit_info_label_text2(struct unit_list *punits);
 const char *get_spaceship_descr(struct player_spaceship *pship);
 const char *get_timeout_label_text(void);
 const char *format_duration(int duration);
Index: client/include/mapview_g.h
===================================================================
--- client/include/mapview_g.h  (revision 11168)
+++ client/include/mapview_g.h  (working copy)
@@ -22,7 +22,7 @@
 #include "mapview_common.h"
 
 void update_info_label(void);
-void update_unit_info_label(struct unit *punit);
+void update_unit_info_label(struct unit_list *punitlist);
 void update_timeout_label(void);
 void update_turn_done_button(bool do_restore);
 void update_city_descriptions(void);
Index: client/goto.c
===================================================================
--- client/goto.c       (revision 11168)
+++ client/goto.c       (working copy)
@@ -49,31 +49,57 @@
   struct pf_map *map;
 };
 
+/* For each tile and each direction we store the number of lines going out 
+ * of the tile in this direction.  Since each line is undirected, we only 
+ * store the 4 lower-numbered directions for each tile; the 4 upper-numbered
+ * directions are stored as reverses from the target tile.
+ * Notes: 1. This assumes that 
+ * - there are 8 directions
+ * - out of every two opposite directions (like NORTH and SOUTH) one and 
+ *   only one has number less than 4
+ * 2. There _can_ be more than one line drawn between two tiles, because of 
+ * the waypoints. */
 static struct {
-  /* For each tile and each direction we store the number of lines going out 
-   * of the tile in this direction.  Since each line is undirected, we only 
-   * store the 4 lower-numbered directions for each tile; the 4 upper-numbered
-   * directions are stored as reverses from the target tile.
-   * Notes: 1. This assumes that 
-   * - there are 8 directions
-   * - out of every two opposite directions (like NORTH and SOUTH) one and 
-   *   only one has number less than 4
-   * 2. There _can_ be more than one line drawn between two tiles, because of 
-   * the waypoints. */
-  struct {
-    unsigned char drawn[4];
-  } *tiles;
+  unsigned char drawn[4];
+} *tiles;
+
+struct goto_map {
   int unit_id;                  /* The unit of the goto map */
   struct part *parts;
   int num_parts;
   struct pf_parameter template;
-} goto_map;
+};
 
-#define DRAWN(ptile, dir) (goto_map.tiles[(ptile)->index].drawn[dir])
+/* get 'struct goto_map_list' and related functions: */
+#define SPECLIST_TAG goto_map
+#define SPECLIST_TYPE struct goto_map
+#include "speclist.h"
+#define goto_map_list_iterate(gotolist, pgoto)                             \
+  TYPED_LIST_ITERATE(struct goto_map, gotolist, pgoto)
+#define goto_map_list_iterate_end                                          \
+  LIST_ITERATE_END
 
+/* Iterate over goto maps, filtering out dead units. */
+#define goto_map_iterate(gotolist, pgoto, punit)                           \
+  goto_map_list_iterate(gotolist, pgoto) {                                 \
+    struct unit *punit = find_unit_by_id(pgoto->unit_id);                  \
+                                                                           \
+    if (punit) {                                                           \
+      assert(unit_is_in_focus(punit));                                     \
+      {
+
+#define goto_map_iterate_end                                               \
+      }                                                                        
    \
+    }                                                                      \
+  } goto_map_list_iterate_end;
+
+static struct goto_map_list *goto_maps;
+
+#define DRAWN(ptile, dir) (tiles[(ptile)->index].drawn[dir])
+
 static void increment_drawn(struct tile *src_tile, enum direction8 dir);
 static void decrement_drawn(struct tile *src_tile, enum direction8 dir);
-static void reset_last_part(void);
+static void reset_last_part(struct goto_map *goto_map);
 
 
 /**************************************************************************
@@ -83,6 +109,31 @@
 static bool is_init = FALSE;
 static int connect_initial;
 
+/****************************************************************************
+  Create a new goto map.
+****************************************************************************/
+static struct goto_map *goto_map_new(void)
+{
+  struct goto_map *goto_map = fc_malloc(sizeof(*goto_map));
+
+  goto_map->unit_id = -1;
+  goto_map->parts = NULL;
+  goto_map->num_parts = 0;
+
+  return goto_map;
+}
+
+/****************************************************************************
+  Free an existing goto map.
+****************************************************************************/
+static void goto_map_free(struct goto_map *goto_map)
+{
+  if (goto_map->parts) {
+    free(goto_map->parts);
+  }
+  free(goto_map);
+}
+
 /********************************************************************** 
   Called once per game.
 ***********************************************************************/
@@ -92,12 +143,12 @@
     free_client_goto();
   }
 
-  goto_map.tiles = fc_malloc(MAP_INDEX_SIZE * sizeof(*goto_map.tiles));
-  goto_map.parts = NULL;
-  goto_map.num_parts = 0;
-  goto_map.unit_id = -1;
+  goto_maps = goto_map_list_new();
+
+  tiles = fc_malloc(MAP_INDEX_SIZE * sizeof(*tiles));
   whole_map_iterate(ptile) {
     int dir;
+
     for (dir = 0; dir < 4; dir++) {
       DRAWN(ptile, dir) = 0;
     }
@@ -112,12 +163,13 @@
 void free_client_goto(void)
 {
   if (is_init) {
-    free(goto_map.tiles);
-    if (goto_map.parts) {
-      free(goto_map.parts);
-    }
+    goto_map_list_iterate(goto_maps, goto_map) {
+      goto_map_free(goto_map);
+    } goto_map_list_iterate_end;
+    goto_map_list_unlink_all(goto_maps);
+    goto_map_list_free(goto_maps);
 
-    memset(&goto_map, 0, sizeof(goto_map));
+    free(tiles);
 
     is_init = FALSE;
   }
@@ -127,20 +179,20 @@
   Change the destination of the last part to the given position if a
   path can be found. If not the destination is set to the start.
 ***********************************************************************/
-static void update_last_part(struct tile *ptile)
+static void update_last_part(struct goto_map *goto_map, struct tile *ptile)
 {
-  struct part *p = &goto_map.parts[goto_map.num_parts - 1];
+  struct part *p = &goto_map->parts[goto_map->num_parts - 1];
   struct pf_path *new_path;
   struct tile *old_tile = p->start_tile;
   int i, start_index = 0;
 
   freelog(LOG_DEBUG, "update_last_part(%d,%d) old (%d,%d)-(%d,%d)",
-          TILE_XY(ptile), TILE_XY(p->start_tile), TILE_XY(p->end_tile));
+         TILE_XY(ptile), TILE_XY(p->start_tile), TILE_XY(p->end_tile));
   new_path = pf_get_path(p->map, ptile);
 
   if (!new_path) {
     freelog(PATH_LOG_LEVEL, "  no path found");
-    reset_last_part();
+    reset_last_part(goto_map);
     return;
   }
 
@@ -155,8 +207,8 @@
       struct pf_position *b = &new_path->positions[i];
 
       if (a->dir_to_next_pos != b->dir_to_next_pos
-          || !same_pos(a->tile, b->tile)) {
-        break;
+         || !same_pos(a->tile, b->tile)) {
+       break;
       }
     }
     start_index = i;
@@ -195,7 +247,7 @@
   p->end_fuel_left = pf_last_position(p->path)->fuel_left;
 
   if (hover_state == HOVER_CONNECT) {
-    int move_rate = goto_map.template.move_rate;
+    int move_rate = goto_map->template.move_rate;
     int moves = pf_last_position(p->path)->total_MC;
 
     p->time = moves / move_rate;
@@ -203,8 +255,7 @@
       p->time += connect_initial;
     }
     freelog(PATH_LOG_LEVEL, "To (%d,%d) MC: %d, connect_initial: %d",
-            TILE_XY(ptile), moves, connect_initial);
-
+           TILE_XY(ptile), moves, connect_initial);
   } else {
     p->time = pf_last_position(p->path)->turn;
   }
@@ -218,13 +269,13 @@
   Change the drawn path to a size of 0 steps by setting it to the
   start position.
 ***********************************************************************/
-static void reset_last_part(void)
+static void reset_last_part(struct goto_map *goto_map)
 {
-  struct part *p = &goto_map.parts[goto_map.num_parts - 1];
+  struct part *p = &goto_map->parts[goto_map->num_parts - 1];
 
   if (!same_pos(p->start_tile, p->end_tile)) {
     /* Otherwise no need to update */
-    update_last_part(p->start_tile);
+    update_last_part(goto_map, p->start_tile);
   }
 }
 
@@ -233,26 +284,29 @@
   of the new part is either the unit position (for the first part) or
   the destination of the last part (not the first part).
 ***********************************************************************/
-static void add_part(void)
+static void add_part(struct goto_map *goto_map)
 {
   struct part *p;
-  struct pf_parameter parameter = goto_map.template;
+  struct pf_parameter parameter = goto_map->template;
+  struct unit *punit = find_unit_by_id(goto_map->unit_id);
 
-  goto_map.num_parts++;
-  goto_map.parts =
-      fc_realloc(goto_map.parts,
-                 goto_map.num_parts * sizeof(*goto_map.parts));
-  p = &goto_map.parts[goto_map.num_parts - 1];
+  if (!punit) {
+    return;
+  }
 
-  if (goto_map.num_parts == 1) {
+  goto_map->num_parts++;
+  goto_map->parts =
+      fc_realloc(goto_map->parts,
+                 goto_map->num_parts * sizeof(*goto_map->parts));
+  p = &goto_map->parts[goto_map->num_parts - 1];
+
+  if (goto_map->num_parts == 1) {
     /* first part */
-    struct unit *punit = find_unit_by_id(goto_map.unit_id);
-
     p->start_tile = punit->tile;
     p->start_moves_left = parameter.moves_left_initially;
     p->start_fuel_left = parameter.fuel_left_initially;
   } else {
-    struct part *prev = &goto_map.parts[goto_map.num_parts - 2];
+    struct part *prev = &goto_map->parts[goto_map->num_parts - 2];
 
     p->start_tile = prev->end_tile;
     p->start_moves_left = prev->end_moves_left;
@@ -270,19 +324,19 @@
 /********************************************************************** 
   Remove the last part, erasing the corresponding path segment.
 ***********************************************************************/
-static void remove_last_part(void)
+static void remove_last_part(struct goto_map *goto_map)
 {
-  struct part *p = &goto_map.parts[goto_map.num_parts - 1];
+  struct part *p = &goto_map->parts[goto_map->num_parts - 1];
 
-  assert(goto_map.num_parts >= 1);
+  assert(goto_map->num_parts >= 1);
 
-  reset_last_part();
+  reset_last_part(goto_map);
   if (p->path) {
     /* We do not always have a path */
     pf_destroy_path(p->path);
   }
   pf_destroy_map(p->map);
-  goto_map.num_parts--;
+  goto_map->num_parts--;
 }
 
 /********************************************************************** 
@@ -290,16 +344,15 @@
 ***********************************************************************/
 void goto_add_waypoint(void)
 {
-  struct part *p = &goto_map.parts[goto_map.num_parts - 1];
-
   assert(is_active);
-  assert(find_unit_by_id(goto_map.unit_id)
-        && find_unit_by_id(goto_map.unit_id) == get_unit_in_focus());
+  goto_map_iterate(goto_maps, goto_map, punit) {
+    struct part *p = &goto_map->parts[goto_map->num_parts - 1];
 
-  if (!same_pos(p->start_tile, p->end_tile)) {
-    /* Otherwise the last part has zero length. */
-    add_part();
-  }
+    if (!same_pos(p->start_tile, p->end_tile)) {
+      /* Otherwise the last part has zero length. */
+      add_part(goto_map);
+    }
+  } goto_map_iterate_end;
 }
 
 /********************************************************************** 
@@ -308,27 +361,29 @@
 ***********************************************************************/
 bool goto_pop_waypoint(void)
 {
-  struct part *p = &goto_map.parts[goto_map.num_parts - 1];
-  struct tile *end_tile = p->end_tile;
+  bool popped = FALSE;
 
   assert(is_active);
-  assert(find_unit_by_id(goto_map.unit_id)
-        && find_unit_by_id(goto_map.unit_id) == get_unit_in_focus());
+  goto_map_iterate(goto_maps, goto_map, punit) {
+    struct part *p = &goto_map->parts[goto_map->num_parts - 1];
+    struct tile *end_tile = p->end_tile;
 
-  if (goto_map.num_parts == 1) {
-    /* we don't have any waypoint but the start pos. */
-    return FALSE;
-  }
+    if (goto_map->num_parts == 1) {
+      /* we don't have any waypoint but the start pos. */
+      continue;
+    }
+    popped = TRUE;
 
-  remove_last_part();
+    remove_last_part(goto_map);
 
-  /* 
-   * Set the end position of the previous part (now the last) to the
-   * end position of the last part (now gone). I.e. redraw a line to
-   * the mouse position. 
-   */
-  update_last_part(end_tile);
-  return TRUE;
+    /* 
+     * Set the end position of the previous part (now the last) to the
+     * end position of the last part (now gone). I.e. redraw a line to
+     * the mouse position. 
+     */
+    update_last_part(goto_map, end_tile);
+  } goto_map_iterate_end;
+  return popped;
 }
 
 /********************************************************************** 
@@ -710,16 +765,23 @@
   Enter the goto state: activate, prepare PF-template and add the 
   initial part.
 ***********************************************************************/
-void enter_goto_state(struct unit *punit)
+void enter_goto_state(struct unit_list *punits)
 {
   assert(!is_active);
+  assert(goto_map_list_size(goto_maps) == 0);
 
-  goto_map.unit_id = punit->id;
-  assert(goto_map.num_parts == 0);
+  unit_list_iterate(punits, punit) {
+    struct goto_map *goto_map = goto_map_new();
 
-  fill_client_goto_parameter(punit, &goto_map.template);
+    goto_map->unit_id = punit->id;
+    assert(goto_map->num_parts == 0);
 
-  add_part();
+    fill_client_goto_parameter(punit, &goto_map->template);
+
+    add_part(goto_map);
+
+    goto_map_list_append(goto_maps, goto_map);
+  } unit_list_iterate_end;
   is_active = TRUE;
 }
 
@@ -732,11 +794,13 @@
     return;
   }
 
-  while (goto_map.num_parts > 0) {
-    remove_last_part();
-  }
-  free(goto_map.parts);
-  goto_map.parts = NULL;
+  goto_map_list_iterate(goto_maps, goto_map) {
+    while (goto_map->num_parts > 0) {
+      remove_last_part(goto_map);
+    }
+    goto_map_free(goto_map);
+  } goto_map_list_iterate_end;
+  goto_map_list_unlink_all(goto_maps);
 
   is_active = FALSE;
 }
@@ -754,25 +818,43 @@
 ***********************************************************************/
 struct tile *get_line_dest(void)
 {
-  struct part *p = &goto_map.parts[goto_map.num_parts - 1];
+  if (is_active) {
+    struct goto_map *goto_map = goto_map_list_get(goto_maps, 0);
+    struct part *p = &goto_map->parts[goto_map->num_parts - 1];
 
-  assert(is_active);
-
-  return p->end_tile;
+    return p->end_tile;
+  } else {
+    return NULL;
+  }
 }
 
 /**************************************************************************
   Return the path length (in turns).
 ***************************************************************************/
-int get_goto_turns(void)
+void goto_get_turns(int *min, int *max)
 {
-  int time = 0, i;
+  if (min) {
+    *min = FC_INFINITY;
+  }
+  if (max) {
+    *max = -1;
+  }
+  if (is_active) {
+    goto_map_iterate(goto_maps, goto_map, punit) {
+      int time = 0, i;
 
-  for(i = 0; i < goto_map.num_parts; i++) {
-    time += goto_map.parts[i].time;
+      for (i = 0; i < goto_map->num_parts; i++) {
+       time += goto_map->parts[i].time;
+      }
+
+      if (min) {
+       *min = MIN(*min, time);
+      }
+      if (max) {
+       *max = MAX(*max, time);
+      }
+    } goto_map_iterate_end;
   }
-
-  return time;
 }
 
 /********************************************************************** 
@@ -784,10 +866,12 @@
 {
   assert(is_active);
 
-  update_last_part(dest_tile);
+  goto_map_iterate(goto_maps, goto_map, punit) {
+    update_last_part(goto_map, dest_tile);
+  } goto_map_iterate_end;
 
   /* Update goto data in info label. */
-  update_unit_info_label(get_unit_in_focus());
+  update_unit_info_label(get_units_in_focus());
 }
 
 /****************************************************************************
@@ -918,118 +1002,118 @@
   Send the current patrol route (i.e., the one generated via HOVER_STATE)
   to the server.
 **************************************************************************/
-void send_patrol_route(struct unit *punit)
+void send_patrol_route(void)
 {
-  int i;
-  struct pf_path *path = NULL, *return_path;
-  struct pf_parameter parameter = goto_map.template;
-  struct pf_map *map;
-
   assert(is_active);
-  assert(punit->id == goto_map.unit_id);
+  goto_map_iterate(goto_maps, goto_map, punit) {
+    int i;
+    struct pf_path *path = NULL, *return_path;
+    struct pf_parameter parameter = goto_map->template;
+    struct pf_map *map;
 
-  parameter.start_tile = goto_map.parts[goto_map.num_parts - 1].end_tile;
-  parameter.moves_left_initially
-    = goto_map.parts[goto_map.num_parts - 1].end_moves_left;
-  parameter.fuel_left_initially
-    = goto_map.parts[goto_map.num_parts - 1].end_fuel_left;
-  map = pf_create_map(&parameter);
-  return_path = pf_get_path(map, goto_map.parts[0].start_tile);
-  if (!return_path) {
-    die("No return path found!");
-  }
+    parameter.start_tile = goto_map->parts[goto_map->num_parts - 1].end_tile;
+    parameter.moves_left_initially
+      = goto_map->parts[goto_map->num_parts - 1].end_moves_left;
+    parameter.fuel_left_initially
+      = goto_map->parts[goto_map->num_parts - 1].end_fuel_left;
+    map = pf_create_map(&parameter);
+    return_path = pf_get_path(map, goto_map->parts[0].start_tile);
+    if (!return_path) {
+      die("No return path found!");
+    }
 
-  for (i = 0; i < goto_map.num_parts; i++) {
-    path = pft_concat(path, goto_map.parts[i].path);
-  }
-  path = pft_concat(path, return_path);
+    for (i = 0; i < goto_map->num_parts; i++) {
+      path = pft_concat(path, goto_map->parts[i].path);
+    }
+    path = pft_concat(path, return_path);
 
-  pf_destroy_map(map);
-  pf_destroy_path(return_path);
+    pf_destroy_map(map);
+    pf_destroy_path(return_path);
 
-  send_path_orders(punit, path, TRUE, TRUE, NULL);
+    send_path_orders(punit, path, TRUE, TRUE, NULL);
 
-  pf_destroy_path(path);
+    pf_destroy_path(path);
+  } goto_map_iterate_end;
 }
 
 /**************************************************************************
   Send the current connect route (i.e., the one generated via HOVER_STATE)
   to the server.
 **************************************************************************/
-void send_connect_route(struct unit *punit, enum unit_activity activity)
+void send_connect_route(enum unit_activity activity)
 {
-  struct pf_path *path = NULL;
-  int i;
-  struct packet_unit_orders p;
-  struct tile *old_tile;
-
   assert(is_active);
-  assert(punit->id == goto_map.unit_id);
+  goto_map_iterate(goto_maps, goto_map, punit) {
+    struct pf_path *path = NULL;
+    int i;
+    struct packet_unit_orders p;
+    struct tile *old_tile;
 
-  memset(&p, 0, sizeof(p));
+    memset(&p, 0, sizeof(p));
 
-  for (i = 0; i < goto_map.num_parts; i++) {
-    path = pft_concat(path, goto_map.parts[i].path);
-  }
+    for (i = 0; i < goto_map->num_parts; i++) {
+      path = pft_concat(path, goto_map->parts[i].path);
+    }
 
-  p.unit_id = punit->id;
-  p.src_x = punit->tile->x;
-  p.src_y = punit->tile->y;
-  p.repeat = FALSE;
-  p.vigilant = FALSE; /* Should be TRUE? */
+    p.unit_id = punit->id;
+    p.src_x = punit->tile->x;
+    p.src_y = punit->tile->y;
+    p.repeat = FALSE;
+    p.vigilant = FALSE; /* Should be TRUE? */
 
-  p.length = 0;
-  old_tile = path->positions[0].tile;
+    p.length = 0;
+    old_tile = path->positions[0].tile;
 
-  for (i = 0; i < path->length; i++) {
-    switch (activity) {
-    case ACTIVITY_IRRIGATE:
-      if (!tile_has_special(old_tile, S_IRRIGATION)) {
-       /* Assume the unit can irrigate or we wouldn't be here. */
-       p.orders[p.length] = ORDER_ACTIVITY;
-       p.activity[p.length] = ACTIVITY_IRRIGATE;
-       p.length++;
-      }
-      break;
-    case ACTIVITY_ROAD:
-    case ACTIVITY_RAILROAD:
-      if (!tile_has_special(old_tile, S_ROAD)) {
-       /* Assume the unit can build the road or we wouldn't be here. */
-       p.orders[p.length] = ORDER_ACTIVITY;
-       p.activity[p.length] = ACTIVITY_ROAD;
-       p.length++;
-      }
-      if (activity == ACTIVITY_RAILROAD) {
-       if (!tile_has_special(old_tile, S_RAILROAD)) {
-         /* Assume the unit can build the rail or we wouldn't be here. */
+    for (i = 0; i < path->length; i++) {
+      switch (activity) {
+      case ACTIVITY_IRRIGATE:
+       if (!tile_has_special(old_tile, S_IRRIGATION)) {
+         /* Assume the unit can irrigate or we wouldn't be here. */
          p.orders[p.length] = ORDER_ACTIVITY;
-         p.activity[p.length] = ACTIVITY_RAILROAD;
+         p.activity[p.length] = ACTIVITY_IRRIGATE;
          p.length++;
        }
+       break;
+      case ACTIVITY_ROAD:
+      case ACTIVITY_RAILROAD:
+       if (!tile_has_special(old_tile, S_ROAD)) {
+         /* Assume the unit can build the road or we wouldn't be here. */
+         p.orders[p.length] = ORDER_ACTIVITY;
+         p.activity[p.length] = ACTIVITY_ROAD;
+         p.length++;
+       }
+       if (activity == ACTIVITY_RAILROAD) {
+         if (!tile_has_special(old_tile, S_RAILROAD)) {
+           /* Assume the unit can build the rail or we wouldn't be here. */
+           p.orders[p.length] = ORDER_ACTIVITY;
+           p.activity[p.length] = ACTIVITY_RAILROAD;
+           p.length++;
+         }
+       }
+       break;
+      default:
+       die("Invalid connect activity.");
+       break;
       }
-      break;
-    default:
-      die("Invalid connect activity.");
-      break;
-    }
 
-    if (i != path->length - 1) {
-      struct tile *new_tile = path->positions[i + 1].tile;
+      if (i != path->length - 1) {
+       struct tile *new_tile = path->positions[i + 1].tile;
 
-      assert(!same_pos(new_tile, old_tile));
+       assert(!same_pos(new_tile, old_tile));
 
-      p.orders[p.length] = ORDER_MOVE;
-      p.dir[p.length] = get_direction_for_step(old_tile, new_tile);
-      p.length++;
+       p.orders[p.length] = ORDER_MOVE;
+       p.dir[p.length] = get_direction_for_step(old_tile, new_tile);
+       p.length++;
 
-      old_tile = new_tile;
+       old_tile = new_tile;
+      }
     }
-  }
 
-  p.dest_x = old_tile->x;
-  p.dest_y = old_tile->y;
+    p.dest_x = old_tile->x;
+    p.dest_y = old_tile->y;
 
-  send_packet_unit_orders(&aconnection, &p);
+    send_packet_unit_orders(&aconnection, &p);
+  } goto_map_iterate_end;
 }
 
 /**************************************************************************
@@ -1037,27 +1121,27 @@
   HOVER_STATE) to the server.  The route might involve more than one
   part if waypoints were used.  FIXME: danger paths are not supported.
 **************************************************************************/
-void send_goto_route(struct unit *punit)
+void send_goto_route(void)
 {
-  struct pf_path *path = NULL;
-  int i;
-
   assert(is_active);
-  assert(punit->id == goto_map.unit_id);
+  goto_map_iterate(goto_maps, goto_map, punit) {
+    struct pf_path *path = NULL;
+    int i;
 
-  for (i = 0; i < goto_map.num_parts; i++) {
-    path = pft_concat(path, goto_map.parts[i].path);
-  }
+    for (i = 0; i < goto_map->num_parts; i++) {
+      path = pft_concat(path, goto_map->parts[i].path);
+    }
 
-  if (goto_last_order == ORDER_LAST) {
-    send_goto_path(punit, path, NULL);
-  } else {
-    struct unit_order order;
+    if (goto_last_order == ORDER_LAST) {
+      send_goto_path(punit, path, NULL);
+    } else {
+      struct unit_order order;
 
-    order.order = goto_last_order;
-    send_goto_path(punit, path, &order);
-  }
-  pf_destroy_path(path);
+      order.order = goto_last_order;
+      send_goto_path(punit, path, &order);
+    }
+    pf_destroy_path(path);
+  } goto_map_iterate_end;
 }
 
 /* ================= drawn functions ============================ */
Index: client/packhand.c
===================================================================
--- client/packhand.c   (revision 11168)
+++ client/packhand.c   (working copy)
@@ -115,6 +115,7 @@
   } else {
     punit->transported_by = -1;
   }
+  punit->battlegroup = packet->battlegroup;
   punit->has_orders = packet->has_orders;
   punit->orders.length = packet->orders_length;
   punit->orders.index = packet->orders_index;
@@ -227,7 +228,7 @@
   client_remove_city(pcity);
 
   /* update menus if the focus unit is on the tile. */
-  if (get_unit_in_focus()) {
+  if (get_num_units_in_focus() > 0) {
     update_menus();
   }
 }
@@ -360,7 +361,7 @@
 
     update_info_label();       /* get initial population right */
     update_unit_focus();
-    update_unit_info_label(get_unit_in_focus());
+    update_unit_info_label(get_units_in_focus());
 
     /* Find something sensible to display instead of the intro gfx. */
     center_on_something();
@@ -392,7 +393,7 @@
   bool need_units_dialog_update = FALSE;
   struct city *pcity;
   bool popup, update_descriptions = FALSE, name_changed = FALSE;
-  struct unit *pfocus_unit = get_unit_in_focus();
+  struct unit_list *pfocus_units = get_units_in_focus();
 
   pcity=find_city_by_id(packet->id);
 
@@ -554,8 +555,13 @@
   }
 
   /* Update focus unit info label if necessary. */
-  if (name_changed && pfocus_unit && pfocus_unit->homecity == pcity->id) {
-    update_unit_info_label(pfocus_unit);
+  if (name_changed) {
+    unit_list_iterate(pfocus_units, pfocus_unit) {
+      if (pfocus_unit->homecity == pcity->id) {
+       update_unit_info_label(pfocus_units);
+       break;
+      }
+    } unit_list_iterate_end;
   }
 
   /* Update the units dialog if necessary. */
@@ -622,11 +628,8 @@
   }
 
   /* update menus if the focus unit is on the tile. */
-  {
-    struct unit *punit = get_unit_in_focus();
-    if (punit && same_pos(punit->tile, pcity->tile)) {
-      update_menus();
-    }
+  if (get_focus_unit_on_tile(pcity->tile)) {
+    update_menus();
   }
 
   if(is_new) {
@@ -775,7 +778,7 @@
   update_unit_focus();
   auto_center_on_focus_unit();
 
-  update_unit_info_label(get_unit_in_focus());
+  update_unit_info_label(get_units_in_focus());
   update_menus();
 
   set_seconds_to_turndone(game.info.timeout);
@@ -961,7 +964,6 @@
   bool check_focus = FALSE;     /* conservative focus change */
   bool moved = FALSE;
   bool ret = FALSE;
-  struct unit *focus_unit = get_unit_in_focus();
   
   punit = player_find_unit_by_id(packet_unit->owner, packet_unit->id);
   if (!punit && find_unit_by_id(packet_unit->id)) {
@@ -973,12 +975,13 @@
   if (punit) {
     ret = TRUE;
     punit->activity_count = packet_unit->activity_count;
+    unit_change_battlegroup(punit, packet_unit->battlegroup);
     if (punit->ai.control != packet_unit->ai.control) {
       punit->ai.control = packet_unit->ai.control;
       repaint_unit = TRUE;
       /* AI is set:     may change focus */
       /* AI is cleared: keep focus */
-      if (packet_unit->ai.control && punit == get_unit_in_focus()) {
+      if (packet_unit->ai.control && unit_is_in_focus(punit)) {
         check_focus = TRUE;
       }
     }
@@ -997,7 +1000,7 @@
       /* May change focus if focus unit gets a new activity.
        * But if new activity is Idle, it means user specifically selected
        * the unit */
-      if (punit == get_unit_in_focus()
+      if (unit_is_in_focus(punit)
          && (packet_unit->activity != ACTIVITY_IDLE
              || packet_unit->has_orders)) {
         check_focus = TRUE;
@@ -1013,9 +1016,8 @@
           && punit->activity == ACTIVITY_SENTRY
           && packet_unit->activity == ACTIVITY_IDLE
          && is_player_phase(game.player_ptr, game.info.phase)
-          && (!get_unit_in_focus()
               /* only 1 wakeup focus per tile is useful */
-              || !same_pos(packet_unit->tile, get_unit_in_focus()->tile))) {
+          && !get_focus_unit_on_tile(packet_unit->tile)) {
         set_unit_focus(punit);
         check_focus = FALSE; /* and keep it */
 
@@ -1031,7 +1033,7 @@
 
       punit->transported_by = packet_unit->transported_by;
       if (punit->occupy != packet_unit->occupy
-         && focus_unit && focus_unit->tile == packet_unit->tile) {
+         && get_focus_unit_on_tile(packet_unit->tile)) {
        /* Special case: (un)loading a unit in a transporter on the
         * same tile as the focus unit may (dis)allow the focus unit to be
         * loaded.  Thus the orders->(un)load menu item needs updating. */
@@ -1059,7 +1061,7 @@
 
     /* These two lines force the menus to be updated as appropriate when
      * the focus unit changes. */
-    if (punit == get_unit_in_focus()) {
+    if (unit_is_in_focus(punit)) {
       need_update_menus = TRUE;
     }
 
@@ -1094,7 +1096,7 @@
       if (pcity && (pcity->id != punit->homecity)) {
        refresh_city_dialog(pcity);
       }
-      if(punit == get_unit_in_focus()) {
+      if (unit_is_in_focus(punit)) {
         /* Update the orders menu -- the unit might have new abilities */
        need_update_menus = TRUE;
       }
@@ -1102,7 +1104,7 @@
 
     /* May change focus if an attempted move or attack exhausted unit */
     if (punit->moves_left != packet_unit->moves_left
-        && punit == get_unit_in_focus()) {
+        && unit_is_in_focus(punit)) {
       check_focus = TRUE;
     }
 
@@ -1206,6 +1208,8 @@
     unit_list_prepend(punit->owner->units, punit);
     unit_list_prepend(punit->tile->units, punit);
 
+    unit_register_battlegroup(punit);
+
     if((pcity=find_city_by_id(punit->homecity))) {
       unit_list_prepend(pcity->units_supported, punit);
     }
@@ -1226,20 +1230,17 @@
 
   assert(punit != NULL);
 
-  if (punit == get_unit_in_focus()) {
-    update_unit_info_label(punit);
-  } else if (get_unit_in_focus()
-            && (same_pos(get_unit_in_focus()->tile, punit->tile)
-                || (moved
-                    && same_pos(get_unit_in_focus()->tile, old_tile)))) {
-    update_unit_info_label(get_unit_in_focus());
+  if (unit_is_in_focus(punit)
+      || get_focus_unit_on_tile(punit->tile)
+      || (moved && get_focus_unit_on_tile(old_tile))) {
+    update_unit_info_label(get_units_in_focus());
   }
 
   if (repaint_unit) {
     refresh_unit_mapcanvas(punit, punit->tile, TRUE, FALSE);
   }
 
-  if ((check_focus || get_unit_in_focus() == NULL)
+  if ((check_focus || get_num_units_in_focus() == 0)
       && game.player_ptr
       && !game.player_ptr->ai.control
       && is_player_phase(game.player_ptr, game.info.phase)) {
@@ -1529,7 +1530,7 @@
       /* If we just learned bridge building and focus is on a settler
         on a river the road menu item will remain disabled unless we
         do this. (applys in other cases as well.) */
-      if (get_unit_in_focus()) {
+      if (get_num_units_in_focus() > 0) {
        update_menus();
       }
     }
@@ -2014,8 +2015,7 @@
 
   /* update menus if the focus unit is on the tile. */
   if (tile_changed) {
-    struct unit *punit = get_unit_in_focus();
-    if (punit && same_pos(punit->tile, ptile)) {
+    if (get_focus_unit_on_tile(ptile)) {
       update_menus();
     }
   }
Index: client/goto.h
===================================================================
--- client/goto.h       (revision 11168)
+++ client/goto.h       (working copy)
@@ -19,11 +19,11 @@
 
 void init_client_goto(void);
 void free_client_goto(void);
-void enter_goto_state(struct unit *punit);
+void enter_goto_state(struct unit_list *punits);
 void exit_goto_state(void);
 bool goto_is_active(void);
 struct tile *get_line_dest(void);
-int get_goto_turns(void);
+void goto_get_turns(int *min, int *max);
 void goto_add_waypoint(void);
 bool goto_pop_waypoint(void);
 
@@ -36,9 +36,9 @@
 void send_goto_path(struct unit *punit, struct pf_path *path,
                    struct unit_order *last_order);
 bool send_goto_tile(struct unit *punit, struct tile *ptile);
-void send_patrol_route(struct unit *punit);
-void send_goto_route(struct unit *punit);
-void send_connect_route(struct unit *punit, enum unit_activity activity);
+void send_patrol_route(void);
+void send_goto_route(void);
+void send_connect_route(enum unit_activity activity);
 
 struct pf_path *path_to_nearest_allied_city(struct unit *punit);
 
Index: client/tilespec.c
===================================================================
--- client/tilespec.c   (revision 11168)
+++ client/tilespec.c   (working copy)
@@ -190,6 +190,7 @@
       *transform,
       *connect,
       *patrol,
+      *battlegroup[MAX_NUM_BATTLEGROUPS],
       *lowfuel,
       *tired;
   } unit;
@@ -1861,7 +1862,7 @@
 ***********************************************************************/
 static void tileset_lookup_sprite_tags(struct tileset *t)
 {
-  char buffer[512];
+  char buffer[512], buffer2[512];
   const char dir_char[] = "nsew";
   const int W = t->normal_tile_width, H = t->normal_tile_height;
   int i, j;
@@ -2040,6 +2041,12 @@
   SET_SPRITE(unit.transform,    "unit.transform");
   SET_SPRITE(unit.connect,      "unit.connect");
   SET_SPRITE(unit.patrol,       "unit.patrol");
+  for (i = 0; i < MAX_NUM_BATTLEGROUPS; i++) {
+    my_snprintf(buffer, sizeof(buffer), "unit.battlegroup_%d", i);
+    my_snprintf(buffer2, sizeof(buffer2), "city.size_%d", i + 1);
+    assert(MAX_NUM_BATTLEGROUPS < NUM_TILES_DIGITS);
+    SET_SPRITE_ALT(unit.battlegroup[i], buffer, buffer2);
+  }
   SET_SPRITE(unit.lowfuel, "unit.lowfuel");
   SET_SPRITE(unit.tired, "unit.tired");
 
@@ -2086,8 +2093,6 @@
   SET_SPRITE(city.disorder, "city.disorder");
 
   for(i=0; i<NUM_TILES_DIGITS; i++) {
-    char buffer2[512];
-
     my_snprintf(buffer, sizeof(buffer), "city.size_%d", i);
     SET_SPRITE(city.size[i], buffer);
     my_snprintf(buffer2, sizeof(buffer2), "path.turns_%d", i);
@@ -2926,6 +2931,10 @@
     }
   }
 
+  if (punit->battlegroup != BATTLEGROUP_NONE) {
+    ADD_SPRITE_FULL(t->sprites.unit.battlegroup[punit->battlegroup]);
+  }
+
   if (t->sprites.unit.lowfuel
       && unit_type(punit)->fuel > 0
       && punit->fuel == 1
@@ -3660,7 +3669,7 @@
     bool known[NUM_EDGE_TILES], city[NUM_EDGE_TILES];
     bool unit[NUM_EDGE_TILES], worked[NUM_EDGE_TILES];
     int i;
-    struct unit *pfocus = get_unit_in_focus();
+    struct unit_list *pfocus_units = get_units_in_focus();
 
     for (i = 0; i < NUM_EDGE_TILES; i++) {
       const struct tile *tile = pedge->tile[i];
@@ -3668,9 +3677,18 @@
       int dummy_x, dummy_y;
 
       known[i] = tile && client_tile_get_known(tile) != TILE_UNKNOWN;
-      unit[i] = tile && pfocus && unit_flag(pfocus, F_CITIES)
-       && city_can_be_built_here(pfocus->tile, pfocus)
-       && base_map_to_city_map(&dummy_x, &dummy_y, pfocus->tile, tile);
+      unit[i] = FALSE;
+      if (tile) {
+       unit_list_iterate(pfocus_units, pfocus_unit) {
+         if (unit_flag(pfocus_unit, F_CITIES)
+             && city_can_be_built_here(pfocus_unit->tile, pfocus_unit)
+             && base_map_to_city_map(&dummy_x, &dummy_y,
+                                     pfocus_unit->tile, tile)) {
+           unit[i] = TRUE;
+           break;
+         }
+       } unit_list_iterate_end;
+      }
       worked[i] = FALSE;
 
       city[i] = (tile
@@ -3778,10 +3796,11 @@
     return 0;
   }
   if (ptile && ptile == get_line_dest()) {
-    int length = get_goto_turns();
+    int length;
     int units = length % NUM_TILES_DIGITS;
     int tens = (length / 10) % NUM_TILES_DIGITS;
 
+    goto_get_turns(NULL, &length);
     if (length >= 100) {
       static bool reported = FALSE;
 
@@ -3830,14 +3849,14 @@
   struct terrain *pterrain = NULL, *tterrain_near[8];
   bv_special tspecial, tspecial_near[8];
   int tileno, dir;
-  struct unit *pfocus = get_unit_in_focus();
   struct drawn_sprite *save_sprs = sprs;
   struct player *owner = NULL;
 
   /* Unit drawing is disabled if the view options is turned off, but only
    * if we're drawing on the mapview. */
   bool do_draw_unit = (punit && (draw_units || !ptile
-                                || (draw_focus_unit && pfocus == punit)));
+                                || (draw_focus_unit
+                                    && unit_is_in_focus(punit))));
   bool solid_bg = (solid_color_behind_units
                   && (do_draw_unit
                       || (pcity && draw_cities)
@@ -4030,12 +4049,11 @@
 
   case LAYER_UNIT:
   case LAYER_FOCUS_UNIT:
-    if (do_draw_unit && XOR(layer == LAYER_UNIT,
-                           punit == get_unit_in_focus())) {
+    if (do_draw_unit && XOR(layer == LAYER_UNIT, unit_is_in_focus(punit))) {
       bool stacked = ptile && (unit_list_size(ptile->units) > 1);
       bool backdrop = !pcity;
 
-      if (ptile && punit == get_unit_in_focus()
+      if (ptile && unit_is_in_focus(punit)
          && t->sprites.unit.select[0]) {
        /* Special case for drawing the selection rectangle.  The blinking
         * unit is handled separately, inside get_drawable_unit(). */
@@ -4279,7 +4297,6 @@
                               const struct city *citymode)
 {
   struct unit *punit = find_visible_unit(ptile);
-  struct unit *pfocus = get_unit_in_focus();
 
   if (!punit)
     return NULL;
@@ -4287,9 +4304,8 @@
   if (citymode && punit->owner == citymode->owner)
     return NULL;
 
-  if (punit != pfocus
-      || t->sprites.unit.select[0] || focus_unit_state == 0
-      || !same_pos(punit->tile, pfocus->tile))
+  if (!unit_is_in_focus(punit)
+      || t->sprites.unit.select[0] || focus_unit_state == 0)
     return punit;
   else
     return NULL;
Index: client/mapctrl_common.c
===================================================================
--- client/mapctrl_common.c     (revision 11168)
+++ client/mapctrl_common.c     (working copy)
@@ -49,6 +49,7 @@
 
 bool rbutton_down = FALSE;
 bool rectangle_active = FALSE;
+static bool rectangle_append;
 
 /* This changes the behaviour of left mouse
    button in Area Selection mode. */
@@ -79,7 +80,8 @@
  anchor is not the drawing start point, but is used to calculate
  width, height. Also record the current mapview centering.
 **************************************************************************/
-void anchor_selection_rectangle(int canvas_x, int canvas_y)
+void anchor_selection_rectangle(int canvas_x, int canvas_y,
+                               bool append)
 {
   struct tile *ptile = canvas_pos_to_nearest_tile(canvas_x, canvas_y);
 
@@ -89,6 +91,7 @@
   /* FIXME: This may be off-by-one. */
   rec_canvas_center_tile = get_center_tile_mapcanvas();
   rec_w = rec_h = 0;
+  rectangle_append = append;
 }
 
 /**************************************************************************
@@ -112,8 +115,8 @@
   /* Iteration direction */
   const int inc_x = (rec_w > 0 ? half_W : -half_W);
   const int inc_y = (rec_h > 0 ? half_H : -half_H);
-
   int x, y, x2, y2, xx, yy;
+  int units = 0;
 
   y = rec_corner_y;
   for (yy = 0; yy <= segments_y; yy++, y += inc_y) {
@@ -144,9 +147,20 @@
       /*  Tile passed all tests; process it.
        */
       if (ptile->city && ptile->city->owner == game.player_ptr) {
+       /* FIXME: handle rectangle_append */
         map_deco[ptile->index].hilite = HILITE_CITY;
         tiles_hilited_cities = TRUE;
       }
+      unit_list_iterate(ptile->units, punit) {
+       if (punit->owner == game.player_ptr) {
+         if (units == 0 && !rectangle_append) {
+           set_unit_focus(punit);
+         } else {
+           add_unit_focus(punit);
+         }
+         units++;
+       }
+      } unit_list_iterate_end;
     }
   }
 
@@ -227,6 +241,7 @@
   }
 
   /* It is currently drawn only to the screen, not backing store */
+  rectangle_active = TRUE;
   draw_selection_rectangle(canvas_x, canvas_y, rec_w, rec_h);
   rec_corner_x = canvas_x;
   rec_corner_y = canvas_y;
@@ -427,12 +442,9 @@
   struct tile *ptile = canvas_pos_to_tile(canvas_x, canvas_y);
 
   if (keyboardless_goto_active && hover_state == HOVER_GOTO && ptile) {
-    struct unit *punit =
-        player_find_unit_by_id(game.player_ptr, hover_unit);
-
     do_unit_goto(ptile);
     set_hover_state(NULL, HOVER_NONE, ACTIVITY_LAST, ORDER_LAST);
-    update_unit_info_label(punit);
+    update_unit_info_label(hover_units);
   }
   keyboardless_goto_active = FALSE;
   keyboardless_goto_button_down = FALSE;
@@ -447,7 +459,7 @@
 {
   struct tile *ptile = canvas_pos_to_tile(canvas_x, canvas_y);
 
-  if (ptile && get_unit_in_focus()
+  if (ptile && get_num_units_in_focus() > 0
       && !same_pos(keyboardless_goto_start_tile, ptile)
       && can_client_issue_orders()) {
     keyboardless_goto_active = TRUE;
@@ -658,7 +670,7 @@
 {
   struct unit *my_unit, *defender, *attacker;
 
-  if (!(my_unit = get_unit_in_focus())
+  if (!unit_is_in_focus(my_unit)
       || !(defender = get_defender(my_unit, ptile))
       || !(attacker = get_attacker(my_unit, ptile))) {
     return FALSE;
Index: client/mapctrl_common.h
===================================================================
--- client/mapctrl_common.h     (revision 11168)
+++ client/mapctrl_common.h     (working copy)
@@ -27,7 +27,7 @@
 extern bool keyboardless_goto_active;
 extern struct tile *keyboardless_goto_start_tile;
 
-void anchor_selection_rectangle(int canvas_x, int canvas_y);
+void anchor_selection_rectangle(int canvas_x, int canvas_y, bool append);
 void update_selection_rectangle(int canvas_x, int canvas_y);
 
 bool is_city_hilited(struct city *pcity);
Index: client/mapview_common.c
===================================================================
--- client/mapview_common.c     (revision 11168)
+++ client/mapview_common.c     (working copy)
@@ -1733,9 +1733,9 @@
     return;
   }
 
-  if (punit == get_unit_in_focus() && hover_state != HOVER_NONE) {
+  if (unit_is_in_focus(punit) && hover_state != HOVER_NONE) {
     set_hover_state(NULL, HOVER_NONE, ACTIVITY_LAST, ORDER_LAST);
-    update_unit_info_label(punit);
+    update_unit_info_label(get_units_in_focus());
   }
 
   dest_x = src_tile->x + dx;
Index: client/civclient.c
===================================================================
--- client/civclient.c  (revision 11168)
+++ client/civclient.c  (working copy)
@@ -680,7 +680,7 @@
     time_until_next_call = MIN(time_until_next_call, blink_time);
   }
 
-  if (get_unit_in_focus()) {
+  if (get_num_units_in_focus() > 0) {
     double blink_time = blink_active_unit();
 
     time_until_next_call = MIN(time_until_next_call, blink_time);
Index: client/climisc.c
===================================================================
--- client/climisc.c    (revision 11168)
+++ client/climisc.c    (working copy)
@@ -74,28 +74,23 @@
   struct city *pcity;
   struct tile *ptile = punit->tile;
   int hc = punit->homecity;
-  struct unit *ufocus = get_unit_in_focus();
   struct unit old_unit = *punit;
+  int old = get_num_units_in_focus();
+  bool update;
 
   freelog(LOG_DEBUG, "removing unit %d, %s %s (%d %d) hcity %d",
          punit->id, get_nation_name(unit_owner(punit)->nation),
          unit_name(punit->type), TILE_XY(punit->tile), hc);
 
-  if (punit == ufocus) {
-    set_unit_focus(NULL);
-    game_remove_unit(punit);
-    punit = ufocus = NULL;
+  update = (get_focus_unit_on_tile(punit->tile) != NULL);
+  control_unit_killed(punit);
+  game_remove_unit(punit);
+  punit = NULL;
+  if (old > 0 && get_num_units_in_focus() == 0) {
     advance_unit_focus();
-  } else {
-    /* calculate before punit disappears, use after punit removed: */
-    bool update = (ufocus
-                  && same_pos(ufocus->tile, punit->tile));
-
-    game_remove_unit(punit);
-    punit = NULL;
-    if (update) {
-      update_unit_pix_label(ufocus);
-    }
+  } else if (update) {
+    update_unit_pix_label(get_units_in_focus());
+    update_unit_info_label(get_units_in_focus());
   }
 
   pcity = tile_get_city(ptile);
@@ -409,8 +404,8 @@
   }
 
   can_slide = FALSE;
-  if ((punit = get_unit_in_focus())) {
-    center_tile_mapcanvas(punit->tile);
+  if (get_num_units_in_focus() > 0) {
+    center_tile_mapcanvas(unit_list_get(get_units_in_focus(), 0)->tile);
   } else if (game.player_ptr && (pcity = find_palace(game.player_ptr))) {
     /* Else focus on the capital. */
     center_tile_mapcanvas(pcity->tile);
@@ -1081,3 +1076,18 @@
   }
   dsend_packet_player_rates(&aconnection, tax, lux, sci);
 }
+
+/****************************************************************************
+  Returns TRUE if any of the units can do the connect activity.
+****************************************************************************/
+bool can_units_do_connect(struct unit_list *punits,
+                         enum unit_activity activity)
+{
+  unit_list_iterate(punits, punit) {
+    if (can_unit_do_connect(punit, activity)) {
+      return TRUE;
+    }
+  } unit_list_iterate_end;
+
+  return FALSE;
+}
Index: client/climisc.h
===================================================================
--- client/climisc.h    (revision 11168)
+++ client/climisc.h    (working copy)
@@ -113,4 +113,7 @@
 void cityrep_buy(struct city *pcity);
 void common_taxrates_callback(int i);
 
+bool can_units_do_connect(struct unit_list *punits,
+                         enum unit_activity activity);
+
 #endif  /* FC__CLIMISC_H */

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