Complete.Org: Mailing Lists: Archives: freeciv-dev: October 2005:
[Freeciv-Dev] (PR#14316) rewrite vision interface
Home

[Freeciv-Dev] (PR#14316) rewrite vision interface

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
Subject: [Freeciv-Dev] (PR#14316) rewrite vision interface
From: "Jason Short" <jdorje@xxxxxxxxxxxxxxxxxxxxx>
Date: Thu, 13 Oct 2005 23:18:45 -0700
Reply-to: bugs@xxxxxxxxxxx

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

This patch rewrites the vision interface.  I'd bet my left pinky finger 
against a pint of Grimbergen that the new system is both more readible 
and more robust.

1.  The vision interface is rewritten.  Instead of map_refog_circle, 
remove_unit_sight_points, map_(un)fog_city_area there is a vision 
structure.  The users are simpler because the vision structure does more 
and the callers don't have to know about the internals.

2.  All watchtower ugliness is removed.  In its place we have a new 
function get_unit_vision_at().  Unit watchtower vision is no longer 
dependent on transported_by (in the old code some bits assumed 
transported units had their base vision range).

3.  Most high-level server code can just use city_refresh_vision and 
unit_refresh_vision.  These functions have small overhead and can be 
called often - any time an effect might change vision range.  However 
NOT calling them isn't a major bug; it just means the vision update will 
be delayed until the next call.  This is in stark contrast to the 
current system where a missed update results in out-of-synch fog that 
won't be fixed until you save-and-reload.

There is extensive explanation in maphand.h, which is recommended 
reading for everyone.

-jason

Index: server/srv_main.c
===================================================================
--- server/srv_main.c   (revision 11127)
+++ server/srv_main.c   (working copy)
@@ -1902,6 +1902,16 @@
 void server_game_free()
 {
   players_iterate(pplayer) {
+    unit_list_iterate(pplayer->units, punit) {
+      vision_free(punit->server.vision);
+      punit->server.vision = NULL;
+    } unit_list_iterate_end;
+
+    city_list_iterate(pplayer->cities, pcity) {
+      vision_free(pcity->server.vision);
+      pcity->server.vision = NULL;
+    } city_list_iterate_end;
+
     player_map_free(pplayer);
   } players_iterate_end;
   game_free();
Index: server/citytools.c
===================================================================
--- server/citytools.c  (revision 11127)
+++ server/citytools.c  (working copy)
@@ -766,6 +766,7 @@
   int old_trade_routes[NUM_TRADEROUTES];
   bv_imprs had_small_wonders;
   char old_city_name[MAX_LEN_NAME];
+  struct vision *old_vision;
 
   assert(pgiver != ptaker);
 
@@ -795,8 +796,9 @@
   } built_impr_iterate_end;
 
   give_citymap_from_player_to_player(pcity, pgiver, ptaker);
-  map_refog_circle(ptaker, pcity->tile,
-                  -1, pcity->server.vision_radius_sq, TRUE);
+  old_vision = pcity->server.vision;
+  pcity->server.vision = vision_new(ptaker, pcity->tile, FALSE);
+  vision_change_sight(pcity->server.vision, vision_get_sight(old_vision));
 
   sz_strlcpy(old_city_name, pcity->name);
   if (game.info.allowed_city_names == 1
@@ -904,15 +906,18 @@
     update_tile_knowledge(pcity->tile);
   }
 
-  map_refog_circle(pgiver, pcity->tile,
-                  pcity->server.vision_radius_sq, -1, TRUE);
-
   /* Build a new palace for free if the player lost her capital and
      savepalace is on. */
   if (game.info.savepalace) {
     build_free_small_wonders(pgiver, pcity->name, &had_small_wonders);
   }
 
+  /* Remove the sight points from the giver...and refresh the city's
+   * vision range, since it might be different under the new owner. */
+  vision_clear_sight(old_vision);
+  vision_free(old_vision);
+  city_refresh_vision(pcity);
+
   sanity_check_city(pcity);
   sync_cities();
 }
@@ -967,7 +972,8 @@
   }
 
   /* Before arranging workers to show unknown land */
-  map_unfog_city_area(pcity);
+  pcity->server.vision = vision_new(pplayer, ptile, FALSE);
+  city_refresh_vision(pcity);
 
   tile_set_city(ptile, pcity);
 
@@ -1004,19 +1010,10 @@
 
   /* Put vision back to normal, if fortress acted as a watchtower */
   if (tile_has_special(ptile, S_FORTRESS)) {
-    /* This could be a helper function. */
-    unit_list_iterate((ptile)->units, punit) {
-      struct player *owner = unit_owner(punit);
+    tile_clear_special(ptile, S_FORTRESS);
+    unit_list_refresh_vision(ptile->units);
+  }
 
-      if (player_knows_techs_with_flag(owner, TF_WATCHTOWER)) {
-       map_refog_circle(owner, punit->tile,
-                        get_watchtower_vision(punit),
-                        punit->type->vision_radius_sq,
-                        FALSE);
-      }
-    } unit_list_iterate_end;
-  }
-  tile_clear_special(ptile, S_FORTRESS);
   update_tile_knowledge(ptile);
 
   pcity->synced = FALSE;
