Complete.Org: Mailing Lists: Archives: freeciv-dev: September 2005:
[Freeciv-Dev] Re: (PR#13878) redesign observers
Home

[Freeciv-Dev] Re: (PR#13878) redesign observers

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
Subject: [Freeciv-Dev] Re: (PR#13878) redesign observers
From: "Jason Short" <jdorje@xxxxxxxxxxxxxxxxxxxxx>
Date: Wed, 28 Sep 2005 11:38:41 -0700
Reply-to: bugs@xxxxxxxxxxx

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

This patch fixes one additional bug.  When doing /observe <player> while 
already in global observer mode, detach_command wasn't called so there 
was no reset done.

-jason


Index: client/attribute.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/attribute.c,v
retrieving revision 1.22
diff -p -u -r1.22 attribute.c
--- client/attribute.c  23 Sep 2005 19:23:31 -0000      1.22
+++ client/attribute.c  28 Sep 2005 18:36:05 -0000
@@ -257,6 +257,10 @@ void attribute_flush(void)
 {
   struct player *pplayer = game.player_ptr;
 
+  if (!pplayer) {
+    return;
+  }
+
   assert(attribute_hash != NULL);
 
   if (hash_num_entries(attribute_hash) == 0)
@@ -280,6 +284,10 @@ void attribute_restore(void)
 {
   struct player *pplayer = game.player_ptr;
 
+  if (!pplayer) {
+    return;
+  }
+
   assert(attribute_hash != NULL);
 
   if (!unserialize_hash(attribute_hash, pplayer->attribute_block.data,
Index: client/citydlg_common.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/citydlg_common.c,v
retrieving revision 1.85
diff -p -u -r1.85 citydlg_common.c
--- client/citydlg_common.c     1 Aug 2005 23:09:35 -0000       1.85
+++ client/citydlg_common.c     28 Sep 2005 18:36:06 -0000
@@ -318,7 +318,7 @@ void get_city_dialog_production_row(char
     }
     my_snprintf(buf[2], column_size, "%d", unit_build_shield_cost(ptype));
   } else {
-    struct player *pplayer = game.player_ptr;
+    struct player *pplayer = pcity->owner;
 
     /* Total & turns left meaningless on capitalization */
     if (building_has_effect(target.value, EFT_PROD_TO_GOLD)) {
Index: client/civclient.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/civclient.c,v
retrieving revision 1.234
diff -p -u -r1.234 civclient.c
--- client/civclient.c  5 Sep 2005 15:55:45 -0000       1.234
+++ client/civclient.c  28 Sep 2005 18:36:06 -0000
@@ -724,8 +724,9 @@ bool can_client_issue_orders(void)
 **************************************************************************/
 bool can_meet_with_player(const struct player *pplayer)
 {
-  return (could_meet_with_player(game.player_ptr, pplayer)
-          && can_client_issue_orders());
+  return (can_client_issue_orders()
+         && game.player_ptr
+         && could_meet_with_player(game.player_ptr, pplayer));
 }
 
 /**************************************************************************
@@ -734,7 +735,9 @@ bool can_meet_with_player(const struct p
 **************************************************************************/
 bool can_intel_with_player(const struct player *pplayer)
 {
-  return could_intel_with_player(game.player_ptr, pplayer);
+  return (client_is_observer()
+         || (game.player_ptr
+             && could_intel_with_player(game.player_ptr, pplayer)));
 }
 
 /**************************************************************************
@@ -744,7 +747,7 @@ bool can_intel_with_player(const struct 
 **************************************************************************/
 bool can_client_change_view(void)
 {
-  return (game.player_ptr
+  return ((game.player_ptr || client_is_observer())
          && (get_client_state() == CLIENT_GAME_RUNNING_STATE
              || get_client_state() == CLIENT_GAME_OVER_STATE));
 }
Index: client/climap.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/climap.c,v
retrieving revision 1.10
diff -p -u -r1.10 climap.c
--- client/climap.c     5 May 2005 18:32:46 -0000       1.10
+++ client/climap.c     28 Sep 2005 18:36:06 -0000
@@ -19,9 +19,9 @@
 #include "map.h"
 #include "shared.h"
 
-#include "tilespec.h"           /* tileset_is_isometric(tileset) */
-
+#include "civclient.h"
 #include "climap.h"
+#include "tilespec.h"           /* tileset_is_isometric(tileset) */
 
 /************************************************************************
  A tile's "known" field is used by the server to store whether _each_
@@ -35,6 +35,9 @@
 *************************************************************************/
 enum known_type client_tile_get_known(const struct tile *ptile)
 {
+  if (!game.player_ptr && client_is_observer()) {
+    return TILE_KNOWN;
+  }
   return tile_get_known(ptile, game.player_ptr);
 }
 
Index: client/climisc.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/climisc.c,v
retrieving revision 1.181
diff -p -u -r1.181 climisc.c
--- client/climisc.c    13 Sep 2005 08:13:10 -0000      1.181
+++ client/climisc.c    28 Sep 2005 18:36:06 -0000
@@ -111,12 +111,15 @@ void client_remove_unit(struct unit *pun
            TILE_XY(pcity->tile));
   }
 
-  pcity = player_find_city_by_id(game.player_ptr, hc);
-  if (pcity) {
-    refresh_city_dialog(pcity);
-    freelog(LOG_DEBUG, "home city %s, %s, (%d %d)", pcity->name,
-           get_nation_name(city_owner(pcity)->nation),
-           TILE_XY(pcity->tile));
+  /* FIXME: this can cause two refreshes to be done? */
+  if (game.player_ptr) {
+    pcity = player_find_city_by_id(game.player_ptr, hc);
+    if (pcity) {
+      refresh_city_dialog(pcity);
+      freelog(LOG_DEBUG, "home city %s, %s, (%d %d)", pcity->name,
+             get_nation_name(city_owner(pcity)->nation),
+             TILE_XY(pcity->tile));
+    }
   }
 
   refresh_unit_mapcanvas(&old_unit, ptile, TRUE, FALSE);
@@ -162,6 +165,10 @@ void client_change_all(struct city_produ
 {
   int last_request_id = 0;
 
+  if (!can_client_issue_orders()) {
+    return;
+  }
+
   create_event(NULL, E_CITY_PRODUCTION_CHANGED,
               _("Changing production of every %s into %s."),
               from.is_unit ? get_unit_type(from.value)->name
@@ -191,7 +198,8 @@ void client_change_all(struct city_produ
 const char *get_embassy_status(const struct player *me,
                               const struct player *them)
 {
-  if (me == them
+  if (!me || !them
+      || me == them
       || !them->is_alive
       || !me->is_alive) {
     return "-";
@@ -218,13 +226,13 @@ const char *get_embassy_status(const str
 const char *get_vision_status(const struct player *me,
                              const struct player *them)
 {
-  if (gives_shared_vision(me, them)) {
+  if (me && them && gives_shared_vision(me, them)) {
     if (gives_shared_vision(them, me)) {
       return Q_("?vision:Both");
     } else {
       return Q_("?vision:To Them");
     }
-  } else if (gives_shared_vision(them, me)) {
+  } else if (me && them && gives_shared_vision(them, me)) {
     return Q_("?vision:To Us");
   } else {
     return "";
@@ -373,7 +381,8 @@ struct sprite *client_cooling_sprite(voi
 **************************************************************************/
 struct sprite *client_government_sprite(void)
 {
-  if (can_client_change_view() && game.control.government_count > 0) {
+  if (can_client_change_view() && game.player_ptr
+      && game.control.government_count > 0) {
     struct government *gov = game.player_ptr->government;
 
     return get_government_sprite(tileset, gov);
@@ -402,15 +411,15 @@ void center_on_something(void)
   can_slide = FALSE;
   if ((punit = get_unit_in_focus())) {
     center_tile_mapcanvas(punit->tile);
-  } else if ((pcity = find_palace(game.player_ptr))) {
+  } else if (game.player_ptr && (pcity = find_palace(game.player_ptr))) {
     /* Else focus on the capital. */
     center_tile_mapcanvas(pcity->tile);
-  } else if (city_list_size(game.player_ptr->cities) > 0) {
+  } else if (game.player_ptr && city_list_size(game.player_ptr->cities) > 0) {
     /* Just focus on any city. */
     pcity = city_list_get(game.player_ptr->cities, 0);
     assert(pcity != NULL);
     center_tile_mapcanvas(pcity->tile);
-  } else if (unit_list_size(game.player_ptr->units) > 0) {
+  } else if (game.player_ptr && unit_list_size(game.player_ptr->units) > 0) {
     /* Just focus on any unit. */
     punit = unit_list_get(game.player_ptr->units, 0);
     assert(punit != NULL);
@@ -633,7 +642,9 @@ void name_and_sort_items(struct city_pro
 }
 
 /**************************************************************************
-...
+  Return possible production targets for the current player's cities.
+
+  FIXME: this should probably take a pplayer argument.
 **************************************************************************/
 int collect_production_targets(struct city_production *targets,
                               struct city **selected_cities,
@@ -648,6 +659,10 @@ int collect_production_targets(struct ci
   cid cid;
   int items_used = 0;
 
+  if (!game.player_ptr) {
+    return 0;
+  }
+
   for (cid = first; cid < last; cid++) {
     bool append = FALSE;
     struct city_production target = cid_decode(cid);
@@ -681,6 +696,8 @@ int collect_production_targets(struct ci
 /**************************************************************************
  Collect the cids of all targets (improvements and units) which are
  currently built in a city.
+
+  FIXME: this should probably take a pplayer argument.
 **************************************************************************/
 int collect_currently_building_targets(struct city_production *targets)
 {
@@ -688,6 +705,10 @@ int collect_currently_building_targets(s
   int cids_used = 0;
   cid cid;
 
+  if (!game.player_ptr) {
+    return 0;
+  }
+
   memset(mapping, 0, sizeof(mapping));
   city_list_iterate(game.player_ptr->cities, pcity) {
     mapping[cid_encode_from_city(pcity)] = TRUE;
@@ -706,11 +727,17 @@ int collect_currently_building_targets(s
 /**************************************************************************
  Collect the cids of all targets (improvements and units) which can
  be build in a city.
+
+  FIXME: this should probably take a pplayer argument.
 **************************************************************************/
 int collect_buildable_targets(struct city_production *targets)
 {
   int cids_used = 0;
 
+  if (!game.player_ptr) {
+    return 0;
+  }
+
   impr_type_iterate(id) {
     if (can_player_build_improvement(game.player_ptr, id)) {
       targets[cids_used].is_unit = FALSE;
@@ -733,6 +760,8 @@ int collect_buildable_targets(struct cit
 /**************************************************************************
  Collect the cids of all targets which can be build by this city or
  in general.
+
+  FIXME: this should probably take a pplayer argument.
 **************************************************************************/
 int collect_eventually_buildable_targets(struct city_production *targets,
                                         struct city *pcity,
@@ -740,6 +769,10 @@ int collect_eventually_buildable_targets
 {
   int cids_used = 0;
 
+  if (!game.player_ptr) {
+    return 0;
+  }
+
   impr_type_iterate(id) {
     bool can_build = can_player_build_improvement(game.player_ptr, id);
     bool can_eventually_build =
@@ -809,7 +842,8 @@ int num_supported_units_in_city(struct c
 {
   struct unit_list *plist;
 
-  if (pcity->owner != game.player_ptr) {
+  if (can_player_see_city_internals(game.player_ptr, pcity)) {
+    /* Other players don't see inside the city (but observers do). */
     plist = pcity->info_units_supported;
   } else {
     plist = pcity->units_supported;
@@ -825,7 +859,8 @@ int num_present_units_in_city(struct cit
 {
   struct unit_list *plist;
 
-  if (pcity->owner != game.player_ptr) {
+  if (can_player_see_units_in_city(game.player_ptr, pcity)) {
+    /* Other players don't see inside the city (but observers do). */
     plist = pcity->info_units_present;
   } else {
     plist = pcity->tile->units;
@@ -855,8 +890,8 @@ void handle_event(char *message, struct 
   if (BOOL_VAL(where & MW_MESSAGES)) {
     add_notify_window(message, ptile, event);
   }
-  if (BOOL_VAL(where & MW_POPUP) &&
-      (!game.player_ptr->ai.control)) {
+  if (BOOL_VAL(where & MW_POPUP)
+      && (!game.player_ptr || !game.player_ptr->ai.control)) {
     popup_notify_goto_dialog(_("Popup Request"), message, ptile);
   }
 
@@ -999,7 +1034,7 @@ void cityrep_buy(struct city *pcity)
     return;
   }
 
-  if (game.player_ptr->economic.gold >= value) {
+  if (pcity->owner->economic.gold >= value) {
     city_buy_production(pcity);
   } else {
     const char *name;
@@ -1012,7 +1047,7 @@ void cityrep_buy(struct city *pcity)
 
     create_event(NULL, E_BAD_COMMAND,
                 _("%s costs %d gold and you only have %d gold."),
-                name, value, game.player_ptr->economic.gold);
+                name, value, pcity->owner->economic.gold);
   }
 }
 
Index: client/control.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/control.c,v
retrieving revision 1.187
diff -p -u -r1.187 control.c
--- client/control.c    13 Sep 2005 08:13:10 -0000      1.187
+++ client/control.c    28 Sep 2005 18:36:06 -0000
@@ -145,10 +145,8 @@ void set_unit_focus(struct unit *punit)
 {
   struct unit *punit_old_focus = punit_focus;
 
-  if (punit && punit->owner != game.player_ptr) {
+  if (punit && game.player_ptr && punit->owner != game.player_ptr) {
     /* Callers should make sure this never happens. */
-    freelog(LOG_ERROR, "Trying to focus on another player's unit!");
-    assert(0);
     return;
   }
 
@@ -227,7 +225,7 @@ at the end of the goto, then they are st
 **************************************************************************/
 void update_unit_focus(void)
 {
-  if (!can_client_change_view()) {
+  if (!game.player_ptr || !can_client_change_view()) {
     return;
   }
   if (!punit_focus
@@ -261,7 +259,10 @@ void advance_unit_focus(void)
   struct unit *punit_old_focus = punit_focus;
   struct unit *candidate = find_best_focus_candidate(FALSE);
 
-  assert(can_client_change_view());
+  if (!game.player_ptr || !can_client_change_view()) {
+    set_unit_focus(NULL);
+    return;
+  }
 
   set_hover_state(NULL, HOVER_NONE, ACTIVITY_LAST, ORDER_LAST);
   if (!can_client_change_view()) {
@@ -315,7 +316,8 @@ static struct unit *find_best_focus_cand
   int best_dist = 99999;
   struct tile *ptile;
 
-  if (!is_player_phase(game.player_ptr, game.info.phase)) {
+  if (!game.player_ptr
+      || !is_player_phase(game.player_ptr, game.info.phase)) {
     /* No focus unit wanted. */
     return NULL;
   }
@@ -457,7 +459,8 @@ double blink_turn_done_button(void)
   static struct timer *blink_timer = NULL;
   const double blink_time = 0.5; /* half-second blink interval */
 
-  if (game.player_ptr && game.player_ptr->is_alive
+  if (game.player_ptr
+      && game.player_ptr->is_alive
       && !game.player_ptr->phase_done) {
     if (!blink_timer || read_timer_seconds(blink_timer) > blink_time) {
       int is_waiting = 0, is_moving = 0;
@@ -588,13 +591,16 @@ void process_caravan_arrival(struct unit
     id = *p_id;
     free(p_id);
     p_id = NULL;
-    punit = player_find_unit_by_id(game.player_ptr, id);
+    punit = find_unit_by_id(id);
 
     if (punit && (unit_can_help_build_wonder_here(punit)
                  || unit_can_est_traderoute_here(punit))
-       && (!game.player_ptr->ai.control)) {
+       && (!game.player_ptr
+           || (game.player_ptr == punit->owner
+               && !game.player_ptr->ai.control))) {
       struct city *pcity_dest = tile_get_city(punit->tile);
       struct city *pcity_homecity = find_city_by_id(punit->homecity);
+
       if (pcity_dest && pcity_homecity) {
        popup_caravan_dialog(punit, pcity_homecity, pcity_dest);
        return;
@@ -794,7 +800,7 @@ void request_unit_unload_all(struct unit
        request_new_unit_activity(pcargo, ACTIVITY_IDLE);
       }
 
-      if (pcargo->owner == game.player_ptr) {
+      if (pcargo->owner == punit->owner) {
        plast = pcargo;
       }
     }
@@ -867,6 +873,9 @@ void request_diplomat_action(enum diplom
 
 void wakeup_sentried_units(struct tile *ptile)
 {
+  if (!can_client_issue_orders()) {
+    return;
+  }
   unit_list_iterate(ptile->units, punit) {
     if (punit->activity == ACTIVITY_SENTRY
        && game.player_ptr == punit->owner) {
Index: client/goto.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/goto.c,v
retrieving revision 1.91
diff -p -u -r1.91 goto.c
--- client/goto.c       20 Aug 2005 19:53:40 -0000      1.91
+++ client/goto.c       28 Sep 2005 18:36:07 -0000
@@ -1152,7 +1152,7 @@ struct pf_path *path_to_nearest_allied_c
   struct pf_map *map;
   struct pf_path *path = NULL;
 
-  if ((pcity = is_allied_city_tile(punit->tile, game.player_ptr))) {
+  if ((pcity = is_allied_city_tile(punit->tile, punit->owner))) {
     /* We're already on a city - don't go anywhere. */
     return NULL;
   }
@@ -1165,7 +1165,7 @@ struct pf_path *path_to_nearest_allied_c
 
     pf_next_get_position(map, &pos);
 
-    if ((pcity = is_allied_city_tile(pos.tile, game.player_ptr))) {
+    if ((pcity = is_allied_city_tile(pos.tile, punit->owner))) {
       break;
     }
   }
Index: client/mapctrl_common.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/mapctrl_common.c,v
retrieving revision 1.62
diff -p -u -r1.62 mapctrl_common.c
--- client/mapctrl_common.c     13 Sep 2005 08:13:11 -0000      1.62
+++ client/mapctrl_common.c     28 Sep 2005 18:36:07 -0000
@@ -324,6 +324,10 @@ void clipboard_copy_production(struct ti
 {
   struct city *pcity = ptile->city;
 
+  if (!can_client_issue_orders()) {
+    return;
+  }
+
   if (pcity) {
     if (pcity->owner != game.player_ptr)  {
       return;
@@ -402,6 +406,9 @@ static void clipboard_send_production_pa
 **************************************************************************/
 void upgrade_canvas_clipboard(void)
 {
+  if (!can_client_issue_orders()) {
+    return;
+  }
   if (clipboard.is_unit)  {
     struct unit_type *u
       = can_upgrade_unittype(game.player_ptr, get_unit_type(clipboard.value));
@@ -589,7 +596,9 @@ void update_turn_done_button_state()
 
   if (turn_done_state) {
     if (waiting_for_end_turn
-       || (game.player_ptr->ai.control && !ai_manual_turn_done)) {
+       || (game.player_ptr
+           && game.player_ptr->ai.control
+           && !ai_manual_turn_done)) {
       send_turn_done();
     } else {
       update_turn_done_button(TRUE);
Index: client/mapview_common.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/mapview_common.c,v
retrieving revision 1.246
diff -p -u -r1.246 mapview_common.c
--- client/mapview_common.c     26 Sep 2005 22:34:47 -0000      1.246
+++ client/mapview_common.c     28 Sep 2005 18:36:08 -0000
@@ -1217,7 +1217,8 @@ static void show_full_citybar(struct can
   const struct citybar_sprites *citybar = get_citybar_sprites(tileset);
   const bool line1 = draw_city_names;
   const bool line2 = ((draw_city_productions || draw_city_growth)
-                     && pcity->owner == game.player_ptr);
+                     && (!game.player_ptr
+                         || pcity->owner == game.player_ptr));
   static char name[512], growth[32], prod[512], size[32];
   enum color_std growth_color;
   struct color *owner_color;
@@ -1432,7 +1433,8 @@ static void show_small_citybar(struct ca
     *width = MAX(*width, total_width);
     *height += total_height + 3;
   }
-  if (draw_city_productions && pcity->owner == game.player_ptr) {
+  if (draw_city_productions
+      && (pcity->owner == game.player_ptr || !game.player_ptr)) {
     get_city_mapview_production(pcity, prod, sizeof(prod));
     get_text_size(&prod_rect.w, &prod_rect.h, FONT_CITY_PROD, prod);
 
@@ -1818,7 +1820,7 @@ struct city *find_city_or_settler_near_t
   }
 
   if (pcity) {
-    if (pcity->owner == game.player_ptr) {
+    if (!game.player_ptr || pcity->owner == game.player_ptr) {
       /* rule a */
       return pcity;
     } else {
@@ -1832,7 +1834,8 @@ struct city *find_city_or_settler_near_t
 
   city_map_checked_iterate(ptile, city_x, city_y, tile1) {
     pcity = tile_get_city(tile1);
-    if (pcity && pcity->owner == game.player_ptr
+    if (pcity
+       && (!game.player_ptr || pcity->owner == game.player_ptr)
        && get_worker_city(pcity, CITY_MAP_SIZE - 1 - city_x,
                           CITY_MAP_SIZE - 1 - city_y) == C_TILE_EMPTY) {
       /*
@@ -1862,7 +1865,7 @@ struct city *find_city_or_settler_near_t
 
     if (tile1) {
       unit_list_iterate(tile1->units, psettler) {
-       if (psettler->owner == game.player_ptr
+       if ((!game.player_ptr || psettler->owner == game.player_ptr)
            && unit_flag(psettler, F_CITIES)
            && city_can_be_built_here(psettler->tile, psettler)) {
          if (!closest_settler) {
@@ -2134,7 +2137,7 @@ void get_city_mapview_name_and_growth(st
 {
   my_snprintf(name_buffer, name_buffer_len, pcity->name);
 
-  if (pcity->owner == game.player_ptr) {
+  if (!game.player_ptr || pcity->owner == game.player_ptr) {
     int turns = city_turns_to_grow(pcity);
 
     if (turns == 0) {
Index: client/messagewin_common.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/messagewin_common.c,v
retrieving revision 1.24
diff -p -u -r1.24 messagewin_common.c
--- client/messagewin_common.c  25 Aug 2005 20:36:12 -0000      1.24
+++ client/messagewin_common.c  28 Sep 2005 18:36:08 -0000
@@ -75,8 +75,10 @@ void update_meswin_dialog(void)
     return;
   }
 
-  if (!is_meswin_open() && messages_total > 0 &&
-      (!game.player_ptr->ai.control)) {
+  if (!is_meswin_open() && messages_total > 0
+      && !client_is_observer()
+      && game.player_ptr
+      && !game.player_ptr->ai.control) {
     popup_meswin_dialog(FALSE);
     change = FALSE;
     return;
@@ -144,7 +146,9 @@ void add_notify_window(char *message, st
     if (messages[i].location_ok) {
       struct city *pcity = tile_get_city(messages[i].tile);
 
-      messages[i].city_ok = (pcity && city_owner(pcity) == game.player_ptr);
+      messages[i].city_ok
+       = (pcity
+          && can_player_see_city_internals(game.player_ptr, pcity));
     } else {
       messages[i].city_ok = FALSE;
     }
@@ -193,7 +197,8 @@ void meswin_popup_city(int message_index
       center_tile_mapcanvas(ptile);
     }
 
-    if (pcity && city_owner(pcity) == game.player_ptr) {
+    if (pcity
+       && can_player_see_units_in_city(game.player_ptr, pcity)) {
       /* If the event was the city being destroyed, pcity will be NULL
        * and we'd better not try to pop it up.  It's also possible that
        * events will happen on enemy cities; we generally don't want to pop
Index: client/overview_common.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/overview_common.c,v
retrieving revision 1.18
diff -p -u -r1.18 overview_common.c
--- client/overview_common.c    31 Aug 2005 20:18:52 -0000      1.18
+++ client/overview_common.c    28 Sep 2005 18:36:08 -0000
@@ -112,7 +112,7 @@ static struct color *overview_tile_color
     struct city *pcity = tile_get_city(ptile);
 
     if (pcity) {
-      if (pcity->owner == game.player_ptr) {
+      if (!game.player_ptr || pcity->owner == game.player_ptr) {
        return get_color(tileset, COLOR_OVERVIEW_MY_CITY);
       } else if (pplayers_allied(city_owner(pcity), game.player_ptr)) {
        /* Includes teams. */
@@ -126,7 +126,7 @@ static struct color *overview_tile_color
     struct unit *punit = find_visible_unit(ptile);
 
     if (punit) {
-      if (punit->owner == game.player_ptr) {
+      if (!game.player_ptr || punit->owner == game.player_ptr) {
        return get_color(tileset, COLOR_OVERVIEW_MY_UNIT);
       } else if (pplayers_allied(unit_owner(punit), game.player_ptr)) {
        /* Includes teams. */
Index: client/packhand.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/packhand.c,v
retrieving revision 1.550
diff -p -u -r1.550 packhand.c
--- client/packhand.c   26 Sep 2005 22:03:09 -0000      1.550
+++ client/packhand.c   28 Sep 2005 18:36:09 -0000
@@ -600,15 +600,17 @@ static void handle_city_packet_common(st
   }
 
   if (popup
-      && (!game.player_ptr->ai.control)
-      && can_client_issue_orders()) {
+      && can_client_issue_orders()
+      && game.player_ptr
+      && !game.player_ptr->ai.control) {
     update_menus();
     if (!city_dialog_is_open(pcity)) {
       popup_city_dialog(pcity);
     }
   }
 
-  if (!is_new && (pcity->owner==game.player_ptr || popup)) {
+  if (!is_new
+      && (can_player_see_city_internals(game.player_ptr, pcity) || popup)) {
     refresh_city_dialog(pcity);
   }
 
@@ -774,14 +776,16 @@ void handle_new_year(int year, int turn)
 #if 0
   /* This information shouldn't be needed, but if it is this is the only
    * way we can get it. */
-  turn_gold_difference=game.player_ptr->economic.gold-last_turn_gold_amount;
-  last_turn_gold_amount=game.player_ptr->economic.gold;
+  if (game.player_ptr) {
+    turn_gold_difference
+      = game.player_ptr->economic.gold - last_turn_gold_amount;
+    last_turn_gold_amount = game.player_ptr->economic.gold;
+  }
 #endif
 
   update_city_descriptions();
 
-  if (sound_bell_at_new_turn &&
-      (!game.player_ptr->ai.control || ai_manual_turn_done)) {
+  if (sound_bell_at_new_turn) {
     create_event(NULL, E_TURN_BELL, _("Start of turn %d"), game.info.turn);
   }
 
@@ -912,11 +916,6 @@ void handle_unit_info(struct packet_unit
 {
   struct unit *punit;
 
-  if (packet->owner != game.info.player_idx) {
-    freelog(LOG_ERROR, "Got packet_unit_info for unit of %s.",
-            game.players[packet->owner].name);
-  }
-
   punit = unpackage_unit(packet);
   if (handle_unit_packet_common(punit)) {
     free(punit);
@@ -1001,6 +1000,7 @@ static bool handle_unit_packet_common(st
 
       /* Wakeup Focus */
       if (wakeup_focus 
+         && game.player_ptr
           && !game.player_ptr->ai.control
           && punit->owner == game.player_ptr
           && punit->activity == ACTIVITY_SENTRY
@@ -1045,7 +1045,7 @@ static bool handle_unit_packet_common(st
       punit->orders.list = packet_unit->orders.list;
       packet_unit->orders.list = NULL;
 
-      if (punit->owner == game.player_ptr) {
+      if (!game.player_ptr || punit->owner == game.player_ptr) {
         refresh_unit_city_dialogs(punit);
       }
     } /*** End of Change in activity or activity's target. ***/
@@ -1149,7 +1149,8 @@ static bool handle_unit_packet_common(st
          refresh_city_dialog(pcity);
        
         if((unit_flag(punit, F_TRADE_ROUTE) || unit_flag(punit, F_HELP_WONDER))
-          && (!game.player_ptr->ai.control)
+          && game.player_ptr
+          && !game.player_ptr->ai.control
           && punit->owner == game.player_ptr
           && !unit_has_orders(punit)
           && can_client_issue_orders()
@@ -1238,6 +1239,7 @@ static bool handle_unit_packet_common(st
   }
 
   if ((check_focus || get_unit_in_focus() == NULL)
+      && game.player_ptr
       && !game.player_ptr->ai.control
       && is_player_phase(game.player_ptr, game.info.phase)) {
     update_unit_focus();
@@ -1322,6 +1324,10 @@ void handle_unit_short_info(struct packe
 ****************************************************************************/
 void handle_map_info(int xsize, int ysize, int topology_id)
 {
+  if (!map_is_empty()) {
+    map_free();
+  }
+
   map.xsize = xsize;
   map.ysize = ysize;
   map.topology_id = topology_id;
@@ -1407,8 +1413,9 @@ static bool read_player_info_techs(struc
 **************************************************************************/
 void set_government_choice(struct government *government)
 {
-  if (government != game.player_ptr->government
-      && can_client_issue_orders()) {
+  if (can_client_issue_orders()
+      && game.player_ptr
+      && government != game.player_ptr->government) {
     dsend_packet_player_change_government(&aconnection, government->index);
   }
 }
@@ -1437,7 +1444,6 @@ void handle_player_info(struct packet_pl
 
   sz_strlcpy(pplayer->name, pinfo->name);
 
-  pplayer->is_observer = pinfo->is_observer;
   is_new_nation = player_set_nation(pplayer, get_nation_by_idx(pinfo->nation));
   pplayer->is_male=pinfo->is_male;
   team_add_player(pplayer, team_get_by_id(pinfo->team));
@@ -1514,7 +1520,7 @@ void handle_player_info(struct packet_pl
       science_dialog_update();
     }
     if (poptechup) {
-      if (!game.player_ptr->ai.control) {
+      if (game.player_ptr && !game.player_ptr->ai.control) {
        popup_science_dialog(FALSE);
       }
     }
@@ -1919,21 +1925,23 @@ void handle_tile_info(struct packet_tile
   if (old_known != packet->known) {
     known_changed = TRUE;
   }
-  BV_CLR(ptile->tile_known, game.info.player_idx);
-  BV_CLR(ptile->tile_seen, game.info.player_idx);
-  switch (packet->known) {
-  case TILE_KNOWN:
-    BV_SET(ptile->tile_known, game.info.player_idx);
-    BV_SET(ptile->tile_seen, game.info.player_idx);
-    break;
-  case TILE_KNOWN_FOGGED:
-    BV_SET(ptile->tile_known, game.info.player_idx);
-    break;
-  case TILE_UNKNOWN:
-    break;
-  default:
-    freelog(LOG_NORMAL, "Unknown tile value %d.", packet->known);
-    break;
+  if (game.player_ptr) {
+    BV_CLR(ptile->tile_known, game.info.player_idx);
+    BV_CLR(ptile->tile_seen, game.info.player_idx);
+    switch (packet->known) {
+    case TILE_KNOWN:
+      BV_SET(ptile->tile_known, game.info.player_idx);
+      BV_SET(ptile->tile_seen, game.info.player_idx);
+      break;
+    case TILE_KNOWN_FOGGED:
+      BV_SET(ptile->tile_known, game.info.player_idx);
+      break;
+    case TILE_UNKNOWN:
+      break;
+    default:
+      freelog(LOG_NORMAL, "Unknown tile value %d.", packet->known);
+      break;
+    }
   }
 
   if (packet->spec_sprite[0] != '\0') {
@@ -2371,7 +2379,6 @@ void handle_ruleset_nation(struct packet
   pl->city_style = p->city_style;
 
   pl->is_playable = p->is_playable;
-  pl->is_observer = p->is_observer;
   pl->is_barbarian = p->is_barbarian;
 
   memcpy(pl->init_techs, p->init_techs, sizeof(pl->init_techs));
@@ -2483,7 +2490,7 @@ void handle_unit_bribe_info(int unit_id,
 
   if (punit) {
     punit->bribe_cost = cost;
-    if (!game.player_ptr->ai.control) {
+    if (game.player_ptr && !game.player_ptr->ai.control) {
       popup_bribe_dialog(punit);
     }
   }
@@ -2498,7 +2505,7 @@ void handle_city_incite_info(int city_id
 
   if (pcity) {
     pcity->incite_revolt_cost = cost;
-    if (!game.player_ptr->ai.control) {
+    if (game.player_ptr && !game.player_ptr->ai.control) {
       popup_incite_dialog(pcity);
     }
   }
@@ -2532,7 +2539,7 @@ void handle_unit_diplomat_popup_dialog(i
   struct unit *pdiplomat =
       player_find_unit_by_id(game.player_ptr, diplomat_id);
 
-  if (pdiplomat) {
+  if (pdiplomat && can_client_issue_orders()) {
     process_diplomat_arrival(pdiplomat, target_id);
   }
 }
@@ -2546,7 +2553,7 @@ void handle_city_sabotage_list(int diplo
   struct unit *punit = player_find_unit_by_id(game.player_ptr, diplomat_id);
   struct city *pcity = find_city_by_id(city_id);
 
-  if (punit && pcity) {
+  if (punit && pcity && can_client_issue_orders()) {
     impr_type_iterate(i) {
       pcity->improvements[i] = BV_ISSET(improvements, i) ? I_ACTIVE : I_NONE;
     } impr_type_iterate_end;
@@ -2568,6 +2575,9 @@ void handle_endgame_report(struct packet
 **************************************************************************/
 void handle_player_attribute_chunk(struct packet_player_attribute_chunk 
*packet)
 {
+  if (!game.player_ptr) {
+    return;
+  }
   generic_handle_player_attribute_chunk(game.player_ptr, packet);
 
   if (packet->offset + packet->chunk_length == packet->total_length) {
Index: client/plrdlg_common.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/plrdlg_common.c,v
retrieving revision 1.23
diff -p -u -r1.23 plrdlg_common.c
--- client/plrdlg_common.c      18 Aug 2005 06:53:29 -0000      1.23
+++ client/plrdlg_common.c      28 Sep 2005 18:36:09 -0000
@@ -126,7 +126,7 @@ static const char *col_diplstate(const s
   static char buf[100];
   const struct player_diplstate *pds;
 
-  if (player == game.player_ptr) {
+  if (!game.player_ptr || player == game.player_ptr) {
     return "-";
   } else {
     pds = pplayer_get_diplstate(game.player_ptr, player);
@@ -145,7 +145,7 @@ static const char *col_diplstate(const s
 *******************************************************************/
 static const char *col_love(const struct player *player)
 {
-  if (player == game.player_ptr || !player->ai.control) {
+  if (!game.player_ptr || player == game.player_ptr || !player->ai.control) {
     return "-";
   } else {
     return love_text(player->ai.love[game.player_ptr->player_no]);
@@ -159,6 +159,11 @@ static int cmp_love(const struct player 
                           const struct player *player2)
 {
   int love1, love2;
+
+  if (!game.player_ptr) {
+    return player1->player_no - player2->player_no;
+  }
+
   if (player1 == game.player_ptr || !player1->ai.control) {
     love1 = MAX_AI_LOVE + 999;
   } else {
Index: client/repodlgs_common.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/repodlgs_common.c,v
retrieving revision 1.29
diff -p -u -r1.29 repodlgs_common.c
--- client/repodlgs_common.c    18 Aug 2005 06:44:27 -0000      1.29
+++ client/repodlgs_common.c    28 Sep 2005 18:36:09 -0000
@@ -47,6 +47,10 @@ void get_economy_report_data(struct impr
   *num_entries_used = 0;
   *total_cost = 0;
 
+  if (!game.player_ptr) {
+    return;
+  }
+
   impr_type_iterate(impr_id) {
     if (is_improvement(impr_id)) {
       int count = 0, cost = 0;
@@ -96,6 +100,13 @@ void get_economy_report_units_data(struc
 {
   int count, cost, partial_cost;
 
+  *num_entries_used = 0;
+  *total_cost = 0;
+
+  if (!game.player_ptr) {
+    return;
+  }
+
   unit_type_iterate(unittype) {
     cost = utype_upkeep_cost(unittype, game.player_ptr,
                              get_gov_pplayer(game.player_ptr), O_GOLD);
Index: client/reqtree.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/reqtree.c,v
retrieving revision 1.11
diff -p -u -r1.11 reqtree.c
--- client/reqtree.c    25 Sep 2005 14:06:02 -0000      1.11
+++ client/reqtree.c    28 Sep 2005 18:36:10 -0000
@@ -836,6 +836,11 @@ static enum color_std node_color(struct 
 {
   if (!node->is_dummy) {
     struct player_research* research = get_player_research(game.player_ptr);
+
+    if (!game.player_ptr || !research) {
+      return COLOR_REQTREE_KNOWN;
+    }
+
     if (research->researching == node->tech) {
       return COLOR_REQTREE_RESEARCHING;
     }
Index: client/text.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/text.c,v
retrieving revision 1.53
diff -p -u -r1.53 text.c
--- client/text.c       15 Sep 2005 18:05:51 -0000      1.53
+++ client/text.c       28 Sep 2005 18:36:10 -0000
@@ -101,11 +101,16 @@ const char *popup_info_text(struct tile 
   }
   if (game.info.borders > 0 && !pcity) {
     struct player *owner = tile_get_owner(ptile);
-    struct player_diplstate *ds = game.player_ptr->diplstates;
 
-    if (owner == game.player_ptr){
+    if (game.player_ptr && owner == game.player_ptr){
       astr_add_line(&str, _("Our Territory"));
+    } else if (owner && !game.player_ptr) {
+      /* TRANS: "Territory of the Polish" */
+      astr_add_line(&str, _("Territory of the %s"),
+                   get_nation_name_plural(owner->nation));
     } else if (owner) {
+      struct player_diplstate *ds = game.player_ptr->diplstates;
+
       if (ds[owner->player_no].type == DS_CEASEFIRE) {
        int turns = ds[owner->player_no].turns_left;
 
@@ -131,13 +136,14 @@ const char *popup_info_text(struct tile 
     /* Look at city owner, not tile owner (the two should be the same, if
      * borders are in use). */
     struct player *owner = city_owner(pcity);
-    struct player_diplstate *ds = game.player_ptr->diplstates;
 
-    if (owner == game.player_ptr){
+    if (!game.player_ptr || owner == game.player_ptr){
       /* TRANS: "City: Warsaw (Polish)" */
       astr_add_line(&str, _("City: %s (%s)"), pcity->name,
                    get_nation_name(owner->nation));
-    } else if (owner) {
+    } else {
+      struct player_diplstate *ds = game.player_ptr->diplstates;
+
       if (ds[owner->player_no].type == DS_CEASEFIRE) {
        int turns = ds[owner->player_no].turns_left;
 
@@ -183,14 +189,13 @@ const char *popup_info_text(struct tile 
   }
   if (punit && !pcity) {
     struct player *owner = unit_owner(punit);
-    struct player_diplstate *ds = game.player_ptr->diplstates;
     struct unit_type *ptype = unit_type(punit);
 
-    if (owner == game.player_ptr){
+    if (!game.player_ptr || owner == game.player_ptr){
       struct city *pcity;
       char tmp[64] = {0};
 
-      pcity = player_find_city_by_id(game.player_ptr, punit->homecity);
+      pcity = player_find_city_by_id(owner, punit->homecity);
       if (pcity) {
        my_snprintf(tmp, sizeof(tmp), "/%s", pcity->name);
       }
@@ -199,6 +204,10 @@ const char *popup_info_text(struct tile 
                    get_nation_name(owner->nation),
                    tmp);
     } else if (owner) {
+      struct player_diplstate *ds = game.player_ptr->diplstates;
+
+      assert(owner != NULL && game.player_ptr != NULL);
+
       if (ds[owner->player_no].type == DS_CEASEFIRE) {
        int turns = ds[owner->player_no].turns_left;
 
@@ -249,7 +258,7 @@ const char *popup_info_text(struct tile 
                  ptype->hp,
                  _(ptype->veteran[punit->veteran].name));
 
-    if (owner == game.player_ptr
+    if ((!game.player_ptr || owner == game.player_ptr)
        && unit_list_size(ptile->units) >= 2) {
       /* TRANS: "5 more" units on this tile */
       astr_add(&str, _("  (%d more)"), unit_list_size(ptile->units) - 1);
@@ -334,7 +343,7 @@ const char *unit_description(struct unit
 {
   int pcity_near_dist;
   struct city *pcity =
-      player_find_city_by_id(game.player_ptr, punit->homecity);
+      player_find_city_by_id(punit->owner, punit->homecity);
   struct city *pcity_near = get_nearest_city(punit, &pcity_near_dist);
   struct unit_type *ptype = unit_type(punit);
   static struct astring str = ASTRING_INIT;
@@ -373,6 +382,10 @@ static int get_bulbs_per_turn(int *pours
   struct player *plr = game.player_ptr;
   int ours = 0, theirs = 0;
 
+  if (!game.player_ptr) {
+    return 0;
+  }
+
   /* Sum up science */
   players_iterate(pplayer) {
     enum diplstate_type ds = pplayer_get_diplstate(plr, pplayer)->type;
@@ -409,9 +422,8 @@ const char *science_dialog_text(void)
 
   get_bulbs_per_turn(&ours, &theirs);
 
-  if (ours == 0 && theirs == 0) {
-    astr_add(&str, _("Progress: no research"));
-    return str.str;
+  if (!game.player_ptr || (ours == 0 && theirs == 0)) {
+    return _("Progress: no research");
   }
   assert(ours >= 0 && theirs >= 0);
   if (get_player_research(plr)->researching == A_UNSET) {
@@ -451,6 +463,10 @@ const char *get_science_target_text(doub
   struct player_research *research = get_player_research(game.player_ptr);
   static struct astring str = ASTRING_INIT;
 
+  if (!game.player_ptr) {
+    return "-";
+  }
+
   astr_clear(&str);
   if (research->researching == A_UNSET) {
     astr_add(&str, _("%d/- (never)"), research->bulbs_researched);
@@ -492,6 +508,10 @@ const char *get_science_goal_text(Tech_t
   struct player_research* research = get_player_research(game.player_ptr);
   static struct astring str = ASTRING_INIT;
 
+  if (!game.player_ptr) {
+    return "-";
+  }
+
   astr_clear(&str);
 
   if (is_tech_a_req_for_goal(game.player_ptr,
@@ -561,21 +581,25 @@ const char *get_info_label_text_popup(vo
 
   astr_clear(&str);
 
-  astr_add_line(&str, _("%s People"),
-               population_to_text(civ_population(game.player_ptr)));
+  if (game.player_ptr) {
+    astr_add_line(&str, _("%s People"),
+                 population_to_text(civ_population(game.player_ptr)));
+  }
   astr_add_line(&str, _("Year: %s"), textyear(game.info.year));
   astr_add_line(&str, _("Turn: %d"), game.info.turn);
-  astr_add_line(&str, _("Gold: %d"), game.player_ptr->economic.gold);
-  astr_add_line(&str, _("Net Income: %d"),
-               player_get_expected_income(game.player_ptr));
-  /* TRANS: Gold, luxury, and science rates are in percentage values. */
-  astr_add_line(&str, _("Tax rates: Gold:%d%% Luxury:%d%% Science:%d%%"),
-               game.player_ptr->economic.tax,
-               game.player_ptr->economic.luxury,
-               game.player_ptr->economic.science);
-  astr_add_line(&str, _("Researching %s: %s"),
-               get_tech_name(game.player_ptr, research->researching),
-               get_science_target_text(NULL));
+  if (game.player_ptr) {
+    astr_add_line(&str, _("Gold: %d"), game.player_ptr->economic.gold);
+    astr_add_line(&str, _("Net Income: %d"),
+                 player_get_expected_income(game.player_ptr));
+    /* TRANS: Gold, luxury, and science rates are in percentage values. */
+    astr_add_line(&str, _("Tax rates: Gold:%d%% Luxury:%d%% Science:%d%%"),
+                 game.player_ptr->economic.tax,
+                 game.player_ptr->economic.luxury,
+                 game.player_ptr->economic.science);
+    astr_add_line(&str, _("Researching %s: %s"),
+                 get_tech_name(game.player_ptr, research->researching),
+                 get_science_target_text(NULL));
+  }
 
   /* These mirror the code in get_global_warming_tooltip and
    * get_nuclear_winter_tooltip. */
@@ -586,8 +610,10 @@ const char *get_info_label_text_popup(vo
                CLIP(0, (game.info.nuclearwinter + 1) / 2, 100),
                DIVIDE(game.info.cooling - game.info.coolinglevel + 1, 2));
 
-  astr_add_line(&str, _("Government: %s"),
-               get_government_name(game.player_ptr->government));
+  if (game.player_ptr) {
+    astr_add_line(&str, _("Government: %s"),
+                 get_government_name(game.player_ptr->government));
+  }
 
   return str.str;
 }
@@ -626,7 +652,7 @@ const char *get_unit_info_label_text2(st
    * GUI widgets may be confused and try to resize themselves. */
   if (punit) {
     struct city *pcity =
-       player_find_city_by_id(game.player_ptr, punit->homecity);
+       player_find_city_by_id(punit->owner, punit->homecity);
     int infracount;
     bv_special infrastructure =
       get_tile_infrastructure_set(punit->tile, &infracount);
@@ -877,7 +903,9 @@ const char *get_score_text(const struct 
 
   astr_clear(&str);
 
-  if (pplayer->score.game > 0 || pplayer == game.player_ptr) {
+  if (pplayer->score.game > 0
+      || !game.player_ptr
+      || pplayer == game.player_ptr) {
     astr_add(&str, "%d", pplayer->score.game);
   } else {
     astr_add(&str, "?");
@@ -899,17 +927,23 @@ const char *get_report_title(const char 
 
   astr_add_line(&str, "%s", report_name);
 
-  /* TRANS: "Republic of the Polish" */
-  astr_add_line(&str, _("%s of the %s"),
-               get_government_name(game.player_ptr->government),
-               get_nation_name_plural(game.player_ptr->nation));
-
-  astr_add_line(&str, "%s %s: %s",
-               get_ruler_title(game.player_ptr->government,
-                               game.player_ptr->is_male,
-                               game.player_ptr->nation),
-               game.player_ptr->name,
-               textyear(game.info.year));
+  if (game.player_ptr) {
+    /* TRANS: "Republic of the Polish" */
+    astr_add_line(&str, _("%s of the %s"),
+                 get_government_name(game.player_ptr->government),
+                 get_nation_name_plural(game.player_ptr->nation));
+
+    astr_add_line(&str, "%s %s: %s",
+                 get_ruler_title(game.player_ptr->government,
+                                 game.player_ptr->is_male,
+                                 game.player_ptr->nation),
+                 game.player_ptr->name,
+                 textyear(game.info.year));
+  } else {
+    /* TRANS: "Observer: 1985" */
+    astr_add_line(&str, _("Observer: %s"),
+                 textyear(game.info.year));
+  }
   return str.str;
 }
 
Index: client/tilespec.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/tilespec.c,v
retrieving revision 1.324
diff -p -u -r1.324 tilespec.c
--- client/tilespec.c   22 Sep 2005 04:50:51 -0000      1.324
+++ client/tilespec.c   28 Sep 2005 18:36:11 -0000
@@ -3643,8 +3643,10 @@ static int fill_grid_sprite_array(const 
        && base_map_to_city_map(&dummy_x, &dummy_y, pfocus->tile, tile);
       worked[i] = FALSE;
 
-      city[i] = tile && (powner == NULL || powner == game.player_ptr)
-       && player_in_city_radius(game.player_ptr, tile);
+      city[i] = (tile
+                && (!powner || !game.player_ptr || powner == game.player_ptr)
+                && (!game.player_ptr
+                    || player_in_city_radius(game.player_ptr, tile)));
       if (city[i]) {
        if (citymode) {
          int cx, cy;
@@ -4252,7 +4254,7 @@ struct unit *get_drawable_unit(const str
   if (!punit)
     return NULL;
 
-  if (citymode && punit->owner == game.player_ptr)
+  if (citymode && punit->owner == citymode->owner)
     return NULL;
 
   if (punit != pfocus
Index: client/gui-gtk-2.0/citydlg.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/gui-gtk-2.0/citydlg.c,v
retrieving revision 1.140
diff -p -u -r1.140 citydlg.c
--- client/gui-gtk-2.0/citydlg.c        8 Aug 2005 16:15:29 -0000       1.140
+++ client/gui-gtk-2.0/citydlg.c        28 Sep 2005 18:36:12 -0000
@@ -375,7 +375,7 @@ void refresh_city_dialog(struct city *pc
   city_dialog_update_supported_units(pdialog);
   city_dialog_update_present_units(pdialog);
 
-  if (city_owner(pcity) == game.player_ptr) {
+  if (!game.player_ptr || city_owner(pcity) == game.player_ptr) {
     bool have_present_units =
        (unit_list_size(pcity->tile->units) > 0);
     refresh_worklist(pdialog->production.worklist);
@@ -870,7 +870,7 @@ target_drag_data_received(GtkWidget *w, 
   GtkTreeModel *model;
   GtkTreePath *path;
 
-  if (pdialog->pcity->owner != game.player_ptr) {
+  if (game.player_ptr && pdialog->pcity->owner != game.player_ptr) {
     gtk_drag_finish(context, FALSE, FALSE, time);
   }
     
@@ -1224,7 +1224,7 @@ static struct city_dialog *create_city_d
   create_and_append_worklist_page(pdialog);
 
   /* only create these tabs if not a spy */
-  if (pcity->owner == game.player_ptr) {
+  if (!game.player_ptr || pcity->owner == game.player_ptr) {
     create_and_append_happiness_page(pdialog);
     create_and_append_cma_page(pdialog);
   }
@@ -1263,7 +1263,7 @@ static struct city_dialog *create_city_d
   gtk_dialog_add_action_widget(GTK_DIALOG(pdialog->shell),
                               pdialog->next_command, 2);
   
-  if (pcity->owner != game.player_ptr) {
+  if (!game.player_ptr || pcity->owner != game.player_ptr) {
     gtk_widget_set_sensitive(pdialog->prev_command, FALSE);
     gtk_widget_set_sensitive(pdialog->next_command, FALSE);
   }
@@ -1633,7 +1633,7 @@ static void city_dialog_update_supported
   int n, m, i;
   char buf[30];
 
-  if (pdialog->pcity->owner != game.player_ptr) {
+  if (game.player_ptr && pdialog->pcity->owner != game.player_ptr) {
     units = pdialog->pcity->info_units_supported;
   } else {
     units = pdialog->pcity->units_supported;
@@ -1752,7 +1752,7 @@ static void city_dialog_update_present_u
   int n, m, i;
   char buf[30];
 
-  if (pdialog->pcity->owner != game.player_ptr) {
+  if (game.player_ptr && pdialog->pcity->owner != game.player_ptr) {
     units = pdialog->pcity->info_units_present;
   } else {
     units = pdialog->pcity->tile->units;
@@ -1866,7 +1866,13 @@ static void city_dialog_update_present_u
 static void city_dialog_update_prev_next()
 {
   int count = 0;
-  int city_number = city_list_size(game.player_ptr->cities);
+  int city_number;
+
+  if (game.player_ptr) {
+    city_number = city_list_size(game.player_ptr->cities);
+  } else {
+    city_number = FC_INFINITY; /* ? */
+  }
 
   /* the first time, we see if all the city dialogs are open */
 
@@ -2105,7 +2111,8 @@ static gboolean present_unit_callback(Gt
       GINT_TO_POINTER(punit->id));
     gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
 
-    if (can_upgrade_unittype(game.player_ptr, punit->type) == NULL) {
+    if (!can_client_issue_orders()
+       || can_upgrade_unittype(game.player_ptr, punit->type) == NULL) {
       gtk_widget_set_sensitive(item, FALSE);
     }
 
@@ -2411,12 +2418,14 @@ static void buy_callback_response(GtkWid
 *****************************************************************/
 static void buy_callback(GtkWidget *w, gpointer data)
 {
-  struct city_dialog *pdialog;
+  struct city_dialog *pdialog = data;
   int value;
   const char *name;
   GtkWidget *shell;
 
-  pdialog = (struct city_dialog *) data;
+  if (!can_client_issue_orders()) {
+    return;
+  }
 
   if (pdialog->pcity->production.is_unit) {
     name = get_unit_type(pdialog->pcity->production.value)->name;
@@ -2718,10 +2727,8 @@ static void city_destroy_callback(GtkWid
 
   gtk_widget_hide(pdialog->shell);
 
-  if (pdialog->pcity->owner == game.player_ptr) {
-    close_happiness_dialog(pdialog->pcity);
-    close_cma_dialog(pdialog->pcity);
-  }
+  close_happiness_dialog(pdialog->pcity);
+  close_cma_dialog(pdialog->pcity);
 
   citydialog_height = pdialog->shell->allocation.height;
   citydialog_width = pdialog->shell->allocation.width;
@@ -2787,9 +2794,14 @@ static void close_city_dialog(struct cit
 static void switch_city_callback(GtkWidget *w, gpointer data)
 {
   struct city_dialog *pdialog = (struct city_dialog *) data;
-  int i, j, dir, size = city_list_size(game.player_ptr->cities);
+  int i, j, dir, size;
   struct city *new_pcity = NULL;
 
+  if (!game.player_ptr) {
+    return;
+  }
+  size = city_list_size(game.player_ptr->cities);
+
   assert(city_dialogs_have_been_initialised);
   assert(size >= 1);
   assert(pdialog->pcity->owner == game.player_ptr);
Index: client/gui-gtk-2.0/cityrep.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/gui-gtk-2.0/cityrep.c,v
retrieving revision 1.79
diff -p -u -r1.79 cityrep.c
--- client/gui-gtk-2.0/cityrep.c        8 Aug 2005 16:15:29 -0000       1.79
+++ client/gui-gtk-2.0/cityrep.c        28 Sep 2005 18:36:13 -0000
@@ -535,6 +535,9 @@ static void append_cma_to_menu_item(GtkM
   GtkWidget *w;
 
   gtk_menu_item_remove_submenu(parent_item);
+  if (!can_client_issue_orders()) {
+    return;
+  }
   menu = gtk_menu_new();
   gtk_menu_item_set_submenu(parent_item, menu);
 
@@ -1099,8 +1102,7 @@ static void update_row(GtkTreeIter *row,
 *****************************************************************/
 static void city_model_init(void)
 {
-  if (city_dialog_shell && !is_report_dialogs_frozen()) {
-
+  if (city_dialog_shell && !is_report_dialogs_frozen() && game.player_ptr) {
     city_list_iterate(game.player_ptr->cities, pcity) {
       GtkTreeIter it;
 
@@ -1138,14 +1140,16 @@ void city_report_dialog_update(void)
     /* update. */
     gtk_list_store_clear(city_model);
 
-    city_list_iterate(game.player_ptr->cities, pcity) {
-      gtk_list_store_append(city_model, &it);
-      update_row(&it, pcity);
+    if (game.player_ptr) {
+      city_list_iterate(game.player_ptr->cities, pcity) {
+       gtk_list_store_append(city_model, &it);
+       update_row(&it, pcity);
 
-      if (g_hash_table_remove(copy, pcity)) {
-       gtk_tree_selection_select_iter(city_selection, &it);
-      }
-    } city_list_iterate_end;
+       if (g_hash_table_remove(copy, pcity)) {
+         gtk_tree_selection_select_iter(city_selection, &it);
+       }
+      } city_list_iterate_end;
+    }
 
     /* free the selection. */
     g_hash_table_destroy(copy);
Index: client/gui-gtk-2.0/diplodlg.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/gui-gtk-2.0/diplodlg.c,v
retrieving revision 1.35
diff -p -u -r1.35 diplodlg.c
--- client/gui-gtk-2.0/diplodlg.c       16 Sep 2005 06:59:54 -0000      1.35
+++ client/gui-gtk-2.0/diplodlg.c       28 Sep 2005 18:36:13 -0000
@@ -20,28 +20,30 @@
 
 #include <gtk/gtk.h>
 
+#include "mem.h"
+#include "shared.h"
+#include "support.h"
+
+#include "diptreaty.h"
 #include "fcintl.h"
 #include "game.h"
 #include "government.h"
 #include "map.h"
-#include "mem.h"
 #include "packets.h"
 #include "player.h"
-#include "shared.h"
-#include "support.h"
 
 #include "chatline.h"
+#include "civclient.h"
 #include "climisc.h"
 #include "clinet.h"
-#include "diptreaty.h"
+#include "options.h"
+
+#include "diplodlg.h"
 #include "gui_main.h"
 #include "gui_stuff.h"
 #include "mapview.h"
-#include "options.h"
 #include "plrdlg.h"
 
-#include "diplodlg.h"
-
 #define MAX_NUM_CLAUSES 64
 
 struct Diplomacy_dialog {
@@ -170,6 +172,10 @@ static void popup_diplomacy_dialog(int o
 {
   struct Diplomacy_dialog *pdialog = find_diplomacy_dialog(other_player_id);
 
+  if (!can_client_issue_orders()) {
+    return;
+  }
+
   if (game.player_ptr->ai.control) {
     return;                    /* Don't show if we are AI controlled. */
   }
Index: client/gui-gtk-2.0/gamedlgs.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/gui-gtk-2.0/gamedlgs.c,v
retrieving revision 1.30
diff -p -u -r1.30 gamedlgs.c
--- client/gui-gtk-2.0/gamedlgs.c       2 May 2005 08:45:19 -0000       1.30
+++ client/gui-gtk-2.0/gamedlgs.c       28 Sep 2005 18:36:13 -0000
@@ -22,23 +22,25 @@
 #include <gtk/gtk.h>
 #include <gdk/gdkkeysyms.h>
 
+#include "shared.h"
+#include "support.h"
+
 #include "events.h"
 #include "fcintl.h"
 #include "game.h"
 #include "government.h"
 #include "packets.h"
 #include "player.h"
-#include "shared.h"
-#include "support.h"
+
+#include "civclient.h"
+#include "clinet.h"
+#include "options.h"
 
 #include "chatline.h"
 #include "cityrep.h"
 #include "dialogs.h"
-#include "clinet.h"
 #include "gui_main.h"
 #include "gui_stuff.h"
-#include "options.h"
-
 #include "ratesdlg.h"
 #include "optiondlg.h"
 
@@ -73,7 +75,11 @@ static void rates_set_values(int tax, in
   lux_lock     = GTK_TOGGLE_BUTTON(rates_lux_toggle)->active;
   sci_lock     = GTK_TOGGLE_BUTTON(rates_sci_toggle)->active;
 
-  maxrate = get_player_bonus(game.player_ptr, EFT_MAX_RATES);
+  if (game.player_ptr) {
+    maxrate = get_player_bonus(game.player_ptr, EFT_MAX_RATES);
+  } else {
+    maxrate = 100;
+  }
   /* This's quite a simple-minded "double check".. */
   tax=MIN(tax, maxrate);
   lux=MIN(lux, maxrate);
@@ -213,6 +219,10 @@ static GtkWidget *create_rates_dialog(vo
   GtkWidget    *frame, *hbox;
 
   GtkWidget    *scale;
+
+  if (!can_client_issue_orders()) {
+    return NULL;
+  }
   
   shell = gtk_dialog_new_with_buttons(_("Select tax, luxury and science 
rates"),
        NULL,
@@ -330,8 +340,16 @@ void popup_rates_dialog(void)
 {
   char buf[64];
 
-  if (!rates_dialog_shell)
+  if (!can_client_issue_orders()) {
+    return;
+  }
+
+  if (!rates_dialog_shell) {
     rates_dialog_shell = create_rates_dialog();
+  }
+  if (!rates_dialog_shell) {
+    return;
+  }
 
   my_snprintf(buf, sizeof(buf), _("%s max rate: %d%%"),
       get_government_name(game.player_ptr->government),
Index: client/gui-gtk-2.0/gui_main.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/gui-gtk-2.0/gui_main.c,v
retrieving revision 1.137
diff -p -u -r1.137 gui_main.c
--- client/gui-gtk-2.0/gui_main.c       29 Aug 2005 13:56:49 -0000      1.137
+++ client/gui-gtk-2.0/gui_main.c       28 Sep 2005 18:36:13 -0000
@@ -384,7 +384,7 @@ static gboolean keyboard_handler(GtkWidg
     return TRUE;
   }
 
-  if (!client_is_observer()) {
+  if (!client_is_observer()) { /* FIXME: is this check right? */
     if ((ev->state & GDK_SHIFT_MASK)) {
       switch (ev->keyval) {
        case GDK_Left:
@@ -1260,9 +1260,6 @@ void update_conn_list_dialog(void)
     players_iterate(pplayer) {
       enum cmdlevel_id access_level = ALLOW_NONE;
 
-      if (pplayer->is_observer) {
-       continue; /* Connections are listed individually. */
-      }
       conn_list_iterate(pplayer->connections, pconn) {
         access_level = MAX(pconn->access_level, access_level);
       } conn_list_iterate_end;
@@ -1309,7 +1306,7 @@ void update_conn_list_dialog(void)
                         -1);
     } players_iterate_end;
     conn_list_iterate(game.est_connections, pconn) {
-      if (pconn->player && !pconn->observer && !pconn->player->is_observer) {
+      if (pconn->player && !pconn->observer) {
        continue; /* Already listed above. */
       }
       sz_strlcpy(name, pconn->username);
Index: client/gui-gtk-2.0/happiness.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/gui-gtk-2.0/happiness.c,v
retrieving revision 1.23
diff -p -u -r1.23 happiness.c
--- client/gui-gtk-2.0/happiness.c      4 Jul 2005 17:48:37 -0000       1.23
+++ client/gui-gtk-2.0/happiness.c      28 Sep 2005 18:36:13 -0000
@@ -224,8 +224,8 @@ static void happiness_dialog_update_citi
   int cities = city_list_size(pplayer->cities);
   int content = game.info.unhappysize;
   int basis = game.info.cityfactor 
-              + get_player_bonus(game.player_ptr, EFT_EMPIRE_SIZE_MOD);
-  int step = get_player_bonus(game.player_ptr, EFT_EMPIRE_SIZE_STEP);
+              + get_player_bonus(pcity->owner, EFT_EMPIRE_SIZE_MOD);
+  int step = get_player_bonus(pcity->owner, EFT_EMPIRE_SIZE_STEP);
   int excess = cities - basis;
   int penalty = 0;
 
Index: client/gui-gtk-2.0/menu.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/gui-gtk-2.0/menu.c,v
retrieving revision 1.84
diff -p -u -r1.84 menu.c
--- client/gui-gtk-2.0/menu.c   18 Aug 2005 06:44:27 -0000      1.84
+++ client/gui-gtk-2.0/menu.c   28 Sep 2005 18:36:13 -0000
@@ -553,8 +553,10 @@ static void reports_menu_callback(gpoint
    case MENU_REPORT_DEMOGRAPHIC:
     send_report_request(REPORT_DEMOGRAPHIC);
     break;
-   case MENU_REPORT_SPACESHIP:
-    popup_spaceship_dialog(game.player_ptr);
+  case MENU_REPORT_SPACESHIP:
+    if (game.player_ptr) {
+      popup_spaceship_dialog(game.player_ptr);
+    }
     break;
   }
 }
@@ -1246,7 +1248,8 @@ void update_menus(void)
                        can_client_issue_orders());
 
     menus_set_sensitive("<main>/_Reports/S_paceship",
-                       (game.player_ptr->spaceship.state!=SSHIP_NONE));
+                       (game.player_ptr
+                        && game.player_ptr->spaceship.state != SSHIP_NONE));
 
     menus_set_active("<main>/_View/City Outlines", draw_city_outlines);
     menus_set_active("<main>/_View/Map _Grid", draw_map_grid);
@@ -1385,7 +1388,7 @@ void update_menus(void)
                    get_tile_change_menu_text(punit->tile,
                                              ACTIVITY_IRRIGATE));
       } else if (tile_has_special(punit->tile, S_IRRIGATION)
-                && player_knows_techs_with_flag(game.player_ptr,
+                && player_knows_techs_with_flag(punit->owner,
                                                 TF_FARMLAND)) {
        sz_strlcpy(irrtext, _("Bu_ild Farmland"));
       }
Index: client/gui-gtk-2.0/pages.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/gui-gtk-2.0/pages.c,v
retrieving revision 1.42
diff -p -u -r1.42 pages.c
--- client/gui-gtk-2.0/pages.c  26 Sep 2005 18:59:11 -0000      1.42
+++ client/gui-gtk-2.0/pages.c  28 Sep 2005 18:36:14 -0000
@@ -942,7 +942,9 @@ static void start_start_callback(GtkWidg
 **************************************************************************/
 static void pick_nation_callback(GtkWidget *w, gpointer data)
 {
-  popup_races_dialog(game.player_ptr);
+  if (game.player_ptr) {
+    popup_races_dialog(game.player_ptr);
+  }
 }
 
 /**************************************************************************
Index: client/gui-gtk-2.0/plrdlg.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/gui-gtk-2.0/plrdlg.c,v
retrieving revision 1.68
diff -p -u -r1.68 plrdlg.c
--- client/gui-gtk-2.0/plrdlg.c 1 Aug 2005 06:44:44 -0000       1.68
+++ client/gui-gtk-2.0/plrdlg.c 28 Sep 2005 18:36:14 -0000
@@ -146,15 +146,20 @@ static void update_players_menu(void)
       gtk_widget_set_sensitive(players_sship_command, FALSE);
     }
 
-    switch (pplayer_get_diplstate(game.player_ptr, get_player(plrno))->type) {
-    case DS_WAR:
-    case DS_NO_CONTACT:
+    if (game.player_ptr) {
+      switch (pplayer_get_diplstate(game.player_ptr,
+                                   get_player(plrno))->type) {
+      case DS_WAR:
+      case DS_NO_CONTACT:
+       gtk_widget_set_sensitive(players_war_command, FALSE);
+       break;
+      default:
+       gtk_widget_set_sensitive(players_war_command,
+                                can_client_issue_orders()
+                                && game.info.player_idx != plrno);
+      }
+    } else {
       gtk_widget_set_sensitive(players_war_command, FALSE);
-      break;
-    default:
-      gtk_widget_set_sensitive(players_war_command,
-                              can_client_issue_orders()
-                              && game.info.player_idx != plrno);
     }
 
     gtk_widget_set_sensitive(players_vision_command,
@@ -576,7 +581,7 @@ static void build_row(GtkTreeIter *it, i
 {
   struct player *plr = get_player(i);
   GdkPixbuf *pixbuf;
-  gint style, weight;
+  gint style = PANGO_STYLE_NORMAL, weight = PANGO_WEIGHT_NORMAL;
   int k;
   gchar *p;
 
@@ -610,21 +615,29 @@ static void build_row(GtkTreeIter *it, i
     -1);
 
    /* now add some eye candy ... */
-   switch (pplayer_get_diplstate(game.player_ptr, plr)->type) {
-   case DS_WAR:
-     weight = PANGO_WEIGHT_NORMAL;
-     style = PANGO_STYLE_ITALIC;
-     break;
-   case DS_ALLIANCE:
-   case DS_TEAM:
-     weight = PANGO_WEIGHT_BOLD;
-     style = PANGO_STYLE_NORMAL;
-     break;
-   default:
-     weight = PANGO_WEIGHT_NORMAL;
-     style = PANGO_STYLE_NORMAL;
-     break;
-   }
+  if (game.player_ptr) {
+    switch (pplayer_get_diplstate(game.player_ptr, plr)->type) {
+    case DS_WAR:
+      weight = PANGO_WEIGHT_NORMAL;
+      style = PANGO_STYLE_ITALIC;
+      break;
+    case DS_ALLIANCE:
+    case DS_TEAM:
+      weight = PANGO_WEIGHT_BOLD;
+      style = PANGO_STYLE_NORMAL;
+      break;
+    case DS_NEUTRAL:
+    case DS_CEASEFIRE:
+    case DS_PEACE:
+    case DS_NO_CONTACT:
+      weight = PANGO_WEIGHT_NORMAL;
+      style = PANGO_STYLE_NORMAL;
+      break;
+    case DS_LAST:
+      assert(0);
+      break;
+    }
+  }
    gtk_list_store_set(store, it,
        num_player_dlg_columns, style,
        num_player_dlg_columns + 1, weight,
Index: client/gui-gtk-2.0/repodlgs.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/gui-gtk-2.0/repodlgs.c,v
retrieving revision 1.97
diff -p -u -r1.97 repodlgs.c
--- client/gui-gtk-2.0/repodlgs.c       16 Aug 2005 06:54:53 -0000      1.97
+++ client/gui-gtk-2.0/repodlgs.c       28 Sep 2005 18:36:15 -0000
@@ -145,7 +145,8 @@ void popup_science_dialog(bool raise)
     create_science_dialog(FALSE);
   }
 
-  if (get_player_research(game.player_ptr)->tech_goal == A_UNSET
+  if (can_client_issue_orders()
+      && get_player_research(game.player_ptr)->tech_goal == A_UNSET
       && get_player_research(game.player_ptr)->researching == A_UNSET) {
     gui_dialog_alert(science_dialog_shell);
   } else {
@@ -182,7 +183,7 @@ static void button_release_event_callbac
   if (tech == A_NONE) {
     return;
   }
-  if (event->button == 1) {
+  if (event->button == 1 && can_client_issue_orders()) {
     /* LMB: set research or research goal */
     switch (get_invention(game.player_ptr, tech)) {
     case TECH_REACHABLE:
@@ -227,6 +228,7 @@ static GtkWidget *create_reqtree_diagram
   GtkAdjustment* adjustment;
   int width, height;
   int x;
+  Tech_type_id researching;
 
   get_reqtree_dimensions(reqtree, &width, &height);
 
@@ -252,8 +254,12 @@ static GtkWidget *create_reqtree_diagram
   adjustment = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(sw));
   
   /* Center on currently researched node */
-  if (find_tech_on_reqtree(reqtree,
-                           get_player_research(game.player_ptr)->researching,
+  if (game.player_ptr) {
+    researching = get_player_research(game.player_ptr)->researching;
+  } else {
+    researching = A_UNSET;
+  }
+  if (find_tech_on_reqtree(reqtree, researching,
                           &x, NULL, NULL, NULL)) {
     /* FIXME: this is just an approximation */
     gtk_adjustment_set_value(adjustment, x - 100);
@@ -397,26 +403,10 @@ void science_goal_callback(GtkWidget *wi
 static gint cmp_func(gconstpointer a_p, gconstpointer b_p)
 {
   const gchar *a_str, *b_str;
-  gchar text_a[512], text_b[512];
   gint a = GPOINTER_TO_INT(a_p), b = GPOINTER_TO_INT(b_p);
 
-  /* FIXME: future techs aren't counted this way but are handled by
-   * get_tech_name() when given a player parameter. */
-  if (!is_future_tech(a)) {
-    a_str = get_tech_name(game.player_ptr, a);
-  } else {
-    my_snprintf(text_a,sizeof(text_a), _("Future Tech. %d"),
-               a - game.control.num_tech_types);
-    a_str=text_a;
-  }
-
-  if(!is_future_tech(b)) {
-    b_str = get_tech_name(game.player_ptr, b);
-  } else {
-    my_snprintf(text_b,sizeof(text_b), _("Future Tech. %d"),
-               b - game.control.num_tech_types);
-    b_str=text_b;
-  }
+  a_str = get_tech_name(game.player_ptr, a);
+  b_str = get_tech_name(game.player_ptr, b);
 
   return strcmp(a_str,b_str);
 }
@@ -434,7 +424,7 @@ void science_dialog_update(void)
   GtkSizeGroup *group1, *group2;
   struct player_research *research = get_player_research(game.player_ptr);
 
-  if (is_report_dialogs_frozen()) {
+  if (!game.player_ptr || !research || is_report_dialogs_frozen()) {
     return;
   }
 
@@ -1069,14 +1059,10 @@ static void activeunits_selection_callba
                                      ACTIVEUNITS_NEAREST,
                                      can_client_issue_orders());       
     
-    if (can_upgrade_unittype(game.player_ptr, utype) != NULL) {
-      gui_dialog_set_response_sensitive(activeunits_dialog_shell,
-                                       ACTIVEUNITS_UPGRADE,
-                                       can_client_issue_orders());     
-    } else {
-      gui_dialog_set_response_sensitive(activeunits_dialog_shell,
-                                       ACTIVEUNITS_UPGRADE, FALSE);
-    }
+    gui_dialog_set_response_sensitive(activeunits_dialog_shell,
+               ACTIVEUNITS_UPGRADE,
+               (can_client_issue_orders()
+                && can_upgrade_unittype(game.player_ptr, utype) != NULL));
   }
 }
 
@@ -1087,9 +1073,12 @@ static struct unit *find_nearest_unit(co
                                      struct tile *ptile)
 {
   struct unit *best_candidate;
-  int best_dist = 99999;
+  int best_dist = FC_INFINITY;
 
   best_candidate = NULL;
+  if (!game.player_ptr) {
+    return NULL;
+  }
   unit_list_iterate(game.player_ptr->units, punit) {
     if (punit->type == type) {
       if (punit->focus_status==FOCUS_AVAIL
@@ -1153,7 +1142,7 @@ static void activeunits_command_callback
        }
       }
     }
-  } else {
+  } else if (can_client_issue_orders()) {
     GtkWidget *shell;
     struct unit_type *ut2 = can_upgrade_unittype(game.player_ptr, utype1);
 
@@ -1189,10 +1178,12 @@ void activeunits_report_dialog_update(vo
     int building_count;
   };
 
-  if (is_report_dialogs_frozen())
+  if (is_report_dialogs_frozen() || !activeunits_dialog_shell) {
     return;
+  }
 
-  if (activeunits_dialog_shell) {
+  gtk_list_store_clear(activeunits_store);
+  if (game.player_ptr) {
     int    k, can;
     struct repoinfo unitarray[U_LAST];
     struct repoinfo unittotals;
Index: client/gui-gtk-2.0/wldlg.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/gui-gtk-2.0/wldlg.c,v
retrieving revision 1.52
diff -p -u -r1.52 wldlg.c
--- client/gui-gtk-2.0/wldlg.c  8 Aug 2005 16:15:29 -0000       1.52
+++ client/gui-gtk-2.0/wldlg.c  28 Sep 2005 18:36:15 -0000
@@ -117,7 +117,11 @@ void update_worklist_report_dialog(void)
   plr = game.player_ptr;
 
   gtk_list_store_clear(worklists_store);
-  
+
+  if (!game.player_ptr) {
+    return;
+  }
+
   for (i = 0; i < MAX_NUM_WORKLISTS; i++) {
     if (plr->worklists[i].is_valid) {
       gtk_list_store_append(worklists_store, &it);
@@ -143,6 +147,10 @@ static void worklists_response(GtkWidget
 
   plr = game.player_ptr;
 
+  if (!game.player_ptr) {
+    return;
+  }
+
   for (i = 0; i < MAX_NUM_WORKLISTS; i++) {
     if (!plr->worklists[i].is_valid) {
       break;
@@ -209,13 +217,12 @@ static void cell_edited(GtkCellRendererT
   GtkTreePath *path;
   GtkTreeIter it;
   int pos;
-  struct player *plr;
+  struct player *plr = game.player_ptr;
 
   path = gtk_tree_path_new_from_string(spath);
   gtk_tree_model_get_iter(GTK_TREE_MODEL(worklists_store), &it, path);
   
   gtk_tree_model_get(GTK_TREE_MODEL(worklists_store), &it, 1, &pos, -1);
-  plr = game.player_ptr;
 
   sz_strlcpy(plr->worklists[pos].name, text);
   gtk_list_store_set(worklists_store, &it, 0, text, -1);
@@ -473,6 +480,10 @@ static void menu_item_callback(GtkMenuIt
   gint pos;
   struct worklist *pwl;
 
+  if (!game.player_ptr) {
+    return;
+  }
+
   plr = game.player_ptr;
   pos = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(item), "pos"));
 
@@ -508,6 +519,10 @@ static void popup_add_menu(GtkMenuShell 
                        (GtkCallback) gtk_widget_destroy, NULL);
   plr = game.player_ptr;
 
+  if (!game.player_ptr) {
+    return;
+  }
+
   for (i = 0; i < MAX_NUM_WORKLISTS; i++) {
     if (plr->worklists[i].is_valid) {
       item = gtk_menu_item_new_with_label(plr->worklists[i].name);
Index: common/game.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/game.h,v
retrieving revision 1.200
diff -p -u -r1.200 game.h
--- common/game.h       26 Sep 2005 18:59:11 -0000      1.200
+++ common/game.h       28 Sep 2005 18:36:15 -0000
@@ -70,7 +70,7 @@ struct civ_game {
    * take effect until the next turn. */
   bool simultaneous_phases_stored;
   char *startmessage;
-  struct player *player_ptr;
+  struct player *player_ptr; /* Client-only; may be NULL */
   struct player players[MAX_NUM_PLAYERS + MAX_NUM_BARBARIANS];
   struct conn_list *all_connections;        /* including not yet established */
   struct conn_list *est_connections;        /* all established client conns */
Index: common/government.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/government.c,v
retrieving revision 1.61
diff -p -u -r1.61 government.c
--- common/government.c 18 Aug 2005 06:44:28 -0000      1.61
+++ common/government.c 28 Sep 2005 18:36:15 -0000
@@ -137,12 +137,17 @@ const char *get_government_name(const st
    - no required tech (required is A_NONE)
    - player has required tech
    - we have an appropriate wonder
+  Returns FALSE if pplayer is NULL (used for observers).
 ***************************************************************/
 bool can_change_to_government(struct player *pplayer,
                              const struct government *gov)
 {
   CHECK_GOVERNMENT(gov);
 
+  if (!pplayer) {
+    return FALSE;
+  }
+
   if (get_player_bonus(pplayer, EFT_ANY_GOVERNMENT) > 0) {
     /* Note, this may allow govs that are on someone else's "tech tree". */
     return TRUE;
Index: common/nation.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/nation.c,v
retrieving revision 1.57
diff -p -u -r1.57 nation.c
--- common/nation.c     21 Sep 2005 06:45:24 -0000      1.57
+++ common/nation.c     28 Sep 2005 18:36:15 -0000
@@ -124,20 +124,6 @@ bool is_nation_playable(const struct nat
 }
 
 /****************************************************************************
-  Return whether a nation is usable as an observer.  If true then observers
-  can use this nation.
-
-  This does not check whether a nation is "used" or "available".
-****************************************************************************/
-bool is_nation_observer(const struct nation_type *nation)
-{
-  if (!bounds_check_nation(nation, LOG_FATAL, "is_nation_observer")) {
-    die("wrong nation %d", nation->index);
-  }
-  return nation->is_observer;
-}
-
-/****************************************************************************
   Return whether a nation is usable as a barbarian.  If true then barbarians
   can use this nation.
 
Index: common/nation.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/nation.h,v
retrieving revision 1.57
diff -p -u -r1.57 nation.h
--- common/nation.h     21 Sep 2005 06:45:24 -0000      1.57
+++ common/nation.h     28 Sep 2005 18:36:15 -0000
@@ -77,7 +77,7 @@ struct nation_type {
   struct city_name *city_names;                /* The default city names. */
   char *legend;                                /* may be empty */
 
-  bool is_playable, is_barbarian, is_observer;
+  bool is_playable, is_barbarian;
 
   /* civilwar_nations is a NO_NATION_SELECTED-terminated list of index of
    * the nations that can fork from this one.  parent_nations is the inverse
@@ -116,7 +116,6 @@ const char *get_nation_name(const struct
 const char *get_nation_name_plural(const struct nation_type *nation);
 const char *get_nation_name_orig(const struct nation_type *nation);
 bool is_nation_playable(const struct nation_type *nation);
-bool is_nation_observer(const struct nation_type *nation);
 bool is_nation_barbarian(const struct nation_type *nation);
 struct leader *get_nation_leaders(const struct nation_type *nation, int *dim);
 struct nation_type **get_nation_civilwar(const struct nation_type *nation);
Index: common/packets.def
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/packets.def,v
retrieving revision 1.158
diff -p -u -r1.158 packets.def
--- common/packets.def  26 Sep 2005 18:59:11 -0000      1.158
+++ common/packets.def  28 Sep 2005 18:36:16 -0000
@@ -638,7 +638,6 @@ PACKET_PLAYER_INFO=39; sc
   STRING username[MAX_LEN_NAME];
 
   UINT32 score;
-  BOOL is_observer;
   BOOL is_male;
   GOVERNMENT government;
   GOVERNMENT target_government;
@@ -1180,7 +1179,7 @@ PACKET_RULESET_NATION=102;sc,lsend
   STRING leader_name[MAX_NUM_LEADERS:leader_count][MAX_LEN_NAME];
   BOOL leader_sex[MAX_NUM_LEADERS:leader_count];
 
-  BOOL is_available, is_playable, is_observer, is_barbarian;
+  BOOL is_available, is_playable, is_barbarian;
   
   UINT8 group_count;
   STRING group_name[MAX_NUM_NATION_GROUPS:group_count][MAX_LEN_NAME];
Index: common/player.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/player.c,v
retrieving revision 1.193
diff -p -u -r1.193 player.c
--- common/player.c     17 Sep 2005 15:50:10 -0000      1.193
+++ common/player.c     28 Sep 2005 18:36:16 -0000
@@ -127,7 +127,6 @@ void player_init(struct player *plr)
   plr->connections = conn_list_new();
   plr->current_conn = NULL;
   plr->is_connected = FALSE;
-  plr->is_observer = FALSE;
   plr->was_created = FALSE;
   plr->is_alive=TRUE;
   plr->is_dying = FALSE;
@@ -338,11 +337,15 @@ bool can_player_see_unit(const struct pl
   Note that can_player_see_city_internals => can_player_see_units_in_city.
   Otherwise the player would not know anything about the city's units at
   all, since the full city packet has no "occupied" flag.
+
+  Returns TRUE if given a NULL player.  This is used by the client when in
+  observer mode.
 ****************************************************************************/
 bool can_player_see_units_in_city(const struct player *pplayer,
                                  const struct city *pcity)
 {
-  return (can_player_see_city_internals(pplayer, pcity)
+  return (!pplayer
+         || can_player_see_city_internals(pplayer, pcity)
          || pplayers_allied(pplayer, city_owner(pcity)));
 }
 
@@ -350,17 +353,22 @@ bool can_player_see_units_in_city(const 
   Return TRUE iff the player can see the city's internals.  This means the
   full city packet is sent to the client, who should then be able to popup
   a dialog for it.
+
+  Returns TRUE if given a NULL player.  This is used by the client when in
+  observer mode.
 ****************************************************************************/
 bool can_player_see_city_internals(const struct player *pplayer,
                                   const struct city *pcity)
 {
-  return (pplayer == city_owner(pcity));
+  return (!pplayer || pplayer == city_owner(pcity));
 }
 
 /***************************************************************
  If the specified player owns the city with the specified id,
  return pointer to the city struct.  Else return NULL.
  Now always uses fast idex_lookup_city.
+
+  pplayer may be NULL in which case all cities will be considered.
 ***************************************************************/
 struct city *player_find_city_by_id(const struct player *pplayer,
                                    int city_id)
@@ -374,6 +382,8 @@ struct city *player_find_city_by_id(cons
  If the specified player owns the unit with the specified id,
  return pointer to the unit struct.  Else return NULL.
  Uses fast idex_lookup_city.
+
+  pplayer may be NULL in which case all units will be considered.
 ***************************************************************/
 struct unit *player_find_unit_by_id(const struct player *pplayer,
                                    int unit_id)
@@ -501,6 +511,10 @@ Locate the city where the players palace
 **************************************************************************/
 struct city *find_palace(const struct player *pplayer)
 {
+  if (!pplayer) {
+    /* The client depends on this behavior in some places. */
+    return NULL;
+  }
   city_list_iterate(pplayer->cities, pcity) {
     if (is_capital(pcity)) {
       return pcity;
@@ -765,6 +779,9 @@ bool is_valid_username(const char *name)
 ****************************************************************************/
 struct player_research *get_player_research(const struct player *plr)
 {
-  assert((plr != NULL) && (plr->team != NULL));
+  if (!plr || !plr->team) {
+    /* Some client users depend on this behavior. */
+    return NULL;
+  }
   return &(plr->team->research);
 }
Index: common/player.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/player.h,v
retrieving revision 1.165
diff -p -u -r1.165 player.h
--- common/player.h     17 Sep 2005 15:50:10 -0000      1.165
+++ common/player.h     28 Sep 2005 18:36:16 -0000
@@ -29,7 +29,6 @@
 
 #define ANON_PLAYER_NAME "noname"
 #define ANON_USER_NAME  "Unassigned"
-#define OBSERVER_NAME  "Observer"
 
 /*
  * pplayer->ai.barbarian_type uses this enum. Note that the values
@@ -162,7 +161,6 @@ struct player {
   bool phase_done;
   int nturns_idle;
   bool is_alive;
-  bool is_observer; /* is the player a global observer */ 
   bool is_dying; /* set once the player is in the process of dying */
   bool surrendered; /* has indicated willingness to surrender */
 
@@ -182,7 +180,7 @@ struct player {
   struct player_spaceship spaceship;
   struct player_ai ai;
   bool was_created;                    /* if the player was /created */
-  bool is_connected;                  /* observers don't count */
+  bool is_connected;
   struct connection *current_conn;     /* non-null while handling packet */
   struct conn_list *connections;       /* will replace conn */
   struct worklist worklists[MAX_NUM_WORKLISTS];
Index: common/tech.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/tech.c,v
retrieving revision 1.100
diff -p -u -r1.100 tech.c
--- common/tech.c       2 Sep 2005 23:12:40 -0000       1.100
+++ common/tech.c       28 Sep 2005 18:36:17 -0000
@@ -52,6 +52,8 @@ static const char *flag_names[] = {
   Returns state of the tech for current pplayer.
   This can be: TECH_KNOW, TECH_UNKNOWN or TECH_REACHABLE
   Should be called with existing techs or A_FUTURE
+
+  If pplayer is NULL this simply returns TECH_KNOWN (used by the client).
 **************************************************************************/
 enum tech_state get_invention(const struct player *pplayer,
                              Tech_type_id tech)
@@ -59,7 +61,11 @@ enum tech_state get_invention(const stru
   assert(tech == A_FUTURE
          || (tech >= 0 && tech < game.control.num_tech_types));
 
-  return get_player_research(pplayer)->inventions[tech].state;
+  if (!pplayer) {
+    return TECH_KNOWN;
+  } else {
+    return get_player_research(pplayer)->inventions[tech].state;
+  }
 }
 
 /**************************************************************************
@@ -84,12 +90,18 @@ void set_invention(struct player *pplaye
 /**************************************************************************
   Returns if the given tech has to be researched to reach the
   goal. The goal itself isn't a requirement of itself.
+
+  pplayer may be NULL; however the function will always return FALSE in
+  that case.
 **************************************************************************/
 bool is_tech_a_req_for_goal(const struct player *pplayer, Tech_type_id tech,
                            Tech_type_id goal)
 {
   if (tech == goal) {
     return FALSE;
+  } else if (!pplayer) {
+    /* FIXME: We need a proper implementation here! */
+    return FALSE;
   } else {
     return
       BV_ISSET(get_player_research(pplayer)->inventions[goal].required_techs,
@@ -177,6 +189,9 @@ static void build_required_techs(struct 
 /**************************************************************************
   Returns TRUE iff the given tech is ever reachable by the given player
   by checking tech tree limitations.
+
+  pplayer may be NULL in which case a simplified result is returned
+  (used by the client).
 **************************************************************************/
 bool tech_is_available(const struct player *pplayer, Tech_type_id id)
 {
@@ -390,6 +405,9 @@ int total_bulbs_required(const struct pl
 
   At the end we multiply by the sciencebox value, as a percentage.  The
   cost can never be less than 1.
+
+  pplayer may be NULL in which case a simplified result is returned (used
+  by client and manual code).
 ****************************************************************************/
 int base_total_bulbs_required(const struct player *pplayer,
                              Tech_type_id tech)
@@ -397,7 +415,9 @@ int base_total_bulbs_required(const stru
   int tech_cost_style = game.info.tech_cost_style;
   double base_cost;
 
-  if (!is_future_tech(tech) && get_invention(pplayer, tech) == TECH_KNOWN) {
+  if (pplayer
+      && !is_future_tech(tech)
+      && get_invention(pplayer, tech) == TECH_KNOWN) {
     /* A non-future tech which is already known costs nothing. */
     return 0;
   }
@@ -414,8 +434,12 @@ int base_total_bulbs_required(const stru
 
   switch (tech_cost_style) {
   case 0:
-    base_cost = get_player_research(pplayer)->techs_researched 
-                * game.info.base_tech_cost;
+    if (pplayer) {
+      base_cost = get_player_research(pplayer)->techs_researched 
+       * game.info.base_tech_cost;
+    } else {
+      base_cost = 0;
+    }
     break;
   case 1:
     base_cost = techcoststyle1[tech];
@@ -447,7 +471,7 @@ int base_total_bulbs_required(const stru
       players_iterate(other) {
        players++;
        if (get_invention(other, tech) == TECH_KNOWN
-           && player_has_embassy(pplayer, other)) {
+           && pplayer && player_has_embassy(pplayer, other)) {
          players_with_tech_and_embassy++;
        }
       } players_iterate_end;
@@ -500,7 +524,7 @@ int base_total_bulbs_required(const stru
    * can also be adpoted to create an extra-hard AI skill level where the AI
    * gets science benefits */
 
-  if (pplayer->ai.control) {
+  if (pplayer && pplayer->ai.control) {
     assert(pplayer->ai.science_cost > 0);
     base_cost *= (double)pplayer->ai.science_cost / 100.0;
   }
@@ -514,10 +538,16 @@ int base_total_bulbs_required(const stru
  Returns the number of technologies the player need to research to get
  the goal technology. This includes the goal technology. Technologies
  are only counted once.
+
+  pplayer may be NULL; however the wrong value will be return in this case.
 **************************************************************************/
 int num_unknown_techs_for_goal(const struct player *pplayer,
                               Tech_type_id goal)
 {
+  if (!pplayer) {
+    /* FIXME: need an implementation for this! */
+    return 0;
+  }
   return get_player_research(pplayer)->inventions[goal].num_required_techs;
 }
 
@@ -525,10 +555,16 @@ int num_unknown_techs_for_goal(const str
  Function to determine cost (in bulbs) of reaching goal
  technology. These costs _include_ the cost for researching the goal
  technology itself.
+
+  pplayer may be NULL; however the wrong value will be return in this case.
 **************************************************************************/
 int total_bulbs_required_for_goal(const struct player *pplayer,
                                  Tech_type_id goal)
 {
+  if (!pplayer) {
+    /* FIXME: need an implementation for this! */
+    return 0;
+  }
   return get_player_research(pplayer)->inventions[goal].bulbs_required;
 }
 
@@ -583,14 +619,15 @@ bool is_future_tech(Tech_type_id tech)
 #include "specvec.h"
 
 /**************************************************************************
- Return the name of the given tech. You don't have to free the return
- pointer.
+  Return the name of the given tech. You don't have to free the return
+  pointer.
+
+  pplayer may be NULL.  In this case we won't know the "number" of a
+  future technology.
 **************************************************************************/
 const char *get_tech_name(const struct player *pplayer, Tech_type_id tech)
 {
-  static struct string_vector future;
   int i;
-  struct player_research *research;
 
   /* We don't return a static buffer because that would break anything that
    * needed to work with more than one name at a time. */
@@ -602,22 +639,27 @@ const char *get_tech_name(const struct p
     /* TRANS: "None" tech */
     return _("None");
   case A_FUTURE:
-    research = get_player_research(pplayer);
+    if (pplayer) {
+      struct player_research *research = get_player_research(pplayer);
+      static struct string_vector future;
+
+      /* pplayer->future_tech == 0 means "Future Tech. 1". */
+      for (i = future.size; i <= research->future_tech; i++) {
+       char *ptr = NULL;
 
-    /* pplayer->future_tech == 0 means "Future Tech. 1". */
-    for (i = future.size; i <= research->future_tech; i++) {
-      char *ptr = NULL;
-
-      string_vector_append(&future, &ptr);
-    }
-    if (!future.p[research->future_tech]) {
-      char buffer[1024];
+       string_vector_append(&future, &ptr);
+      }
+      if (!future.p[research->future_tech]) {
+       char buffer[1024];
 
-      my_snprintf(buffer, sizeof(buffer), _("Future Tech. %d"),
-                 research->future_tech + 1);
-      future.p[research->future_tech] = mystrdup(buffer);
+       my_snprintf(buffer, sizeof(buffer), _("Future Tech. %d"),
+                   research->future_tech + 1);
+       future.p[research->future_tech] = mystrdup(buffer);
+      }
+      return future.p[research->future_tech];
+    } else {
+      return _("Future Tech.");
     }
-    return future.p[research->future_tech];
   default:
     /* Includes A_NONE */
     if (!tech_exists(tech)) {
Index: data/civ1/nations.ruleset
===================================================================
RCS file: /home/freeciv/CVS/freeciv/data/civ1/nations.ruleset,v
retrieving revision 1.4
diff -p -u -r1.4 nations.ruleset
--- data/civ1/nations.ruleset   29 Aug 2004 19:03:32 -0000      1.4
+++ data/civ1/nations.ruleset   28 Sep 2005 18:36:17 -0000
@@ -61,8 +61,4 @@ options="1.9"
 *include "nation/english.ruleset"
 *include "nation/mongol.ruleset"
 
-;
-; observer and barbarians MUST go last in THIS order
-;
-*include "nation/observer.ruleset"
 *include "nation/barbarian.ruleset"
Index: data/default/nations.ruleset
===================================================================
RCS file: /home/freeciv/CVS/freeciv/data/default/nations.ruleset,v
retrieving revision 1.83
diff -p -u -r1.83 nations.ruleset
--- data/default/nations.ruleset        13 Sep 2005 00:59:25 -0000      1.83
+++ data/default/nations.ruleset        28 Sep 2005 18:36:17 -0000
@@ -160,8 +160,4 @@ match=2
 *include "nation/tibetan.ruleset"
 *include "nation/tunisian.ruleset"
 *include "nation/uyghur.ruleset"
-;
-; observer and barbarians MUST go last in THIS order
-;
-*include "nation/observer.ruleset"
 *include "nation/barbarian.ruleset"
Index: data/nation/Makefile.am
===================================================================
RCS file: /home/freeciv/CVS/freeciv/data/nation/Makefile.am,v
retrieving revision 1.25
diff -p -u -r1.25 Makefile.am
--- data/nation/Makefile.am     13 Sep 2005 00:59:25 -0000      1.25
+++ data/nation/Makefile.am     28 Sep 2005 18:36:17 -0000
@@ -78,7 +78,6 @@ pkgdata_DATA = \
                mordor.ruleset \
                newzealand.ruleset \
                nigerian.ruleset \
-               observer.ruleset \
                persian.ruleset \
                phoenician.ruleset \
                polish.ruleset \
Index: server/gamehand.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/gamehand.c,v
retrieving revision 1.169
diff -p -u -r1.169 gamehand.c
--- server/gamehand.c   5 Sep 2005 15:55:46 -0000       1.169
+++ server/gamehand.c   28 Sep 2005 18:36:18 -0000
@@ -239,11 +239,6 @@ void init_new_game(void)
     struct start_position pos
       = map.start_positions[start_pos[pplayer->player_no]];
 
-    /* don't give any units to observer */
-    if (pplayer->is_observer) {
-      continue;
-    }
-
     /* Place the first unit. */
     place_starting_unit(pos.tile, pplayer, game.info.start_units[0]);
   } players_iterate_end;
@@ -256,11 +251,6 @@ void init_new_game(void)
     struct start_position p
       = map.start_positions[start_pos[pplayer->player_no]];
 
-    /* don't give any units to observer */
-    if (pplayer->is_observer) {
-      continue;
-    }
-
     /* Place global start units */
     for (i = 1; i < strlen(game.info.start_units); i++) {
       ptile = find_dispersed_position(pplayer, &p);
Index: server/plrhand.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/plrhand.c,v
retrieving revision 1.425
diff -p -u -r1.425 plrhand.c
--- server/plrhand.c    21 Sep 2005 06:45:25 -0000      1.425
+++ server/plrhand.c    28 Sep 2005 18:36:18 -0000
@@ -844,7 +844,7 @@ void send_player_info_c(struct player *s
       package_player_common(pplayer, &info);
 
       conn_list_iterate(dest, pconn) {
-       if (pconn->player && pconn->player->is_observer) {
+       if (!pconn->player && pconn->observer) {
          /* Global observer. */
          package_player_info(pplayer, &info, pconn->player, INFO_FULL);
        } else if (pconn->player) {
@@ -885,7 +885,6 @@ static void package_player_common(struct
   sz_strlcpy(packet->username, plr->username);
   packet->nation = plr->nation ? plr->nation->index : -1;
   packet->is_male=plr->is_male;
-  packet->is_observer=plr->is_observer;
   packet->team = plr->team ? plr->team->index : -1;
   packet->is_ready = plr->is_ready;
   packet->city_style=plr->city_style;
@@ -1426,102 +1425,6 @@ struct nation_type *pick_available_natio
 }
 
 /****************************************************************************
-  Return an available observer nation.  This simply returns the first
-  such nation.  If no nation is available NO_NATION_SELECTED is returned.
-****************************************************************************/
-static struct nation_type *pick_observer_nation(void)
-{
-  nations_iterate(pnation) {
-    if (is_nation_observer(pnation) && !pnation->player) {
-      return pnation;
-    }
-  } nations_iterate_end;
-
-  return NO_NATION_SELECTED;
-}
-
-/****************************************************************************
-  Create a player with is_observer = TRUE and return it.
-  If a global observer has already been created, return that player.
-  If there are no player slots available return NULL.
-
-  (Could be called ensure_global_observer instead.)
-****************************************************************************/
-struct player *create_global_observer(void)
-{
-  struct player *pplayer = NULL;
-  struct nation_type *nation;
-
-  /* Check if a global observer already exists. If so, return it.  Note the
-   * observer may exist at any position in the array. */
-  players_iterate(aplayer) {
-    if (aplayer->is_observer) {
-      return aplayer;
-    }
-  } players_iterate_end
-
-  /* If we're here we couldn't find an observer, so check if we have
-   * a slot available to create one.  Observers are taken from the slots of
-   * normal civs (barbarians are reserved separately). */
-  if (game.info.nplayers - game.info.nbarbarians >= MAX_NUM_PLAYERS) {
-    notify_conn(NULL, NULL, E_CONNECTION,
-               _("A global observer cannot be created: too "
-                 "many regular players."));
-    return NULL;
-  }
-
-  nation = pick_observer_nation();
-  if (nation == NO_NATION_SELECTED) {
-    notify_conn(NULL, NULL, E_CONNECTION,
-               _("A global observer cannot be created: there's "
-                 "no observer nation in the ruleset."));
-    return NULL;
-  }
-
-  /* alright, we can create an observer. go for it. */
-  pplayer = &game.players[game.info.nplayers];
-
-  /* only allocate a player map is the game is running or a game is loaded
-   * in pregame. This is because a game map might not be created otherwise.
-   *
-   * FIXME: could we use map_is_empty here? */
-  server_player_init(pplayer,
-                     (server_state == RUN_GAME_STATE || 
!game.info.is_new_game),
-                    TRUE);
-
-  sz_strlcpy(pplayer->name, OBSERVER_NAME);
-  sz_strlcpy(pplayer->username, ANON_USER_NAME);
-  pplayer->is_connected = FALSE;
-  pplayer->is_observer = TRUE;
-  pplayer->capital = TRUE;     /* is this necessary? maybe for client... */
-  pplayer->phase_done = TRUE;
-  BV_CLR_ALL(pplayer->embassy);                /* no embassies */
-  pplayer->is_alive = FALSE;
-  pplayer->was_created = FALSE;        /* doesn't really matter */
-
-  /* don't do this otherwise, because a game map might
-   * not have been created.
-   *
-   * FIXME: could we use map_is_empty here? */
-  if (server_state == RUN_GAME_STATE || !game.info.is_new_game) {
-    pplayer->nation = nation;
-    init_tech(pplayer);
-    give_initial_techs(pplayer);
-    map_know_and_see_all(pplayer);
-  }
-
-  game.info.nplayers++;
-
-  /* tell everyone that game.info.nplayers has been updated */
-  send_game_info(NULL);
-  send_player_info(pplayer, NULL);
-  notify_conn(NULL, NULL, E_CONNECTION,
-             _("A global observer has been created"));
-
-  return pplayer;
-}
-
-/****************************************************************************
   Called when something is changed; this resets everyone's readiness.
 ****************************************************************************/
 void reset_all_start_commands(void)
Index: server/plrhand.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/plrhand.h,v
retrieving revision 1.87
diff -p -u -r1.87 plrhand.h
--- server/plrhand.h    21 Sep 2005 06:45:25 -0000      1.87
+++ server/plrhand.h    28 Sep 2005 18:36:18 -0000
@@ -74,7 +74,6 @@ void send_player_turn_notifications(stru
 void shuffle_players(void);
 void set_shuffled_players(int *shuffled_players);
 struct player *shuffled_player(int i);
-struct player *create_global_observer(void);
 void reset_all_start_commands(void);
 
 #define shuffled_players_iterate(pplayer)                                   \
Index: server/report.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/report.c,v
retrieving revision 1.71
diff -p -u -r1.71 report.c
--- server/report.c     5 Sep 2005 15:55:47 -0000       1.71
+++ server/report.c     28 Sep 2005 18:36:19 -0000
@@ -97,7 +97,7 @@ static const char *science_to_text(int v
 static const char *mil_service_to_text(int value);
 static const char *pollution_to_text(int value);
 
-#define GOOD_PLAYER(p) ((p)->is_alive && !is_barbarian(p) && !(p)->is_observer)
+#define GOOD_PLAYER(p) ((p)->is_alive && !is_barbarian(p))
 
 /*
  * Describes a row.
Index: server/ruleset.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/ruleset.c,v
retrieving revision 1.285
diff -p -u -r1.285 ruleset.c
--- server/ruleset.c    21 Sep 2005 06:45:25 -0000      1.285
+++ server/ruleset.c    28 Sep 2005 18:36:19 -0000
@@ -2137,8 +2137,6 @@ static void load_ruleset_nations(struct 
 
     pl->is_playable = secfile_lookup_bool_default(file, TRUE,
                                                  "%s.is_playable", sec[i]);
-    pl->is_observer = secfile_lookup_bool_default(file, FALSE,
-                                                 "%s.is_observer", sec[i]);
     pl->is_barbarian = secfile_lookup_bool_default(file, FALSE,
                                                  "%s.is_barbarian", sec[i]);
 
@@ -2969,7 +2967,6 @@ void send_ruleset_nations(struct conn_li
     packet.city_style = n->city_style;
     packet.is_playable = n->is_playable;
     packet.is_available = n->is_available;
-    packet.is_observer = n->is_observer;
     packet.is_barbarian = n->is_barbarian;
     memcpy(packet.init_techs, n->init_techs, sizeof(packet.init_techs));
     memcpy(packet.init_buildings, n->init_buildings, 
Index: server/savegame.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/savegame.c,v
retrieving revision 1.278
diff -p -u -r1.278 savegame.c
--- server/savegame.c   26 Sep 2005 18:59:12 -0000      1.278
+++ server/savegame.c   28 Sep 2005 18:36:20 -0000
@@ -1762,8 +1762,7 @@ static void player_load(struct player *p
   plr->nturns_idle=0;
   plr->is_male=secfile_lookup_bool_default(file, TRUE, "player%d.is_male", 
plrno);
   plr->is_alive=secfile_lookup_bool(file, "player%d.is_alive", plrno);
-  plr->is_observer=secfile_lookup_bool_default(file, FALSE, 
-                                               "player%d.is_observer", plrno);
+  /* "Old" observer players will still be loaded but are considered dead. */
   plr->ai.control = secfile_lookup_bool(file, "player%d.ai.control", plrno);
   for (i = 0; i < MAX_NUM_PLAYERS; i++) {
     plr->ai.love[i]
@@ -2513,7 +2512,6 @@ static void player_save(struct player *p
 
   secfile_insert_bool(file, plr->is_male, "player%d.is_male", plrno);
   secfile_insert_bool(file, plr->is_alive, "player%d.is_alive", plrno);
-  secfile_insert_bool(file, plr->is_observer, "player%d.is_observer", plrno);
   secfile_insert_bool(file, plr->ai.control, "player%d.ai.control", plrno);
   for (i = 0; i < MAX_NUM_PLAYERS; i++) {
     secfile_insert_int(file, plr->ai.love[i],
Index: server/score.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/score.c,v
retrieving revision 1.24
diff -p -u -r1.24 score.c
--- server/score.c      4 Jul 2005 17:48:38 -0000       1.24
+++ server/score.c      28 Sep 2005 18:36:20 -0000
@@ -392,7 +392,7 @@ void calc_civ_score(struct player *pplay
   pplayer->score.literacy = 0;
   pplayer->score.spaceship = 0;
 
-  if (is_barbarian(pplayer) || pplayer->is_observer) {
+  if (is_barbarian(pplayer)) {
     return;
   }
 
Index: server/srv_main.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/srv_main.c,v
retrieving revision 1.294
diff -p -u -r1.294 srv_main.c
--- server/srv_main.c   21 Sep 2005 06:45:25 -0000      1.294
+++ server/srv_main.c   28 Sep 2005 18:36:21 -0000
@@ -204,7 +204,7 @@ void srv_init(void)
 **************************************************************************/
 bool is_game_over(void)
 {
-  int barbs = 0, alive = 0, observers = 0;
+  int barbs = 0, alive = 0;
   bool all_allied;
   struct player *victor = NULL;
 
@@ -222,9 +222,6 @@ bool is_game_over(void)
     if (is_barbarian(pplayer)) {
       barbs++;
     }
-    if (pplayer->is_observer) {
-      observers++;
-    }
   } players_iterate_end;
 
   /* count the living */
@@ -238,7 +235,7 @@ bool is_game_over(void)
   } players_iterate_end;
 
   /* the game does not quit if we are playing solo */
-  if (game.info.nplayers == (observers + barbs + 1)
+  if (game.info.nplayers == (barbs + 1)
       && alive >= 1) {
     return FALSE;
   }
@@ -1336,8 +1333,7 @@ void handle_player_ready(struct player *
 ****************************************************************************/
 void aifill(int amount)
 {
-  int observers = 0, remove;
-  int filled = 0;
+  int remove, filled = 0;
 
   if (server_state != PRE_GAME_STATE || !game.info.is_new_game) {
     return;
@@ -1350,18 +1346,7 @@ void aifill(int amount)
 
   amount = MIN(amount, game.info.max_players);
 
-  /* we don't want aifill to count global observers unless 
-   * aifill = MAX_NUM_PLAYERS */
-  players_iterate(pplayer) {
-    if (pplayer->is_observer) {
-      observers++;
-    }
-  } players_iterate_end;
-  if (amount == game.info.max_players) {
-    observers = 0;
-  }
-
-  while (game.info.nplayers < amount + observers) {
+  while (game.info.nplayers < amount) {
     const int old_nplayers = game.info.nplayers;
     struct player *pplayer = get_player(old_nplayers);
     char player_name[ARRAY_SIZE(pplayer->name)];
@@ -1394,11 +1379,10 @@ void aifill(int amount)
   }
 
   remove = game.info.nplayers - 1;
-  while (game.info.nplayers > amount + observers && remove >= 0) {
+  while (game.info.nplayers > amount && remove >= 0) {
     struct player *pplayer = get_player(remove);
 
-    if (!pplayer->is_observer && !pplayer->is_connected
-       && !pplayer->was_created) {
+    if (!pplayer->is_connected && !pplayer->was_created) {
       server_remove_player(pplayer);
     }
     remove--;
@@ -1890,13 +1874,6 @@ static void srv_loop(void)
   
   if(game.info.is_new_game) {
     init_new_game();
-
-    /* give global observers the entire map */
-    players_iterate(pplayer) {
-      if (pplayer->is_observer) {
-        map_know_and_see_all(pplayer);
-      }
-    } players_iterate_end;
   }
 
   send_game_state(game.est_connections, CLIENT_GAME_RUNNING_STATE);
Index: server/stdinhand.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/stdinhand.c,v
retrieving revision 1.439
diff -p -u -r1.439 stdinhand.c
--- server/stdinhand.c  26 Sep 2005 18:59:12 -0000      1.439
+++ server/stdinhand.c  28 Sep 2005 18:36:21 -0000
@@ -142,7 +142,8 @@ static PlayerNameStatus test_player_name
       return PNameTooLong;
   } else if (mystrcasecmp(name, ANON_PLAYER_NAME) == 0) {
       return PNameIllegal;
-  } else if (mystrcasecmp(name, OBSERVER_NAME) == 0) {
+  } else if (mystrcasecmp(name, "Observer") == 0) {
+    /* "Observer" used to be illegal and we keep it that way for now. */
       return PNameIllegal;
   }
 
@@ -833,23 +834,6 @@ static bool toggle_ai_player(struct conn
   return TRUE;
 }
 
-/****************************************************************************
-  Return the number of non-observer players.  game.info.nplayers includes
-  observers so in some places this function should be called instead.
-****************************************************************************/
-static int get_num_nonobserver_players(void)
-{
-  int nplayers = 0;
-
-  players_iterate(pplayer) {
-    if (!pplayer->is_observer) {
-      nplayers++;
-    }
-  } players_iterate_end;
-
-  return nplayers;
-}
-
 /**************************************************************************
 ...
 **************************************************************************/
@@ -868,13 +852,11 @@ static bool create_ai_player(struct conn
 
   /* game.info.max_players is a limit on the number of non-observer players.
    * MAX_NUM_PLAYERS is a limit on all players. */
-  if (get_num_nonobserver_players() >= game.info.max_players
-      || game.info.nplayers >= MAX_NUM_PLAYERS) {
+  if (game.info.nplayers >= MAX_NUM_PLAYERS) {
     /* Try emptying a slot if there is an ai player
      * created through the /aifill command */
     players_iterate(eplayer) {
-      if (eplayer->is_observer || eplayer->is_connected
-         || eplayer->was_created) {
+      if (eplayer->is_connected || eplayer->was_created) {
         continue;
       }     
       ai_player_should_be_removed = TRUE;
@@ -924,8 +906,7 @@ static bool create_ai_player(struct conn
   
   if (ai_player_should_be_removed) {
     players_iterate(eplayer) {
-      if (eplayer->is_observer || eplayer->is_connected
-         || eplayer->was_created) {
+      if (eplayer->is_connected || eplayer->was_created) {
         continue;
       }
       server_remove_player(eplayer);
@@ -2135,7 +2116,7 @@ static bool vote_command(struct connecti
   if (caller == NULL || caller->player == NULL) {
     cmd_reply(CMD_VOTE, caller, C_FAIL, _("This command is client only."));
     return FALSE;
-  } else if (caller->player->is_observer || caller->observer) {
+  } else if (caller->observer) {
     cmd_reply(CMD_VOTE, caller, C_FAIL, _("Observers cannot vote."));
     return FALSE;
   } else if (server_state != RUN_GAME_STATE) {
@@ -2572,7 +2553,8 @@ static bool is_allowed_to_take(struct pl
 {
   const char *allow;
 
-  if (pplayer->is_observer) {
+  if (!pplayer) {
+    /* Observer */
     if (!(allow = strchr(game.allow_take, (game.info.is_new_game ? 'O' : 
'o')))) {
       if (will_obs) {
         mystrlcpy(msg, _("Sorry, one can't observe globally in this game."),
@@ -2716,11 +2698,6 @@ static bool observe_command(struct conne
   }
 
   /* if we have no pplayer, it means that we want to be a global observer */
-  if (!pplayer) {
-    if (!(pplayer = create_global_observer())) {
-      goto end; /* couldn't create an observer */ 
-    }
-  }
 
   /******** PART II: do the observing ********/
 
@@ -2731,8 +2708,11 @@ static bool observe_command(struct conne
   }
 
   /* observing your own player (during pregame) makes no sense. */
-  if (pconn->player == pplayer && !pconn->observer
-      && is_newgame && !pplayer->was_created) {
+  if (pplayer
+      && pconn->player == pplayer
+      && !pconn->observer
+      && is_newgame
+      && !pplayer->was_created) {
     cmd_reply(CMD_OBSERVE, caller, C_FAIL, 
               _("%s already controls %s. Using 'observe' would remove %s"),
               pconn->username, pplayer->name, pplayer->name);
@@ -2741,9 +2721,15 @@ static bool observe_command(struct conne
 
   /* attempting to observe a player you're already observing should fail. */
   if (pconn->player == pplayer && pconn->observer) {
-    cmd_reply(CMD_OBSERVE, caller, C_FAIL,
-              _("%s is already observing %s."),  
-              pconn->username, pplayer->name);
+    if (pplayer) {
+      cmd_reply(CMD_OBSERVE, caller, C_FAIL,
+               _("%s is already observing %s."),  
+               pconn->username, pplayer->name);
+    } else {
+      cmd_reply(CMD_OBSERVE, caller, C_FAIL,
+               _("%s is already observing."),  
+               pconn->username);
+    }
     goto end;
   }
 
@@ -2754,16 +2740,20 @@ static bool observe_command(struct conne
 
   /* if the connection is already attached to a player,
    * unattach and cleanup old player (rename, remove, etc) */
-  if (pconn->player) {
+  if (TRUE) {
     char name[MAX_LEN_NAME];
 
-    /* if a pconn->player is removed, we'll lose pplayer */
-    sz_strlcpy(name, pplayer->name);
-  
+    if (pplayer) {
+      /* if a pconn->player is removed, we'll lose pplayer */
+      sz_strlcpy(name, pplayer->name);
+    }
+
     detach_command(NULL, pconn->username, FALSE);
 
-    /* find pplayer again, the pointer might have been changed */
-    pplayer = find_player_by_name(name);
+    if (pplayer) {
+      /* find pplayer again, the pointer might have been changed */
+      pplayer = find_player_by_name(name);
+    }
   } 
 
   /* we don't want the connection's username on another player */
@@ -2775,7 +2765,11 @@ static bool observe_command(struct conne
 
   /* attach pconn to new player as an observer */
   pconn->observer = TRUE; /* do this before attach! */
-  attach_connection_to_player(pconn, pplayer);
+  if (pplayer) {
+    attach_connection_to_player(pconn, pplayer);
+  } else {
+    unattach_connection_from_player(pconn);
+  }
   send_conn_info(pconn->self, game.est_connections);
 
   if (server_state == RUN_GAME_STATE) {
@@ -2788,8 +2782,13 @@ static bool observe_command(struct conne
     dsend_packet_start_phase(pconn, game.info.phase);
   }
 
-  cmd_reply(CMD_OBSERVE, caller, C_OK, _("%s now observes %s"),
-            pconn->username, pplayer->name);
+  if (pplayer) {
+    cmd_reply(CMD_OBSERVE, caller, C_OK, _("%s now observes %s"),
+             pconn->username, pplayer->name);
+  } else {
+    cmd_reply(CMD_OBSERVE, caller, C_OK, _("%s now observes"),
+             pconn->username);
+  }
 
   end:;
   /* free our args */
@@ -2867,13 +2866,6 @@ static bool take_command(struct connecti
     goto end;
   }
 
-  /* you may not take over a global observer */
-  if (pplayer->is_observer) {
-    cmd_reply(CMD_TAKE, caller, C_FAIL, _("%s cannot be taken."),
-              pplayer->name);
-    goto end;
-  } 
-
   /* taking your own player makes no sense. */
   if (pconn->player == pplayer && !pconn->observer) {
     cmd_reply(CMD_TAKE, caller, C_FAIL, _("%s already controls %s"),
@@ -2978,6 +2970,9 @@ static bool take_command(struct connecti
 /**************************************************************************
   Detach from a player. if that player wasn't /created and you were 
   controlling the player, remove it (and then detach any observers as well).
+
+  If called for a global observer connection (where pconn->player is NULL)
+  then it will correctly detach from observing mode.
 **************************************************************************/
 static bool detach_command(struct connection *caller, char *str, bool check)
 {
@@ -2988,7 +2983,7 @@ static bool detach_command(struct connec
   struct connection *pconn = NULL;
   struct player *pplayer = NULL;
   bool is_newgame = server_state == PRE_GAME_STATE && game.info.is_new_game;
-  bool one_obs_among_many = FALSE, res = FALSE;
+  bool res = FALSE;
 
   sz_strlcpy(buf, str);
   ntokens = get_tokens(buf, arg, 1, TOKEN_DELIMITERS);
@@ -3022,18 +3017,12 @@ static bool detach_command(struct connec
   pplayer = pconn->player;
 
   /* must have someone to detach from... */
-  if (!pplayer) {
+  if (!pplayer && !pconn->observer) {
     cmd_reply(CMD_DETACH, caller, C_FAIL, 
               _("%s is not attached to any player."), pconn->username);
     goto end;
   }
 
-  /* a special case for global observers: we don't want to remove the
-   * observer player in pregame if someone else is also observing it */
-  if (pplayer->is_observer && conn_list_size(pplayer->connections) > 1) {
-    one_obs_among_many = TRUE;
-  }
-
   res = TRUE;
   if (check) {
     goto end;
@@ -3050,16 +3039,23 @@ static bool detach_command(struct connec
   }
 
   /* actually do the detaching */
-  unattach_connection_from_player(pconn);
+  if (pplayer) {
+    unattach_connection_from_player(pconn);
+    cmd_reply(CMD_DETACH, caller, C_COMMENT,
+             _("%s detaching from %s"), pconn->username, pplayer->name);
+  } else {
+    pconn->observer = FALSE;
+    cmd_reply(CMD_DETACH, caller, C_COMMENT,
+             _("%s no longer observing."), pconn->username);
+  }
   send_conn_info(pconn->self, game.est_connections);
-  cmd_reply(CMD_DETACH, caller, C_COMMENT,
-            _("%s detaching from %s"), pconn->username, pplayer->name);
 
   /* only remove the player if the game is new and in pregame, 
    * the player wasn't /created, and no one is controlling it 
    * and we were observing but no one else was... */
-  if (!pplayer->is_connected && !pplayer->was_created && is_newgame
-      && !one_obs_among_many) {
+  if (pplayer
+      && !pplayer->is_connected
+      && !pplayer->was_created && is_newgame) {
     /* detach any observers */
     conn_list_iterate(pplayer->connections, aconn) {
       if (aconn->observer) {
@@ -3076,7 +3072,7 @@ static bool detach_command(struct connec
     reset_all_start_commands();
   }
 
-  if (!pplayer->is_connected) {
+  if (pplayer && !pplayer->is_connected) {
     /* aitoggle the player if no longer connected. */
     if (game.info.auto_ai_toggle && !pplayer->ai.control) {
       toggle_ai_player_direct(NULL, pplayer);
@@ -3369,7 +3365,6 @@ bool handle_stdin_input(struct connectio
   if (caller 
       && caller->player
       && !check
-      && !caller->player->is_observer
       && caller->access_level == ALLOW_INFO
       && commands[cmd].level == ALLOW_CTRL) {
     int idx = caller->player->player_no;
@@ -3602,10 +3597,10 @@ static bool start_command(struct connect
        game.info.max_players = map.num_start_positions;
       }
 
-      if (get_num_nonobserver_players() > game.info.max_players) {
+      if (game.info.nplayers > game.info.max_players) {
        /* Because of the way player ids are renumbered during
           server_remove_player() this is correct */
-        while (get_num_nonobserver_players() > game.info.max_players) {
+        while (game.info.nplayers > game.info.max_players) {
          /* This may erronously remove observer players sometimes.  This
           * is a bug but non-fatal. */
          server_remove_player(get_player(game.info.max_players));
@@ -3620,7 +3615,7 @@ static bool start_command(struct connect
     }
 
     /* check min_players */
-    if (get_num_nonobserver_players() < game.info.min_players) {
+    if (game.info.nplayers < game.info.min_players) {
       cmd_reply(CMD_START_GAME, caller, C_FAIL,
                _("Not enough players, game will not start."));
       return FALSE;

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