Complete.Org: Mailing Lists: Archives: freeciv-dev: February 2006:
[Freeciv-Dev] Re: (PR#11311) death to smallpox?
Home

[Freeciv-Dev] Re: (PR#11311) death to smallpox?

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
To: jdorje@xxxxxxxxxxxxxxxxxxxxx
Subject: [Freeciv-Dev] Re: (PR#11311) death to smallpox?
From: "Per I. Mathisen" <per@xxxxxxxxxxx>
Date: Sun, 12 Feb 2006 14:21:59 -0800
Reply-to: bugs@xxxxxxxxxxx

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

On Tue, 27 Sep 2005, Jason Short wrote:
> Here is an updated version of the patch.

New one attached. I had to remove the ruleset changes, though.

> The main problem remaining is an ugly one: with the "covered" value.
> Currently:
>
> * ptile->covered gives the number of cities that have access to the free
> output and bonuses from this tile
> * It is updated when cities are built or destroyed, and recalculated on
> game load.If citymap size were to change during the course of a game,
> it would have to be updated then too.

I have to note that this and related problems happen because cities are
created instantly. If city making was a unit activity like all other and
resolved on end turn, such problems would go away.

> * It does not respect borders.Doing so makes things substantially more
> complicated, since it has to be updated when borders change and this
> already coincides with founding of cities.The end result is the "long"
> caluclation would have to be done, that is O(n^2) in the citymap size of
> a city.

With the new borders rule this problem should be gone.

  - Per

Index: server/citytools.c
===================================================================
--- server/citytools.c  (revision 11583)
+++ server/citytools.c  (working copy)
@@ -991,7 +991,14 @@
   /* Place a worker at the city center; this is the free-worked tile.
    * This must be done before the city refresh (below) so that the city
    * is in a sane state. */
-  server_set_tile_city(pcity, CITY_MAP_SIZE/2, CITY_MAP_SIZE/2, C_TILE_WORKER);
+  city_map_checked_iterate(pcity->tile, x, y, ptile) {
+    ptile->covered++;
+    assert(ptile->covered == map_tile_get_coverage(ptile));
+    send_tile_info(NULL, ptile);
+    if (is_free_worked_tile(x, y)) {
+      server_set_tile_city(pcity, x, y, C_TILE_WORKER);
+    }
+  } city_map_checked_iterate_end;
 
   /* Refresh the city.  First a city refresh is done (this shouldn't
    * send any packets to the client because the city has no supported units)
@@ -1147,6 +1154,12 @@
 
   /* Update available tiles in adjacent cities. */
   map_city_radius_iterate(ptile, tile1) {
+    if (TRUE) {
+      assert(tile1->covered > 0);
+      tile1->covered--;
+      send_tile_info(NULL, tile1);
+      assert(tile1->covered == map_tile_get_coverage(tile1));
+    }
     /* For every tile the city could have used. */
     map_city_radius_iterate(tile1, tile2) {
       /* We see what cities are inside reach of the tile. */
@@ -1581,6 +1594,7 @@
   }
 
   output_type_iterate(o) {
+    packet->free[o] = pcity->free[o];
     packet->surplus[o] = pcity->surplus[o];
     packet->waste[o] = pcity->waste[o];
     packet->unhappy_penalty[o] = pcity->unhappy_penalty[o];
Index: server/ruleset.c
===================================================================
--- server/ruleset.c    (revision 11583)
+++ server/ruleset.c    (working copy)
@@ -1489,6 +1489,9 @@
 
   /* terrain details */
 
+  output_type_iterate(o) {
+    terrain_control.terrain_bonuses[o] = FALSE;
+  } output_type_iterate_end;
   terrain_type_iterate(pterrain) {
     char **slist;
     const int i = pterrain->index;
@@ -1525,6 +1528,14 @@
       pterrain->output[o]
        = secfile_lookup_int_default(file, 0, "%s.%s", sec[i],
                                     get_output_identifier(o));
+      pterrain->add[o]
+       = secfile_lookup_int_default(file, 0, "%s.%s_add", sec[i],
+                                    get_output_identifier(o));
+      pterrain->bonus[o]
+       = secfile_lookup_int_default(file, 0, "%s.%s_bonus",
+                                    sec[i],
+                                    get_output_identifier(o));
+      terrain_control.terrain_bonuses[o] |= (pterrain->bonus[o] > 0);
     } output_type_iterate_end;
 
     res = secfile_lookup_str_vec (file, &nval, "%s.resources", sec[i]);
Index: server/maphand.c
===================================================================
--- server/maphand.c    (revision 11583)
+++ server/maphand.c    (working copy)
@@ -470,6 +470,7 @@
   } else {
     info.spec_sprite[0] = '\0';
   }
+  info.covered = ptile->covered; /* Minor cheating. */
 
   conn_list_iterate(dest, pconn) {
     struct player *pplayer = pconn->player;
@@ -1697,6 +1698,35 @@
 }
 
 /*************************************************************************
+  Calculate how many cities have access to this tile.
+
+  Currently this doesn't restrict coverage by borders, because doing so
+  would make the updating logic substantially more complicated.
+*************************************************************************/
+int map_tile_get_coverage(const struct tile *ptile)
+{
+  int covered = 0;
+
+  city_map_checked_iterate(ptile, cx, cy, itr_tile) {
+    if (itr_tile->city) {
+      covered++;
+    }
+  } city_map_checked_iterate_end;
+
+  return covered;
+}
+
+/*************************************************************************
+  Calculate how many cities have access to each tile.
+*************************************************************************/
+void map_calculate_coverage(void)
+{
+  whole_map_iterate(ptile) {
+    ptile->covered = map_tile_get_coverage(ptile);
+  } whole_map_iterate_end;
+}
+
+/*************************************************************************
   Return size in tiles of the given continent(not ocean)
 *************************************************************************/
 int get_continent_size(Continent_id id)
Index: server/maphand.h
===================================================================
--- server/maphand.h    (revision 11583)
+++ server/maphand.h    (working copy)
@@ -96,6 +96,9 @@
 void map_claim_ownership(struct tile *ptile, struct player *owner,
                          struct tile *source);
 
+int map_tile_get_coverage(const struct tile *ptile);
+void map_calculate_coverage(void);
+
 void check_terrain_change(struct tile *ptile, struct terrain *oldter);
 int get_continent_size(Continent_id id);
 int get_ocean_size(Continent_id id);
Index: server/sanitycheck.c
===================================================================
--- server/sanitycheck.c        (revision 11583)
+++ server/sanitycheck.c        (working copy)
@@ -194,6 +194,8 @@
       } adjc_iterate_end;
     }
 
+    SANITY_CHECK(ptile->covered == map_tile_get_coverage(ptile));
+
     if (pcity) {
       SANITY_TILE(ptile, same_pos(pcity->tile, ptile));
     }
@@ -304,11 +306,12 @@
 
   /* Sanity check city size versus worker and specialist counts. */
   city_map_iterate(x, y) {
-    if (get_worker_city(pcity, x, y) == C_TILE_WORKER) {
+    if (get_worker_city(pcity, x, y) == C_TILE_WORKER
+       && !is_free_worked_tile(x, y)) {
       workers++;
     }
   } city_map_iterate_end;
-  if (workers + city_specialists(pcity) != pcity->size + 1) {
+  if (workers + city_specialists(pcity) != pcity->size) {
     int diff = pcity->size + 1 - workers - city_specialists(pcity);
 
     SANITY_CITY(pcity, workers + city_specialists(pcity) == pcity->size + 1);
Index: server/savegame.c
===================================================================
--- server/savegame.c   (revision 11583)
+++ server/savegame.c   (working copy)
@@ -3914,6 +3914,8 @@
     initialize_globals();
     apply_unit_ordering();
 
+    map_calculate_coverage();
+
     /* Make sure everything is consistent. */
     players_iterate(pplayer) {
       unit_list_iterate(pplayer->units, punit) {
Index: common/packets.def
===================================================================
--- common/packets.def  (revision 11583)
+++ common/packets.def  (working copy)
@@ -339,6 +339,7 @@
 
   TERRAIN type;
   UINT8 known;
+  UINT8 covered;
   BOOL special[S_LAST];
   RESOURCE resource;
   PLAYER owner;
@@ -500,6 +501,7 @@
   UINT8 specialists_size;
   UINT8 specialists[SP_MAX:specialists_size];
 
+  SINT16 free[O_MAX];
   SINT16 surplus[O_MAX];
   UINT16 waste[O_MAX];
   SINT16 unhappy_penalty[O_MAX];
@@ -1135,6 +1137,7 @@
   BOOL may_irrigate;   /* may build irrigation/farmland */
   BOOL may_mine;       /* may build mines */
   BOOL may_transform;  /* may transform terrain */
+  BOOL terrain_bonuses[O_MAX];
 
   /* parameters */
   UINT8 ocean_reclaim_requirement_pct; /* # adjacent land tiles for reclaim */
@@ -1220,6 +1223,8 @@
   SINT8 defense_bonus;
 
   UINT8 output[O_MAX];
+  SINT8 bonus[O_MAX];
+  SINT8 add[O_MAX];
   UINT8 num_resources;
   RESOURCE resources[MAX_NUM_RESOURCES:num_resources];
 
@@ -1344,6 +1349,8 @@
   
   STRING name_orig[MAX_LEN_NAME];
   UINT8 output[O_MAX];
+  SINT8 bonus[O_MAX];
+  SINT8 add[O_MAX];
   STRING graphic_str[MAX_LEN_NAME];
   STRING graphic_alt[MAX_LEN_NAME];
 end
Index: common/city.c
===================================================================
--- common/city.c       (revision 11583)
+++ common/city.c       (working copy)
@@ -1438,15 +1438,36 @@
 int get_final_city_output_bonus(const struct city *pcity, Output_type_id otype)
 {
   struct output_type *output = &output_types[otype];
+  int bonus0 = get_city_terrain_bonus(pcity, otype);
   int bonus1 = 100 + get_city_tile_output_bonus(pcity, NULL, output,
                                                EFT_OUTPUT_BONUS);
   int bonus2 = 100 + get_city_tile_output_bonus(pcity, NULL, output,
                                                EFT_OUTPUT_BONUS_2);
 
-  return MAX(bonus1 * bonus2 / 100, 0);
+  return MAX(((bonus0 * bonus1 / 100) * bonus2 / 100), 0);
 }
 
 /**************************************************************************
+  ?
+**************************************************************************/
+int get_city_terrain_bonus(const struct city *pcity, Output_type_id otype)
+{
+  int bonus = 0;
+
+  if (terrain_control.terrain_bonuses[otype]) {
+    const int factor = 420; /* Avoid rounding errors.  lcm(2..7) == 420. */
+
+    map_city_radius_iterate(pcity->tile, ptile) {
+      if (tile_get_known(ptile, pcity->owner) != TILE_UNKNOWN) {
+        bonus += tile_get_output_bonus(ptile, otype) * factor / ptile->covered;
+      }
+    } map_city_radius_iterate_end;
+    bonus /= factor;
+  }
+  return 100 + bonus;
+}
+
+/**************************************************************************
   Return the amount of gold generated by buildings under "tithe" attribute
   governments.
 **************************************************************************/
@@ -1562,6 +1583,24 @@
 }
 
 /****************************************************************************
+  Calculate the free output received by the city.
+****************************************************************************/
+static void city_get_free_output(const struct city *pcity, int *output)
+{
+  const int factor = 420; /* lcm(2..7) */
+
+  memset(output, 0, O_COUNT * sizeof(*output));
+  map_city_radius_iterate(pcity->tile, ptile) {
+    output_type_iterate(o) {
+      output[o] += factor * tile_get_output_add(ptile, o) / ptile->covered;
+    } output_type_iterate_end;
+  } map_city_radius_iterate_end;
+  output_type_iterate(o) {
+    output[o] /= factor;
+  } output_type_iterate_end;
+}
+
+/****************************************************************************
   This function sets all the values in the pcity->tile_output[] array. This
   should be called near the beginning of generic_city_refresh.  It doesn't
   depend on anything else in the refresh and doesn't change when workers are
@@ -1912,7 +1951,7 @@
    */
 
   output_type_iterate(o) {
-    pcity->prod[o] = pcity->citizen_base[o];
+    pcity->prod[o] = pcity->citizen_base[o] + pcity->free[o];
   } output_type_iterate_end;
 
   /* Add on special extra incomes: trade routes and tithes. */
@@ -2094,6 +2133,7 @@
     set_city_bonuses(pcity);   /* Calculate the bonus[] array values. */
     set_city_tile_output(pcity); /* Calculate the tile_output[] values. */
     city_support(pcity, send_unit_info); /* manage settlers, and units */
+    city_get_free_output(pcity, pcity->free);
   }
 
   /* Calculate output from citizens. */
Index: common/city.h
===================================================================
--- common/city.h       (revision 11583)
+++ common/city.h       (working copy)
@@ -230,6 +230,7 @@
   int trade[NUM_TRADEROUTES], trade_value[NUM_TRADEROUTES];
 
   /* the productions */
+  int free[O_MAX]; /* Free output received by the city. */
   int surplus[O_MAX]; /* Final surplus in each category. */
   int waste[O_MAX]; /* Waste/corruption in each category. */
   int unhappy_penalty[O_MAX]; /* Penalty from unhappy cities. */
@@ -499,6 +500,7 @@
 Specialist_type_id best_specialist(Output_type_id otype,
                                   const struct city *pcity);
 int get_final_city_output_bonus(const struct city *pcity, Output_type_id 
otype);
+int get_city_terrain_bonus(const struct city *pcity, Output_type_id otype);
 bool city_built_last_turn(const struct city *pcity);
 
 /* city creation / destruction */
Index: common/tile.c
===================================================================
--- common/tile.c       (revision 11583)
+++ common/tile.c       (working copy)
@@ -174,6 +174,41 @@
 }
 
 /****************************************************************************
+  The bonus multiplier provided by this tile.  This free bonus should be
+  split between all cities for whom the tile is inside the citymap of
+  (see map_tile_get_coverage).  It works identically to
+  the EFT_OUTPUT_BONUS.
+****************************************************************************/
+int tile_get_output_bonus(const struct tile *ptile, Output_type_id output)
+{
+  if (!ptile->terrain) {
+    return 0;
+  }
+  if (ptile->resource) {
+    return ptile->resource->bonus[output];
+  } else {
+    return ptile->terrain->bonus[output];
+  }
+}
+
+/****************************************************************************
+  The free output provided by this tile.  This free output should be
+  split between all cities for whom the tile is inside the citymap of
+  (see map_tile_get_coverage).
+****************************************************************************/
+int tile_get_output_add(const struct tile *ptile, Output_type_id output)
+{
+  if (!ptile->terrain) {
+    return 0;
+  }
+  if (ptile->resource) {
+    return ptile->resource->add[output];
+  } else {
+    return ptile->terrain->add[output];
+  }
+}
+
+/****************************************************************************
   Time to complete the given activity on the given tile.
 ****************************************************************************/
 int tile_activity_time(enum unit_activity activity, const struct tile *ptile)
Index: common/tile.h
===================================================================
--- common/tile.h       (revision 11583)
+++ common/tile.h       (working copy)
@@ -35,6 +35,7 @@
   struct unit_list *units;
   bv_player tile_known, tile_seen[V_COUNT];
   struct city *worked;      /* city working tile, or NULL if none */
+  int covered; /* Number of cities whose citymaps cover the tile. */
   Continent_id continent;
   struct player *owner;     /* Player owning this tile, or NULL. */
   struct tile *owner_source; /* what makes it owned by owner */
@@ -70,6 +71,9 @@
 enum known_type tile_get_known(const struct tile *ptile,
                              const struct player *pplayer);
 
+int tile_get_output_bonus(const struct tile *ptile, Output_type_id otype);
+int tile_get_output_add(const struct tile *ptile, Output_type_id otype);
+
 /* An arbitrary somewhat integer value.  Activity times are multiplied by
  * this amount, and divided by them later before being used.  This may
  * help to avoid rounding errors; however it should probably be removed. */
Index: common/terrain.h
===================================================================
--- common/terrain.h    (revision 11583)
+++ common/terrain.h    (working copy)
@@ -112,6 +112,8 @@
   int defense_bonus; /* % defense bonus - defaults to zero */
 
   int output[O_MAX];
+  int bonus[O_MAX];
+  int add[O_MAX];
 
   const struct resource **resources; /* NULL-terminated */
 
@@ -158,6 +160,8 @@
   char name_orig[MAX_LEN_NAME];
   char identifier; /* server-only, same as terrain->identifier */
   int output[O_MAX]; /* Amount added by this resource. */
+  int bonus[O_MAX];
+  int add[O_MAX];
   char graphic_str[MAX_LEN_NAME];
   char graphic_alt[MAX_LEN_NAME];
 };
Index: common/map.c
===================================================================
--- common/map.c        (revision 11583)
+++ common/map.c        (working copy)
@@ -275,6 +275,7 @@
   } vision_layer_iterate_end;
   ptile->continent = 0;
   ptile->city     = NULL;
+  ptile->covered = 0;
   ptile->units    = unit_list_new();
   ptile->worked   = NULL; /* pointer to city working tile */
   ptile->owner    = NULL; /* Tile not claimed by any nation. */
Index: ai/aisettler.c
===================================================================
--- ai/aisettler.c      (revision 11583)
+++ ai/aisettler.c      (working copy)
@@ -152,15 +152,21 @@
 
       /* Food */
       result->citymap[i][j].food
-       = base_city_get_output_tile(i, j, pcity, FALSE, O_FOOD);
+       = (base_city_get_output_tile(i, j, pcity, FALSE, O_FOOD)
+          + 4 * tile_get_output_add(ptile, O_FOOD));
 
       /* Shields */
       result->citymap[i][j].shield
-       = base_city_get_output_tile(i, j, pcity, FALSE, O_SHIELD);
+       = (base_city_get_output_tile(i, j, pcity, FALSE, O_SHIELD)
+          + 4 * tile_get_output_add(ptile, O_SHIELD));
 
       /* Trade */
       result->citymap[i][j].trade
-       = base_city_get_output_tile(i, j, pcity, FALSE, O_TRADE);
+       = (base_city_get_output_tile(i, j, pcity, FALSE, O_TRADE)
+          + 4 * tile_get_output_add(ptile, O_TRADE)
+          + 4 * tile_get_output_add(ptile, O_GOLD)
+          + 4 * tile_get_output_add(ptile, O_LUXURY)
+          + 4 * tile_get_output_add(ptile, O_SCIENCE));
 
       sum = result->citymap[i][j].food * ai->food_priority
             + result->citymap[i][j].trade * ai->science_priority
@@ -168,7 +174,8 @@
 
       /* Balance perfection */
       sum *= PERFECTION / 2;
-      if (result->citymap[i][j].food >= 2) {
+      if (result->citymap[i][j].food >= 2
+         || tile_get_output_add(ptile, O_FOOD) > 0) {
         sum *= 2; /* we need this to grow */
       }
 
Index: client/citydlg_common.c
===================================================================
--- client/citydlg_common.c     (revision 11583)
+++ client/citydlg_common.c     (working copy)
@@ -395,6 +395,12 @@
 
   buf[0] = '\0';
 
+  if (pcity->free[otype] != 0) {
+    cat_snprintf(buf, bufsz,
+                _("%+4d : Free from terrain\n"), pcity->free[otype]);
+    total += pcity->free[otype];
+  }
+
   cat_snprintf(buf, bufsz,
               _("%+4d : Citizens\n"), pcity->citizen_base[otype]);
   total += pcity->citizen_base[otype];
@@ -435,6 +441,18 @@
     }
   }
 
+/* was  if (priority == 0 && terrain_control.terrain_bonuses[otype]) { ?? */
+  if (terrain_control.terrain_bonuses[otype]) {
+    const int base = total, bonus = get_city_terrain_bonus(pcity, otype);
+
+    total = total * bonus / 100;
+    if (total != base) {
+      cat_snprintf(buf, bufsz,
+                  _("%+4d : Bonus from terrain\n"),
+                  total - base);
+    }
+  }
+
   for (priority = 0; priority < 2; priority++) {
     enum effect_type eft[] = {EFT_OUTPUT_BONUS, EFT_OUTPUT_BONUS_2};
 
Index: client/packhand.c
===================================================================
--- client/packhand.c   (revision 11583)
+++ client/packhand.c   (working copy)
@@ -466,6 +466,7 @@
   }
 
   output_type_iterate(o) {
+    pcity->free[o] = packet->free[o];
     pcity->surplus[o] = packet->surplus[o];
     pcity->waste[o] = packet->waste[o];
     pcity->unhappy_penalty[o] = packet->unhappy_penalty[o];
@@ -624,7 +625,6 @@
     }
   }
 
-
   if (can_client_change_view()) {
     refresh_city_mapcanvas(pcity, pcity->tile, FALSE, FALSE);
   }
@@ -2024,6 +2024,8 @@
     unit_list_unlink_all(ptile->units);
   }
 
+  ptile->covered = packet->covered;
+
   /* update continents */
   if (ptile->continent != packet->continent && ptile->continent != 0
       && packet->continent > 0) {
@@ -2331,6 +2333,8 @@
 
   output_type_iterate(o) {
     pterrain->output[o] = p->output[o];
+    pterrain->bonus[o] = p->bonus[o];
+    pterrain->add[o] = p->add[o];
   } output_type_iterate_end;
 
   pterrain->resources = fc_calloc(p->num_resources + 1,
@@ -2388,6 +2392,8 @@
 
   output_type_iterate(o) {
     presource->output[o] = p->output[o];
+    presource->bonus[o] = p->bonus[o];
+    presource->add[o] = p->add[o];
   } output_type_iterate_end;
 
   tileset_setup_resource(tileset, presource);

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