@@ -1057,7 +1054,7 @@
   struct tile *ptile = pcity->tile;
   bv_imprs had_small_wonders;
   char *city_name = mystrdup(pcity->name);
-  int old_vision_range;
+  struct vision *old_vision;
 
   BV_CLR_ALL(had_small_wonders);
   built_impr_iterate(pcity, i) {
@@ -1143,8 +1140,8 @@
      alive in the client. As the number of removed cities is small the leak is
      acceptable. */
 
-  old_vision_range = pcity->server.vision_radius_sq;
-  pcity->server.vision_radius_sq = -1;
+  old_vision = pcity->server.vision;
+  pcity->server.vision = NULL;
   game_remove_city(pcity);
   map_update_borders_city_destroyed(ptile);
 
@@ -1154,7 +1151,8 @@
     }
   } players_iterate_end;
 
-  map_refog_circle(pplayer, ptile, old_vision_range, -1, TRUE);
+  vision_clear_sight(old_vision);
+  vision_free(old_vision);
 
   /* Update available tiles in adjacent cities. */
   map_city_radius_iterate(ptile, tile1) {
@@ -1810,7 +1808,7 @@
 
   /* Re-update the city's visible area.  This updates fog if the vision
    * range increases or decreases. */
-  map_unfog_city_area(pcity);
+  city_refresh_vision(pcity);
 }
 
 /**************************************************************************
@@ -2125,3 +2123,15 @@
     }
   } adjc_iterate_end;
 }
+
+/****************************************************************************
+  Refresh the city's vision.
+
+  This function has very small overhead and can be called any time effects
+  may have changed the vision range of the city.
+****************************************************************************/
+void city_refresh_vision(struct city *pcity)
+{
+  vision_change_sight(pcity->server.vision,
+                     get_city_bonus(pcity, EFT_CITY_VISION_RADIUS_SQ));
+}
Index: server/citytools.h
===================================================================
--- server/citytools.h  (revision 11127)
+++ server/citytools.h  (working copy)
@@ -89,5 +89,6 @@
 bool can_place_worker_here(struct city *pcity, int city_x, int city_y);
 void check_city_workers(struct player *pplayer);
 void city_landlocked_sell_coastal_improvements(struct tile *ptile);
+void city_refresh_vision(struct city *pcity);
 
 #endif  /* FC__CITYTOOLS_H */
Index: server/cityturn.c
===================================================================
--- server/cityturn.c   (revision 11127)
+++ server/cityturn.c   (working copy)
@@ -1123,7 +1123,7 @@
 
     /* Call this function since some buildings may change the
      * the vision range of a city */
-    map_unfog_city_area(pcity);
+    city_refresh_vision(pcity);
 
     if ((mod = get_current_construction_bonus(pcity, EFT_GIVE_IMM_TECH))) {
       int i;
Index: server/maphand.c
===================================================================
--- server/maphand.c    (revision 11127)
+++ server/maphand.c    (working copy)
@@ -743,43 +743,6 @@
 }
 
 /**************************************************************************
-  Changes the vision range of the city.  The new range is given in
-  the radius_sq.
-**************************************************************************/
-static void map_refog_city_area(struct city *pcity, int new_radius_sq)
-{
-  if (pcity) {
-    map_refog_circle(city_owner(pcity), pcity->tile,
-                    pcity->server.vision_radius_sq, new_radius_sq, TRUE);
-    pcity->server.vision_radius_sq = new_radius_sq;
-  } else {
-    freelog(LOG_ERROR, "Attempting to change fog for non-existent city");
-  }
-}
-
-/**************************************************************************
-  Fogs the area visible by the city.  Unlike other unfog functions this
-  one may be called multiple times in a row without any penalty.  Call it
-  before destroying the city (or go straight to the source and use
-  map_refog_circle by hand).
-**************************************************************************/
-void map_fog_city_area(struct city *pcity)
-{
-  map_refog_city_area(pcity, -1);
-}
-
-/**************************************************************************
-  Unfogs the area visible by the city.  Unlike other unfog functions this
-  one may be called multiple times in a row without any penalty.  Basically
-  it should be called any time the city's vision range may have changed.
-**************************************************************************/
-void map_unfog_city_area(struct city *pcity)
-{
-  map_refog_city_area(pcity,
-                     get_city_bonus(pcity, EFT_CITY_VISION_RADIUS_SQ));
-}
-
-/**************************************************************************
 ...
 **************************************************************************/
 static void shared_vision_change_seen(struct tile *ptile, struct player 
*pplayer, int change)
@@ -796,8 +759,9 @@
 /**************************************************************************
 There doesn't have to be a city.
 **************************************************************************/
-void map_refog_circle(struct player *pplayer, struct tile *ptile,
-                     int old_radius_sq, int new_radius_sq, bool pseudo)
+static void map_refog_circle(struct player *pplayer, struct tile *ptile,
+                            int old_radius_sq, int new_radius_sq,
+                            bool can_reveal_tiles)
 {
   if (old_radius_sq != new_radius_sq) {
     int max_radius = MAX(old_radius_sq, new_radius_sq);
@@ -808,13 +772,13 @@
     buffer_shared_vision(pplayer);
     circle_dxyr_iterate(ptile, max_radius, tile1, dx, dy, dr) {
       if (dr > old_radius_sq && dr <= new_radius_sq) {
-       if (!pseudo || map_is_known(tile1, pplayer)) {
+       if (can_reveal_tiles || map_is_known(tile1, pplayer)) {
          map_unfog_tile(pplayer, tile1);
        } else {
          increment_pending_seen(pplayer, tile1);
        }
       } else if (dr > new_radius_sq && dr <= old_radius_sq) {
-       if (!pseudo || map_is_known(tile1, pplayer)) {
+       if (can_reveal_tiles || map_is_known(tile1, pplayer)) {
          map_fog_tile(pplayer, tile1);
        } else {
          decrement_pending_seen(pplayer, tile1);
@@ -826,27 +790,6 @@
 }
 
 /**************************************************************************
-For removing a unit. The actual removal is done in server_remove_unit
-**************************************************************************/
-void remove_unit_sight_points(struct unit *punit)
-{
-  struct tile *ptile = punit->tile;
-  struct player *pplayer = unit_owner(punit);
-  int vision;
-
-  freelog(LOG_DEBUG, "Removing unit sight points at  %i,%i",
-         TILE_XY(punit->tile));
-
-  if (tile_has_special(punit->tile, S_FORTRESS)
-      && unit_profits_of_watchtower(punit)) {
-    vision = get_watchtower_vision(punit);
-  } else {
-    vision = unit_type(punit)->vision_radius_sq;
-  }
-  map_refog_circle(pplayer, ptile, vision, -1, FALSE);
-}
-
-/**************************************************************************
 Shows area even if still fogged. If the tile is not "seen" units are not
 shown
 **************************************************************************/
@@ -1836,3 +1779,77 @@
   assert(id > 0);
   return ocean_sizes[id];
 }
+
+/* Vision structure - see documentation in maphand.h */
+struct vision {
+  /* These values cannot be changed after initialization. */
+  struct player *player;
+  struct tile *tile;
+  bool can_reveal_tiles;
+
+  /* The radius of the vision source. */
+  int radius_sq;
+};
+
+/****************************************************************************
+  Create a new vision source.
+
+  See documentation in maphand.h.
+****************************************************************************/
+struct vision *vision_new(struct player *pplayer, struct tile *ptile,
+                         bool can_reveal_tiles)
+{
+  struct vision *vision = fc_malloc(sizeof(*vision));
+
+  vision->player = pplayer;
+  vision->tile = ptile;
+  vision->can_reveal_tiles = can_reveal_tiles;
+  vision->radius_sq = -1;
+
+  return vision;
+}
+
+/****************************************************************************
+  Returns the sight points (radius_sq) that this vision source has.
+
+  See documentation in maphand.h.
+****************************************************************************/
+int vision_get_sight(const struct vision *vision)
+{
+  return vision->radius_sq;
+}
+
+/****************************************************************************
+  Change the sight points for the vision source, fogging or unfogging tiles
+  as needed.
+
+  See documentation in maphand.h.
+****************************************************************************/
+void vision_change_sight(struct vision *vision, int radius_sq)
+{
+  map_refog_circle(vision->player, vision->tile,
+                  vision->radius_sq, radius_sq,
+                  vision->can_reveal_tiles);
+  vision->radius_sq = radius_sq;
+}
+
+/****************************************************************************
+  Clear all sight poitns from this vision source.
+
+  See documentation in maphand.h.
+****************************************************************************/
+void vision_clear_sight(struct vision *vision)
+{
+  vision_change_sight(vision, -1);
+}
+
+/****************************************************************************
+  Free the vision source.
+
+  See documentation in maphand.h.
+****************************************************************************/
+void vision_free(struct vision *vision)
+{
+  assert(server_state != RUN_GAME_STATE || vision->radius_sq < 0);
+  free(vision);
+}
Index: server/maphand.h
===================================================================
--- server/maphand.h    (revision 11127)
+++ server/maphand.h    (working copy)
@@ -65,12 +65,7 @@
 void conceal_hidden_units(struct player *pplayer, struct tile *ptile);
 void upgrade_city_rails(struct player *pplayer, bool discovery);
 void send_map_info(struct conn_list *dest);
-void map_fog_city_area(struct city *pcity);
-void map_unfog_city_area(struct city *pcity);
-void remove_unit_sight_points(struct unit *punit);
 void show_circle(struct player *pplayer,struct tile *ptile, int radius_sq);
-void map_refog_circle(struct player *pplayer, struct tile *ptile,
-                     int old_radius_sq, int new_radius_sq, bool pseudo);
 
 bool map_is_known_and_seen(const struct tile *ptile, struct player *pplayer);
 void map_change_seen(struct tile *ptile, struct player *pplayer, int change);
@@ -104,4 +99,60 @@
 int get_continent_size(Continent_id id);
 int get_ocean_size(Continent_id id);
 
+/****************************************************************************
+  Vision for units and cities:
+
+  A vision source has a fixed owner and tile; it changes only in range.
+  Vision range is given in radius squared; most such values will come from
+  the ruleset.  All vision is circular.
+
+  A vision source is created using vision_new; this creates the source
+  without any sight points.  Call vision_change_sight to change the sight
+  points of a vision source (generally called from city_refresh_vision
+  and unit_refresh vision; this can be called liberally to do updates after
+  an effect may have changed the source's vision range).  Clear the sight
+  using vision_clear_sight before freeing it with vision_free.
+
+  vision_get_sight returns the sight points of the source.  This should
+  only rarely be necessary since all fogging and unfogging operations
+  are taken care of internally.
+
+  The can_reveal_tiles parameter controls whether the vision source can
+  discover new (unknown) tiles or simply maintain vision on already-known
+  tiles.  Currently cities should pass FALSE for this since they cannot
+  discover new tiles.
+
+  ***** IMPORTANT *****
+  To change any of the parameters given to vision_new - that is, to change
+  the vision source's position (tile) or owner - you must create a new
+  vision and then clear and free the old vision.  Order is very important
+  here since you do not want to fog tiles intermediately.  You must store
+  a copy of the old vision source, then create and attach and fill out the
+  sight for a new vision source, and only then may you clear and free the
+  old vision source.  In most operations you'll want to stick some other
+  code in between so that for the bulk of the operation all tiles are
+  visible.  For instance to move a unit:
+
+    old_vision = punit->server.vision;
+    punit->server.vision = vision_new(punit->owner, dest_tile, TRUE);
+    vision_change_sight(punit->server.vision,
+                        get_unit_vision_at(punit, dest_tile));
+
+    ...then do all the work of moving the unit...
+
+    vision_clear_sight(old_vision);
+    vision_free(old_vision);
+
+  note that for all the code in the middle both the new and the old
+  vision sources are active.  The same process applies when transferring
+  a unit or city between players, etc.
+****************************************************************************/
+struct vision;
+struct vision *vision_new(struct player *pplayer, struct tile *ptile,
+                         bool can_reveal_tiles);
+int vision_get_sight(const struct vision *vision);
+void vision_change_sight(struct vision *vision, int radius_sq);
+void vision_clear_sight(struct vision *vision);
+void vision_free(struct vision *vision);
+
 #endif  /* FC__MAPHAND_H */
Index: server/unittools.c
===================================================================
--- server/unittools.c  (revision 11127)
+++ server/unittools.c  (working copy)
@@ -663,18 +663,7 @@
        /* If a watchtower has been pillaged, reduce sight to normal */
        if (what == S_FORTRESS) {
          freelog(LOG_VERBOSE, "Watchtower pillaged!");
-         /* This could be a helper function. */
-         unit_list_iterate(ptile->units, punit2) {
-            struct player *owner = unit_owner(punit2);
-
-            if (is_ground_unit(punit2)
-                && player_knows_techs_with_flag(owner, TF_WATCHTOWER)) {
-              map_refog_circle(owner, punit2->tile,
-                              get_watchtower_vision(punit2),
-                              unit_type(punit2)->vision_radius_sq, FALSE);
-            }
-         }
-         unit_list_iterate_end;
+         unit_list_refresh_vision(ptile->units);
        }
       }
     }
@@ -697,18 +686,7 @@
       /* If a watchtower has been pillaged, reduce sight to normal */
       if (what_pillaged == S_FORTRESS) {
        freelog(LOG_VERBOSE, "Watchtower(2) pillaged!");
-       /* This could be a helper function. */
-       unit_list_iterate(ptile->units, punit2) {
-          struct player *owner = unit_owner(punit2);
-
-          if (is_ground_unit(punit2)
-              && player_knows_techs_with_flag(owner, TF_WATCHTOWER)) {
-           map_refog_circle(owner, ptile,
-                            get_watchtower_vision(punit2),
-                            unit_type(punit2)->vision_radius_sq, FALSE);
-          }
-       }
-       unit_list_iterate_end;
+       unit_list_refresh_vision(ptile->units);
       }
     }
   }
@@ -735,18 +713,7 @@
       tile_set_special(ptile, S_FORTRESS);
       unit_activity_done = TRUE;
       /* watchtower becomes effective */
-      /* This could be a helper function. */
-      unit_list_iterate(ptile->units, punit) {
-        struct player *owner = unit_owner(punit);
-
-        if (is_ground_unit(punit)
-            && player_knows_techs_with_flag(owner, TF_WATCHTOWER)) {
-          map_refog_circle(pplayer, ptile,
-                          unit_type(punit)->vision_radius_sq,
-                          get_watchtower_vision(punit), FALSE);
-        }
-      }
-      unit_list_iterate_end;
+      unit_list_refresh_vision(ptile->units);
     }
   }
 
@@ -1275,7 +1242,6 @@
                  bool is_free)
 {
   struct player *pplayer = unit_owner(punit);
-  int old_radius_sq, new_radius_sq;
   int old_mr = unit_move_rate(punit), old_hp = unit_type(punit)->hp;
 
   assert(test_unit_upgrade(punit, is_free) == UR_OK);
@@ -1285,14 +1251,6 @@
        unit_upgrade_price(pplayer, punit->type, to_unit);
   }
 
-  /* save old vision range */
-  if (tile_has_special(punit->tile, S_FORTRESS)
-      && unit_profits_of_watchtower(punit)) {
-    old_radius_sq = get_watchtower_vision(punit);
-  } else {
-    old_radius_sq = unit_type(punit)->vision_radius_sq;
-  }
-
   punit->type = to_unit;
 
   /* Scale HP and MP, rounding down.  Be careful with integer arithmetic,
@@ -1303,14 +1261,7 @@
 
   conn_list_do_buffer(pplayer->connections);
 
-  /* apply new vision range */
-  if (tile_has_special(punit->tile, S_FORTRESS)
-      && unit_profits_of_watchtower(punit)) {
-    new_radius_sq = get_watchtower_vision(punit);
-  } else {
-    new_radius_sq = to_unit->vision_radius_sq;
-  }
-  map_refog_circle(pplayer, punit->tile, old_radius_sq, new_radius_sq, FALSE);
+  unit_refresh_vision(punit);
 
   send_unit_info(NULL, punit);
   conn_list_do_unbuffer(pplayer->connections);
@@ -1388,14 +1339,8 @@
     send_city_info(pplayer, pcity);
   }
 
-  if (tile_has_special(ptile, S_FORTRESS)
-      && unit_profits_of_watchtower(punit)) {
-    map_refog_circle(pplayer, punit->tile,
-                    -1, get_watchtower_vision(punit), FALSE);
-  } else {
-    map_refog_circle(pplayer, ptile,
-                    -1, unit_type(punit)->vision_radius_sq, FALSE);
-  }
+  punit->server.vision = vision_new(pplayer, ptile, TRUE);
+  unit_refresh_vision(punit);
 
   send_unit_info(NULL, punit);
   maybe_make_contact(ptile, unit_owner(punit));
@@ -1447,7 +1392,9 @@
     }
   } players_iterate_end;
 
-  remove_unit_sight_points(punit);
+  vision_clear_sight(punit->server.vision);
+  vision_free(punit->server.vision);
+  punit->server.vision = NULL;
 
   /* check if this unit had F_GAMELOSS flag */
   if (unit_flag(punit, F_GAMELOSS) && unit_owner(punit)->is_alive) {
@@ -2551,17 +2498,10 @@
      wake them up if the punit is farther away than 3. */
   square_iterate(punit->tile, 3, ptile) {
     unit_list_iterate(ptile->units, penemy) {
-      int radius_sq;
+      int radius_sq = get_unit_vision_at(penemy, penemy->tile);
       enum unit_move_type move_type = unit_type(penemy)->move_type;
       struct terrain *pterrain = tile_get_terrain(ptile);
 
-      if (tile_has_special(ptile, S_FORTRESS)
-         && unit_profits_of_watchtower(penemy)) {
-       radius_sq = get_watchtower_vision(penemy);
-      } else {
-       radius_sq = unit_type(penemy)->vision_radius_sq;
-      }
-      
       if (!pplayers_allied(unit_owner(punit), unit_owner(penemy))
          && penemy->activity == ACTIVITY_SENTRY
          && radius_sq >= sq_map_distance(punit->tile, ptile)
@@ -2721,6 +2661,7 @@
   struct tile *psrctile = punit->tile;
   struct city *pcity;
   struct unit *ptransporter = NULL;
+  struct vision *old_vision = punit->server.vision;
     
   conn_list_do_buffer(pplayer->connections);
 
@@ -2740,9 +2681,12 @@
 
     /* Insert them again. */
     unit_list_iterate(cargo_units, pcargo) {
-      map_refog_circle(unit_owner(pcargo), pdesttile,
-                      -1, unit_type(pcargo)->vision_radius_sq, FALSE);
+      struct vision *old_vision = pcargo->server.vision;
 
+      pcargo->server.vision = vision_new(pcargo->owner, pdesttile, TRUE);
+      vision_change_sight(pcargo->server.vision,
+                         get_unit_vision_at(pcargo, pdesttile));
+
       /* Silently free orders since they won't be applicable anymore. */
       free_unit_orders(pcargo);
 
@@ -2751,8 +2695,10 @@
       unit_list_prepend(pdesttile->units, pcargo);
       check_unit_activity(pcargo);
       send_unit_info_to_onlookers(NULL, pcargo, psrctile, FALSE);
-      map_refog_circle(unit_owner(pcargo), psrctile,
-                      unit_type(pcargo)->vision_radius_sq, -1, FALSE);
+
+      vision_clear_sight(old_vision);
+      vision_free(old_vision);
+
       handle_unit_move_consequences(pcargo, psrctile, pdesttile);
     } unit_list_iterate_end;
     unit_list_unlink_all(cargo_units);
@@ -2766,14 +2712,9 @@
      move */
 
   /* Enhance vision if unit steps into a fortress */
-  if (unit_profits_of_watchtower(punit)
-      && tile_has_special(pdesttile, S_FORTRESS)) {
-    map_refog_circle(pplayer, pdesttile,
-                    -1, get_watchtower_vision(punit), FALSE);
-  } else {
-    map_refog_circle(pplayer, pdesttile,
-                    -1, unit_type(punit)->vision_radius_sq, FALSE);
-  }
+  punit->server.vision = vision_new(punit->owner, pdesttile, TRUE);
+  vision_change_sight(punit->server.vision,
+                     get_unit_vision_at(punit, pdesttile));
 
   unit_list_unlink(psrctile->units, punit);
   punit->tile = pdesttile;
@@ -2852,14 +2793,8 @@
    * at (src_x, src_y) */
   reveal_hidden_units(pplayer, pdesttile);
 
-  if (unit_profits_of_watchtower(punit)
-      && tile_has_special(psrctile, S_FORTRESS)) {
-    map_refog_circle(pplayer, psrctile,
-                    get_watchtower_vision(punit), -1, FALSE);
-  } else {
-    map_refog_circle(pplayer, psrctile,
-                    unit_type(punit)->vision_radius_sq, -1, FALSE);
-  }
+  vision_clear_sight(old_vision);
+  vision_free(old_vision);
 
   /*
    * Let the unit goes out of sight for the players which doesn't see
@@ -2952,16 +2887,9 @@
 static bool maybe_cancel_patrol_due_to_enemy(struct unit *punit)
 {
   bool cancel = FALSE;
-  int radius_sq;
+  int radius_sq = get_unit_vision_at(punit, punit->tile);
   struct player *pplayer = unit_owner(punit);
 
-  if (tile_has_special(punit->tile, S_FORTRESS)
-      && unit_profits_of_watchtower(punit)) {
-    radius_sq = get_watchtower_vision(punit);
-  } else {
-    radius_sq = unit_type(punit)->vision_radius_sq;
-  }
-  
   circle_iterate(punit->tile, radius_sq, ptile) {
     struct unit *penemy =
        is_non_allied_unit_tile(ptile, pplayer);
@@ -3241,22 +3169,36 @@
   } /* end while */
 }
 
-/**************************************************************************
-  Get the vision range of a unit standing inside a 'watchtower'.
-**************************************************************************/
-int get_watchtower_vision(struct unit *punit)
+int get_unit_vision_at(struct unit *punit, struct tile *ptile)
 {
-  return (unit_type(punit)->vision_radius_sq
-         + terrain_control.watchtower_extra_vision_radius_sq);
+  int radius_sq = punit->type->vision_radius_sq;
+
+  if (is_ground_unit(punit)
+      && player_knows_techs_with_flag(unit_owner(punit), TF_WATCHTOWER)) {
+    radius_sq += terrain_control.watchtower_extra_vision_radius_sq;
+  }
+
+  return radius_sq;
 }
 
-/**************************************************************************
-  Would a unit gain extra vision range due to a 'watchtower', if there
-  was one present?
-**************************************************************************/
-bool unit_profits_of_watchtower(struct unit *punit)
+/****************************************************************************
+  Refresh the unit's vision.
+
+  This function has very small overhead and can be called any time effects
+  may have changed the vision range of the city.
+****************************************************************************/
+void unit_refresh_vision(struct unit *punit)
 {
-  return (is_ground_unit(punit)
-         && player_knows_techs_with_flag(unit_owner(punit),
-                                         TF_WATCHTOWER));
+  vision_change_sight(punit->server.vision,
+                     get_unit_vision_at(punit, punit->tile));
 }
+
+/****************************************************************************
+  Refresh the vision of all units in the list - see unit_refresh_vision.
+****************************************************************************/
+void unit_list_refresh_vision(struct unit_list *punitlist)
+{
+  unit_list_iterate(punitlist, punit) {
+    unit_refresh_vision(punit);
+  } unit_list_iterate_end;
+}
Index: server/techtools.c
===================================================================
--- server/techtools.c  (revision 11127)
+++ server/techtools.c  (working copy)
@@ -212,15 +212,7 @@
   
   /* Enhance vision of units inside a fortress */
   if (tech_flag(tech_found, TF_WATCHTOWER)) {
-    unit_list_iterate(plr->units, punit) {
-      if (tile_has_special(punit->tile, S_FORTRESS)
-         && is_ground_unit(punit)) {
-       map_refog_circle(plr, punit->tile,
-                        unit_type(punit)->vision_radius_sq,
-                        get_watchtower_vision(punit), FALSE);
-      }
-    }
-    unit_list_iterate_end;
+    unit_list_refresh_vision(plr->units);
   }
 
   /* Notify a player about new governments available */
Index: server/unittools.h
===================================================================
--- server/unittools.h  (revision 11127)
+++ server/unittools.h  (working copy)
@@ -46,8 +46,9 @@
 void resolve_unit_stacks(struct player *pplayer, struct player *aplayer,
                          bool verbose);
 void remove_allied_visibility(struct player* pplayer, struct player* aplayer);
-int get_watchtower_vision(struct unit *punit);
-bool unit_profits_of_watchtower(struct unit *punit);
+int get_unit_vision_at(struct unit *punit, struct tile *ptile);
+void unit_refresh_vision(struct unit *punit);
+void unit_list_refresh_vision(struct unit_list *punitlist);
 void pay_for_units(struct player *pplayer, struct city *pcity);
 void bounce_unit(struct unit *punit, bool verbose);
 
Index: server/unithand.c
===================================================================
--- server/unithand.c   (revision 11127)
+++ server/unithand.c   (working copy)
@@ -287,24 +287,18 @@
     unit_list_unlink(old_pcity->units_supported, punit);
   }
   if (old_owner != new_owner) {
-    int vision_radius_sq;
+    vision_clear_sight(punit->server.vision);
+    vision_free(punit->server.vision);
+    conceal_hidden_units(old_owner, punit->tile);
 
-    remove_unit_sight_points(punit);
     ai_reinit(punit);
 
     unit_list_unlink(old_owner->units, punit);
     unit_list_prepend(new_owner->units, punit);
     punit->owner = new_owner;
 
-    if (tile_has_special(punit->tile, S_FORTRESS)
-        && unit_profits_of_watchtower(punit)) {
-      vision_radius_sq = get_watchtower_vision(punit);
-    } else {
-      vision_radius_sq = unit_type(punit)->vision_radius_sq;
-    }
-    map_refog_circle(new_owner, punit->tile, -1, vision_radius_sq, FALSE);
-
-    conceal_hidden_units(old_owner, punit->tile);
+    punit->server.vision = vision_new(new_owner, punit->tile, TRUE);
+    unit_refresh_vision(punit);
     reveal_hidden_units(new_owner, punit->tile);
   }
 
Index: server/savegame.c
===================================================================
--- server/savegame.c   (revision 11127)
+++ server/savegame.c   (working copy)
@@ -1572,27 +1572,13 @@
       punit->orders.list = NULL;
     }
 
-    {
-      int radius_sq;
+    /* allocate the unit's contribution to fog of war */
+    punit->server.vision = vision_new(punit->owner, punit->tile, TRUE);
+    unit_refresh_vision(punit);
+    /* NOTE: There used to be some map_set_known calls here.  These were
+     * unneeded since unfogging the tile when the unit sees it will
+     * automatically reveal that tile. */
 
-      if (tile_has_special(punit->tile, S_FORTRESS)
-         && unit_profits_of_watchtower(punit)) {
-       radius_sq = get_watchtower_vision(punit);
-      } else {
-       radius_sq = unit_type(punit)->vision_radius_sq;
-      }
-
-      /* Sanity: set the map to known for all tiles within the vision
-       * range. */
-      circle_iterate(punit->tile, radius_sq, tile1) {
-       map_set_known(tile1, plr);
-      } circle_iterate_end;
-
-      /* allocate the unit's contribution to fog of war */
-      map_refog_circle(unit_owner(punit), punit->tile,
-                      -1, radius_sq, FALSE);
-    }
-
     unit_list_append(plr->units, punit);
 
     unit_list_prepend(punit->tile->units, punit);
@@ -2132,7 +2118,8 @@
     }
     
     /* adding the cities contribution to fog-of-war */
-    map_unfog_city_area(pcity);
+    pcity->server.vision = vision_new(pcity->owner, pcity->tile, FALSE);
+    city_refresh_vision(pcity);
 
     pcity->units_supported = unit_list_new();
 
Index: common/unit.c
===================================================================
--- common/unit.c       (revision 11127)
+++ common/unit.c       (working copy)
@@ -1491,6 +1491,7 @@
   set_unit_activity(punit, ACTIVITY_IDLE);
   punit->occupy = 0;
   punit->client.colored = FALSE;
+  punit->server.vision = NULL; /* No vision. */
   punit->has_orders = FALSE;
 
   return punit;
Index: common/unit.h
===================================================================
--- common/unit.h       (revision 11127)
+++ common/unit.h       (working copy)
@@ -169,6 +169,9 @@
     bool colored;
     int color_index;
   } client;
+  struct {
+    struct vision *vision; /* See explanation in maphand.h. */
+  } server;
 
   bool has_orders;
   struct {
Index: common/city.c
===================================================================
--- common/city.c       (revision 11127)
+++ common/city.c       (working copy)
@@ -2461,7 +2461,7 @@
 
   pcity->server.workers_frozen = 0;
   pcity->server.needs_arrange = FALSE;
-  pcity->server.vision_radius_sq = -1; /* No vision. */
+  pcity->server.vision = NULL; /* No vision. */
 
   pcity->ai.founder_want = 0; /* calculating this is really expensive */
   pcity->ai.next_founder_want_recalc = 0; /* turns to recalc found_want */
Index: common/city.h
===================================================================
--- common/city.h       (revision 11127)
+++ common/city.h       (working copy)
@@ -294,8 +294,7 @@
      * set inside auto_arrange_workers. */
     bool needs_arrange;
 
-    /* The vision radius that is currently unfogged. */
-    int vision_radius_sq;
+    struct vision *vision; /* See explanation in maphand.h. */
   } server;
 
   int turn_founded;            /* In which turn was the city founded? */
Index: common/game.c
===================================================================
--- common/game.c       (revision 11127)
+++ common/game.c       (working copy)
@@ -121,6 +121,9 @@
 {
   struct city *pcity;
 
+  /* Opaque server-only variable: the server must free this earlier. */
+  assert(punit->server.vision == NULL);
+
   freelog(LOG_DEBUG, "game_remove_unit %d", punit->id);
   freelog(LOG_DEBUG, "removing unit %d, %s %s (%d %d) hcity %d",
          punit->id, get_nation_name(unit_owner(punit)->nation),
@@ -159,6 +162,9 @@
           get_nation_name(city_owner(pcity)->nation), pcity->tile->x,
          pcity->tile->y);
 
+  /* Opaque server-only variable: the server must free this earlier. */
+  assert(pcity->server.vision == NULL);
+
   city_map_checked_iterate(pcity->tile, x, y, map_tile) {
     set_worker_city(pcity, x, y, C_TILE_EMPTY);
   } city_map_checked_iterate_end;

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