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

Attached patches implement civ2 style (i.e. 2.0.x) resource
conversion rules, with some slight caveats. Logically depends
on #40364.

A 'tier' field is added to 'struct resource'. Resources on the
same tier convert to each other, in essence emulating how
resources used to be either "special 1" or "special 2" in
the specials bit vector. The tier value is determined from
the order of the resource as listed in the terrain's
'resources' field in the terrain ruleset.

This would almost work with the ruleset files as they currently
are now, except that the grassland terrain's "Resources"
resource occurs only once. Therefore the patches add a
second "Resources" resource to the default and civ2 terrain
ruleset files.

Complicating the matter for S2_1, since resource names must
be unique to be able to be distinguished in the 'resources'
list, the two different kinds of "Resources" are renamed
"Animal Resources" and "Plant Resources" (which somewhat fits,
considering the resources in their respective tiers, though
I am open to better naming suggestions).

Similarly, "Petroleum" is added as the desert "Oil" resource
for S2_1's default terrain ruleset (oddly, this was already
present in its civ2 terrain ruleset).


High-level tile resource conversion is controlled by the
new function tile_convert_resource. This is where future
resource conversion rules can be implemented (which I will
submit in subsequent patches).

Also, a number of bugs were fixed where code would
temporarily modify a tile to see what would happen if its
terrain changed. Such code was modified to take into account
resource conversion.


----------------------------------------------------------------------
リソースと言うのは止めて下さい
>From 90d77b34b9f67d9a7ec7aaf6de0ca5ee1911a4a5 Mon Sep 17 00:00:00 2001
From: Madeline Book <[EMAIL PROTECTED]>
Date: Mon, 7 Jul 2008 00:03:33 -0400
Subject: [PATCH] Restore civ2/freeciv terrain resource conversion.

---
 client/packhand.c            |   10 +++++++
 common/terrain.c             |   61 ++++++++++++++++++++++++++++++++++++++++++
 common/terrain.h             |    8 +++++
 common/tile.c                |   57 +++++++++++++++++++++++++++++++++++----
 common/tile.h                |    1 +
 data/civ2/terrain.ruleset    |   13 +++++++-
 data/default/terrain.ruleset |   13 +++++++-
 server/generator/startpos.c  |    4 +++
 server/maphand.c             |    2 +
 server/ruleset.c             |   11 +++++++
 server/settlers.c            |   12 ++++++++
 11 files changed, 182 insertions(+), 10 deletions(-)

diff --git a/client/packhand.c b/client/packhand.c
index 8972017..6902981 100644
--- a/client/packhand.c
+++ b/client/packhand.c
@@ -2498,6 +2498,12 @@ void handle_ruleset_control(struct packet_ruleset_control *packet)
       popup_tileset_suggestion_dialog();
     }
   }
+
+  /* Resource "tier" is set in handle_ruleset_terrain.
+   * See resource_get_tier() for explanation. */
+  resource_type_iterate(presource) {
+    presource->tier = -1;
+  } resource_type_iterate_end;
 }
 
 /**************************************************************************
@@ -2779,7 +2785,11 @@ void handle_ruleset_terrain(struct packet_ruleset_terrain *p)
               p->resources[j],
               terrain_rule_name(pterrain));
     }
+    if (pterrain->resources[j] && pterrain->resources[j]->tier == -1) {
+      pterrain->resources[j]->tier = j;
+    }
   }
+  pterrain->num_resources = p->num_resources;
   pterrain->resources[p->num_resources] = NULL;
 
   pterrain->road_time = p->road_time;
diff --git a/common/terrain.c b/common/terrain.c
index baa5d80..5cc2881 100644
--- a/common/terrain.c
+++ b/common/terrain.c
@@ -386,6 +386,31 @@ struct resource *resource_by_number(const Resource_type_id type)
 }
 
 /****************************************************************************
+  Returns the "tier" for the given resource. This value corresponds to
+  the offset of the resource name in a terrain's 'resources' field as
+  listed in the terrain ruleset file. If a resource is listed in more
+  than one 'resources' list, then the tier value will correspond only
+  to the first one.
+
+  The tier value is primarily intended to be used to implement civ2
+  style resource conversion. The tier value thus corresponds to whether
+  the given resource was a "special1" or a "special2" in previous
+  terrain ruleset definitions. Generally, resources on the same
+  tier will convert to each other when terrain is changed.
+
+  Returns an integer greater than or equal to zero indicating the
+  tier, or -1 on invalid input or if not applicable (e.g. resource
+  is never listed in a terrain entry in the ruleset).
+****************************************************************************/
+int resource_get_tier(const struct resource *presource)
+{
+  if (!presource) {
+    return -1;
+  }
+  return presource->tier;
+}
+
+/****************************************************************************
   Return the resource type matching the identifier, or NULL when none matches.
 ****************************************************************************/
 struct resource *find_resource_by_identifier(const char identifier)
@@ -416,6 +441,42 @@ struct resource *find_resource_by_rule_name(const char *name)
 }
 
 /****************************************************************************
+  Returns the resource allowed to exist on the given terrain (according to
+  the terrain ruleset) and having "tier" equal to the given value.
+  See resource_get_tier().
+
+  May return NULL if no suitable resource type is found.
+****************************************************************************/
+struct resource *find_resource_by_tier(const struct terrain *pterrain,
+                                       int tier)
+{
+  if (!pterrain || !pterrain->resources || tier < 0
+      || pterrain->num_resources <= tier) {
+    return NULL;
+  }
+
+  return pterrain->resources[tier];
+}
+
+/****************************************************************************
+  Returns the resource having the same tier as the given resource on the
+  new terrain. See resource_get_tier() for an explanation of "tier".
+  
+  May return NULL.
+****************************************************************************/
+struct resource *resource_convert_by_tier(const struct resource *oldres,
+                                          const struct terrain *newter)
+{
+  int tier;
+  struct resource *newres;
+
+  tier = resource_get_tier(oldres);
+  newres = find_resource_by_tier(newter, tier);
+
+  return newres;
+}
+
+/****************************************************************************
   Return the (translated) name of the resource.
   You don't have to free the return pointer.
 ****************************************************************************/
diff --git a/common/terrain.h b/common/terrain.h
index b6cd4f1..0dbf852 100644
--- a/common/terrain.h
+++ b/common/terrain.h
@@ -82,6 +82,7 @@ struct resource {
 #define RESOURCE_NONE_IDENTIFIER ' '
 
   int output[O_MAX]; /* Amount added by this resource. */
+  int tier; /* See resource_get_tier(). */
 };
 
 /* === */
@@ -159,6 +160,7 @@ struct terrain {
   int output[O_MAX];
 
   struct resource **resources; /* NULL-terminated */
+  int num_resources; /* Length of above array. */
 
   int road_trade_incr;
   int road_time;
@@ -258,10 +260,16 @@ int count_terrain_property_near_tile(const struct tile *ptile,
 Resource_type_id resource_count(void);
 Resource_type_id resource_index(const struct resource *presource);
 Resource_type_id resource_number(const struct resource *presource);
+int resource_get_tier(const struct resource *presource);
 
 struct resource *resource_by_number(const Resource_type_id id);
 struct resource *find_resource_by_identifier(const char identifier);
 struct resource *find_resource_by_rule_name(const char *name);
+struct resource *find_resource_by_tier(const struct terrain *pterrain,
+                                       int tier);
+
+struct resource *resource_convert_by_tier(const struct resource *oldres,
+                                          const struct terrain *newter);
 
 const char *resource_rule_name(const struct resource *presource);
 const char *resource_name_translation(struct resource *presource);
diff --git a/common/tile.c b/common/tile.c
index fe1dca8..a20db32 100644
--- a/common/tile.c
+++ b/common/tile.c
@@ -389,9 +389,46 @@ static void tile_clear_dirtiness(struct tile *ptile)
 }
 
 /****************************************************************************
+  This function handles converting (or not) of terrain resources according
+  to the game rules after a tile's terrain changes due to game actions.
+  It assumes the given tile's terrain has already been updated to the new
+  type (but not its resource).
+
+  Currently, resources are converted according to civ2 style rules;
+  see resource_convert_by_tier().
+
+  NB: May modify the tile's resource to the new resource type, including
+  setting it to NULL (i.e. no resource).
+****************************************************************************/
+void tile_convert_resource(struct tile *ptile)
+{
+  const struct terrain *newter;
+  const struct resource *oldres;
+  struct resource *newres;
+
+  if (!ptile) {
+    return;
+  }
+
+  oldres = tile_resource(ptile);
+  if (!oldres) {
+    return;
+  }
+
+  newter = tile_terrain(ptile);
+  newres = resource_convert_by_tier(oldres, newter);
+
+  tile_set_resource(ptile, newres);
+}
+
+/****************************************************************************
   Change the terrain to the given type.  This does secondary tile updates to
   the tile (as will happen when mining/irrigation/transforming changes the
   tile's terrain).
+  
+  Outside of perhaps edit mode and map generating, you should generally
+  call tile_convert_resource after changing the tile's terrain with this
+  function.
 ****************************************************************************/
 void tile_change_terrain(struct tile *ptile, struct terrain *pterrain)
 {
@@ -484,8 +521,9 @@ void tile_remove_special(struct tile *ptile, enum tile_special_type special)
 }
 
 /****************************************************************************
-  Build irrigation on the tile.  This may change the specials of the tile
-  or change the terrain type itself.
+  Build irrigation on the tile.
+
+  NB: Potentially modifies the tile's terrain, specials and resource!
 ****************************************************************************/
 static void tile_irrigate(struct tile *ptile)
 {
@@ -499,12 +537,14 @@ static void tile_irrigate(struct tile *ptile)
     }
   } else if (pterrain->irrigation_result) {
     tile_change_terrain(ptile, pterrain->irrigation_result);
+    tile_convert_resource(ptile);
   }
 }
 
 /****************************************************************************
-  Build a mine on the tile.  This may change the specials of the tile
-  or change the terrain type itself.
+  Build a mine on the tile.
+
+  NB: Potentially modifies the tile's terrain, specials and resource!
 ****************************************************************************/
 static void tile_mine(struct tile *ptile)
 {
@@ -516,12 +556,14 @@ static void tile_mine(struct tile *ptile)
     tile_clear_special(ptile, S_IRRIGATION);
   } else if (pterrain->mining_result) {
     tile_change_terrain(ptile, pterrain->mining_result);
+    tile_convert_resource(ptile);
   }
 }
 
 /****************************************************************************
-  Transform (ACTIVITY_TRANSFORM) the tile.  This usually changes the tile's
-  terrain type.
+  Transform (ACTIVITY_TRANSFORM) the tile.
+
+  NB: Potentially modifies the tile's terrain, specials and resource!
 ****************************************************************************/
 static void tile_transform(struct tile *ptile)
 {
@@ -529,6 +571,7 @@ static void tile_transform(struct tile *ptile)
 
   if (pterrain->transform_result != T_NONE) {
     tile_change_terrain(ptile, pterrain->transform_result);
+    tile_convert_resource(ptile);
   }
 }
 
@@ -536,6 +579,8 @@ static void tile_transform(struct tile *ptile)
   Apply an activity (Activity_type_id, e.g., ACTIVITY_TRANSFORM) to a tile.
   Return false if there was a error or if the activity is not implemented
   by this function.
+
+  NB: Potentially modifies the tile's terrain, specials and resource!
 ****************************************************************************/
 bool tile_apply_activity(struct tile *ptile, Activity_type_id act) 
 {
diff --git a/common/tile.h b/common/tile.h
index 43cd6a4..84835a7 100644
--- a/common/tile.h
+++ b/common/tile.h
@@ -121,6 +121,7 @@ int tile_activity_base_time(const struct tile *ptile,
 
 /* These are higher-level functions that handle side effects on the tile. */
 void tile_change_terrain(struct tile *ptile, struct terrain *pterrain);
+void tile_convert_resource(struct tile *ptile);
 void tile_add_special(struct tile *ptile, enum tile_special_type special);
 void tile_remove_special(struct tile *ptile, enum tile_special_type special);
 bool tile_apply_activity(struct tile *ptile, Activity_type_id act);
diff --git a/data/civ2/terrain.ruleset b/data/civ2/terrain.ruleset
index 06f225d..cc12611 100644
--- a/data/civ2/terrain.ruleset
+++ b/data/civ2/terrain.ruleset
@@ -327,7 +327,7 @@ defense_bonus        = 0
 food                 = 2
 shield               = 0
 trade                = 0
-resources            = "resource_bonus"
+resources            = "resource_bonus1", "resource_bonus2"
 road_trade_incr      = 1
 road_time            = 2
 irrigation_result    = "yes"
@@ -695,7 +695,7 @@ identifier  = "p"
 food        = 2
 # forest.
 
-[resource_bonus]
+[resource_bonus1]
 name        = _("Resources")
 graphic     = "ts.grassland_resources"
 graphic_alt = "-"
@@ -703,6 +703,15 @@ identifier  = "R"
 update22two = "r"
 shield      = 1
 # grassland.
+#
+[resource_bonus2]
+name        = _("Resources")
+graphic     = "ts.grassland_resources"
+graphic_alt = "-"
+identifier  = "r"
+update22two = "r"
+shield      = 1
+# grassland.
 
 [resource_icy_ivory]
 name        = _("Ivory")
diff --git a/data/default/terrain.ruleset b/data/default/terrain.ruleset
index 526c52c..2b2e39a 100644
--- a/data/default/terrain.ruleset
+++ b/data/default/terrain.ruleset
@@ -412,7 +412,7 @@ defense_bonus        = 0
 food                 = 2
 shield               = 0
 trade                = 0
-resources            = "resource_bonus"
+resources            = "resource_bonus1", "resource_bonus2"
 road_trade_incr      = 1
 road_time            = 2
 irrigation_result    = "yes"
@@ -810,7 +810,7 @@ food        = 2
 
 ; "r" reserved for rubber
 
-[resource_bonus]
+[resource_bonus1]
 name        = _("Resources")
 graphic     = "ts.grassland_resources"
 graphic_alt = "-"
@@ -819,6 +819,15 @@ update22two = "r"
 shield      = 1
 # grassland.
 
+[resource_bonus2]
+name        = _("Resources")
+graphic     = "ts.grassland_resources"
+graphic_alt = "-"
+identifier  = "r"
+update22two = "r"
+shield      = 1
+# grassland.
+
 [resource_icy_ivory]
 name        = _("Ivory")
 graphic     = "ts.arctic_ivory"
diff --git a/server/generator/startpos.c b/server/generator/startpos.c
index ee434fd..f415844 100644
--- a/server/generator/startpos.c
+++ b/server/generator/startpos.c
@@ -43,6 +43,7 @@ static int *islands_index;
 static int get_tile_value(struct tile *ptile)
 {
   struct terrain *old_terrain;
+  struct resource *old_resource;
   bv_special old_special;
   int value, irrig_bonus, mine_bonus;
 
@@ -54,6 +55,7 @@ static int get_tile_value(struct tile *ptile)
 
   old_terrain = ptile->terrain;
   old_special = ptile->special;
+  old_resource = ptile->resource;
 
   tile_set_special(ptile, S_ROAD);
   tile_apply_activity(ptile, ACTIVITY_IRRIGATE);
@@ -64,6 +66,7 @@ static int get_tile_value(struct tile *ptile)
 
   ptile->terrain = old_terrain;
   ptile->special = old_special;
+  ptile->resource = old_resource;
   tile_set_special(ptile, S_ROAD);
   tile_apply_activity(ptile, ACTIVITY_MINE);
   mine_bonus = -value;
@@ -73,6 +76,7 @@ static int get_tile_value(struct tile *ptile)
 
   ptile->terrain = old_terrain;
   ptile->special = old_special;
+  ptile->resource = old_resource;
 
   value += MAX(0, MAX(mine_bonus, irrig_bonus)) / 2;
 
diff --git a/server/maphand.c b/server/maphand.c
index b73faf7..9dcf011 100644
--- a/server/maphand.c
+++ b/server/maphand.c
@@ -99,6 +99,7 @@ void global_warming(int effect)
     if (new != T_NONE && old != new) {
       effect--;
       tile_change_terrain(ptile, new);
+      tile_convert_resource(ptile);
       check_terrain_change(ptile, old);
       update_tile_knowledge(ptile);
       unit_list_iterate(ptile->units, punit) {
@@ -143,6 +144,7 @@ void nuclear_winter(int effect)
     if (new != T_NONE && old != new) {
       effect--;
       tile_change_terrain(ptile, new);
+      tile_convert_resource(ptile);
       check_terrain_change(ptile, old);
       update_tile_knowledge(ptile);
       unit_list_iterate(ptile->units, punit) {
diff --git a/server/ruleset.c b/server/ruleset.c
index 8c5a525..6ee4927 100644
--- a/server/ruleset.c
+++ b/server/ruleset.c
@@ -1714,6 +1714,13 @@ static void load_ruleset_terrain(struct section_file *file)
 				   get_output_identifier(o));
   } output_type_iterate_end;
 
+  /* Reset resource tiers here, since they will be set
+   * below when loading terrains' resource lists.
+   * See resource_get_tier() for tier explanation. */
+  resource_type_iterate(presource) {
+    presource->tier = -1;
+  } resource_type_iterate_end;
+
   /* terrain details */
 
   terrain_type_iterate(pterrain) {
@@ -1763,8 +1770,12 @@ static void load_ruleset_terrain(struct section_file *file)
     pterrain->resources = fc_calloc(nval + 1, sizeof(*pterrain->resources));
     for (j = 0; j < nval; j++) {
       pterrain->resources[j] = lookup_resource(filename, res[j], tsection);
+      if (pterrain->resources[j] && pterrain->resources[j]->tier == -1) {
+        pterrain->resources[j]->tier = j;
+      }
     }
     pterrain->resources[nval] = NULL;
+    pterrain->num_resources = nval;
     free(res);
     res = NULL;
 
diff --git a/server/settlers.c b/server/settlers.c
index 0f764d9..c7116ca 100644
--- a/server/settlers.c
+++ b/server/settlers.c
@@ -308,6 +308,7 @@ static int ai_calc_irrigate(struct city *pcity, struct player *pplayer,
 {
   int goodness;
   struct terrain *old_terrain = tile_terrain(ptile);
+  struct resource *old_resource = tile_resource(ptile);
   bv_special old_special = ptile->special;
   struct terrain *new_terrain = old_terrain->irrigation_result;
 
@@ -318,9 +319,11 @@ static int ai_calc_irrigate(struct city *pcity, struct player *pplayer,
       return -1;
     }
     tile_change_terrain(ptile, new_terrain);
+    tile_convert_resource(ptile);
     tile_clear_special(ptile, S_MINE);
     goodness = city_tile_value(pcity, ptile, 0, 0);
     tile_set_terrain(ptile, old_terrain);
+    tile_set_resource(ptile, old_resource);
     ptile->special = old_special;
     return goodness;
   } else if (old_terrain == new_terrain
@@ -372,6 +375,7 @@ static int ai_calc_mine(struct city *pcity,
 {
   int goodness;
   struct terrain *old_terrain = tile_terrain(ptile);
+  struct resource *old_resource = tile_resource(ptile);
   bv_special old_special = ptile->special;
   struct terrain *new_terrain = old_terrain->mining_result;
 
@@ -382,10 +386,12 @@ static int ai_calc_mine(struct city *pcity,
       return -1;
     }
     tile_change_terrain(ptile, new_terrain);
+    tile_convert_resource(ptile);
     tile_clear_special(ptile, S_IRRIGATION);
     tile_clear_special(ptile, S_FARMLAND);
     goodness = city_tile_value(pcity, ptile, 0, 0);
     tile_set_terrain(ptile, old_terrain);
+    tile_set_resource(ptile, old_resource);
     ptile->special = old_special;
     return goodness;
   } else if (old_terrain == new_terrain
@@ -399,6 +405,7 @@ static int ai_calc_mine(struct city *pcity,
     goodness = city_tile_value(pcity, ptile, 0, 0);
     ptile->special = old_special;
     assert(tile_terrain(ptile) == old_terrain);
+    assert(tile_resource(ptile) == old_resource);
     return goodness;
   } else {
     return -1;
@@ -423,6 +430,7 @@ static int ai_calc_transform(struct city *pcity,
 {
   int goodness;
   struct terrain *old_terrain = tile_terrain(ptile);
+  struct resource *old_resource = tile_resource(ptile);
   bv_special old_special = ptile->special;
   struct terrain *new_terrain = old_terrain->transform_result;
 
@@ -446,9 +454,11 @@ static int ai_calc_transform(struct city *pcity,
   }
 
   tile_change_terrain(ptile, new_terrain);
+  tile_convert_resource(ptile);
   goodness = city_tile_value(pcity, ptile, 0, 0);
 
   tile_set_terrain(ptile, old_terrain);
+  tile_set_resource(ptile, old_resource);
   ptile->special = old_special;
 
   return goodness;
@@ -1189,6 +1199,7 @@ void initialize_infrastructure_cache(struct player *pplayer)
     city_tile_iterate_cxy(pcenter, ptile, city_x, city_y) {
 #ifndef NDEBUG
       struct terrain *old_terrain = tile_terrain(ptile);
+      struct resource *old_resource = tile_resource(ptile);
       bv_special old_special = ptile->special;
 #endif
 
@@ -1213,6 +1224,7 @@ void initialize_infrastructure_cache(struct player *pplayer)
 
       /* Make sure nothing was accidentally changed by these calculations. */
       assert(old_terrain == tile_terrain(ptile)
+             && old_resource == tile_resource(ptile)
 	     && memcmp(&ptile->special, &old_special,
 		       sizeof(old_special)) == 0);
     } city_tile_iterate_cxy_end;
-- 
1.5.5.1

>From 199cefd0bd12f9ec038605df0a21d99a18608a2f Mon Sep 17 00:00:00 2001
From: Madeline Book <[EMAIL PROTECTED]>
Date: Mon, 7 Jul 2008 16:40:36 -0400
Subject: [PATCH] Restore civ2/freeciv terrain resource conversion.

---
 client/packhand.c            |   10 +++++++
 common/terrain.c             |   61 ++++++++++++++++++++++++++++++++++++++++++
 common/terrain.h             |    8 +++++
 common/tile.c                |   56 ++++++++++++++++++++++++++++++++++----
 common/tile.h                |    1 +
 data/civ2/terrain.ruleset    |   13 +++++++--
 data/default/terrain.ruleset |   22 ++++++++++++---
 server/generator/startpos.c  |    4 +++
 server/maphand.c             |    2 +
 server/ruleset.c             |   11 +++++++
 server/settlers.c            |   12 ++++++++
 11 files changed, 187 insertions(+), 13 deletions(-)

diff --git a/client/packhand.c b/client/packhand.c
index 9b7deb9..29284f3 100644
--- a/client/packhand.c
+++ b/client/packhand.c
@@ -2210,6 +2210,12 @@ void handle_ruleset_control(struct packet_ruleset_control *packet)
       popup_tileset_suggestion_dialog();
     }
   }
+
+  /* Resource "tier" is set in handle_ruleset_terrain.
+   * See resource_get_tier() for explanation. */
+  resource_type_iterate(presource) {
+    presource->tier = -1;
+  } resource_type_iterate_end;
 }
 
 /**************************************************************************
@@ -2479,7 +2485,11 @@ void handle_ruleset_terrain(struct packet_ruleset_terrain *p)
               p->resources[j],
               terrain_rule_name(pterrain));
     }
+    if (pterrain->resources[j] && pterrain->resources[j]->tier == -1) {
+      pterrain->resources[j]->tier = j;
+    }
   }
+  pterrain->num_resources = p->num_resources;
   pterrain->resources[p->num_resources] = NULL;
 
   pterrain->road_time = p->road_time;
diff --git a/common/terrain.c b/common/terrain.c
index ca6f0db..52a0709 100644
--- a/common/terrain.c
+++ b/common/terrain.c
@@ -256,6 +256,31 @@ struct resource *resource_by_number(Resource_type_id type)
 }
 
 /****************************************************************************
+  Returns the "tier" for the given resource. This value corresponds to
+  the offset of the resource name in a terrain's 'resources' field as
+  listed in the terrain ruleset file. If a resource is listed in more
+  than one 'resources' list, then the tier value will correspond only
+  to the first one.
+
+  The tier value is primarily intended to be used to implement civ2
+  style resource conversion. The tier value thus corresponds to whether
+  the given resource was a "special1" or a "special2" in previous
+  terrain ruleset definitions. Generally, resources on the same
+  tier will convert to each other when terrain is changed.
+
+  Returns an integer greater than or equal to zero indicating the
+  tier, or -1 on invalid input or if not applicable (e.g. resource
+  is never listed in a terrain entry in the ruleset).
+****************************************************************************/
+int resource_get_tier(const struct resource *presource)
+{
+  if (!presource) {
+    return -1;
+  }
+  return presource->tier;
+}
+
+/****************************************************************************
   Return the resource type matching the name, or T_UNKNOWN if none matches.
 ****************************************************************************/
 struct resource *find_resource_by_rule_name(const char *name)
@@ -272,6 +297,42 @@ struct resource *find_resource_by_rule_name(const char *name)
 }
 
 /****************************************************************************
+  Returns the resource allowed to exist on the given terrain (according to
+  the terrain ruleset) and having "tier" equal to the given value.
+  See resource_get_tier().
+
+  May return NULL if no suitable resource type is found.
+****************************************************************************/
+struct resource *find_resource_by_tier(const struct terrain *pterrain,
+                                       int tier)
+{
+  if (!pterrain || !pterrain->resources || tier < 0
+      || pterrain->num_resources <= tier) {
+    return NULL;
+  }
+
+  return pterrain->resources[tier];
+}
+
+/****************************************************************************
+  Returns the resource having the same tier as the given resource on the
+  new terrain. See resource_get_tier() for an explanation of "tier".
+  
+  May return NULL.
+****************************************************************************/
+struct resource *resource_convert_by_tier(const struct resource *oldres,
+                                          const struct terrain *newter)
+{
+  int tier;
+  struct resource *newres;
+
+  tier = resource_get_tier(oldres);
+  newres = find_resource_by_tier(newter, tier);
+
+  return newres;
+}
+
+/****************************************************************************
   Return the (translated) name of the resource.
   You don't have to free the return pointer.
 ****************************************************************************/
diff --git a/common/terrain.h b/common/terrain.h
index 7c0913f..f0b9417 100644
--- a/common/terrain.h
+++ b/common/terrain.h
@@ -129,6 +129,7 @@ struct terrain {
   int output[O_MAX];
 
   struct resource **resources; /* NULL-terminated */
+  int num_resources; /* Length of above array. */
 
   int road_trade_incr;
   int road_time;
@@ -174,6 +175,7 @@ struct resource {
   char graphic_alt[MAX_LEN_NAME];
   char identifier; /* server-only, same as terrain->identifier */
   int output[O_MAX]; /* Amount added by this resource. */
+  int tier; /* See resource_get_tier(). */
 };
 
 #define RESOURCE_NULL_IDENTIFIER ' '
@@ -205,9 +207,15 @@ void terrains_free(void);
 /* General resource accessor functions. */
 struct resource *resource_by_number(Resource_type_id id);
 struct resource *find_resource_by_rule_name(const char *name);
+struct resource *find_resource_by_tier(const struct terrain *pterrain,
+                                       int tier);
 
 const char *resource_rule_name(const struct resource *presource);
 const char *resource_name_translation(struct resource *presource);
+int resource_get_tier(const struct resource *presource);
+
+struct resource *resource_convert_by_tier(const struct resource *oldres,
+                                          const struct terrain *newter);
 
 /* Functions to operate on a general terrain type. */
 bool is_terrain_near_tile(const struct tile *ptile,
diff --git a/common/tile.c b/common/tile.c
index cecb0a6..d250342 100644
--- a/common/tile.c
+++ b/common/tile.c
@@ -224,9 +224,45 @@ static void tile_clear_dirtiness(struct tile *ptile)
 }
 
 /****************************************************************************
+  This function handles converting (or not) of terrain resources according
+  to the game rules after a tile's terrain changes due to game actions.
+  It assumes the given tile's terrain has already been updated to the new
+  type (but not its resource).
+
+  Currently, resources are converted according to civ2 style rules;
+  see resource_convert_by_tier().
+
+  NB: May modify the tile's resource to the new resource type, including
+  setting it to NULL (i.e. no resource).
+****************************************************************************/
+void tile_convert_resource(struct tile *ptile)
+{
+  const struct terrain *newter;
+  const struct resource *oldres;
+  struct resource *newres;
+
+  if (!ptile) {
+    return;
+  }
+
+  oldres = tile_get_resource(ptile);
+  if (!oldres) {
+    return;
+  }
+
+  newter = tile_get_terrain(ptile);
+  newres = resource_convert_by_tier(oldres, newter);
+
+  tile_set_resource(ptile, newres);
+}
+
+/****************************************************************************
   Change the terrain to the given type.  This does secondary tile updates to
   the tile (as will happen when mining/irrigation/transforming changes the
   tile's terrain).
+  
+  You should generally call tile_convert_resource after changing the tile's
+  terrain with this function.
 ****************************************************************************/
 void tile_change_terrain(struct tile *ptile, struct terrain *pterrain)
 {
@@ -319,8 +355,9 @@ void tile_remove_special(struct tile *ptile, enum tile_special_type special)
 }
 
 /****************************************************************************
-  Build irrigation on the tile.  This may change the specials of the tile
-  or change the terrain type itself.
+  Build irrigation on the tile.
+
+  NB: Potentially modifies the tile's terrain, specials and resource!
 ****************************************************************************/
 static void tile_irrigate(struct tile *ptile)
 {
@@ -332,12 +369,14 @@ static void tile_irrigate(struct tile *ptile)
     }
   } else if (ptile->terrain->irrigation_result) {
     tile_change_terrain(ptile, ptile->terrain->irrigation_result);
+    tile_convert_resource(ptile);
   }
 }
 
 /****************************************************************************
-  Build a mine on the tile.  This may change the specials of the tile
-  or change the terrain type itself.
+  Build a mine on the tile.
+
+  NB: Potentially modifies the tile's terrain, specials and resource!
 ****************************************************************************/
 static void tile_mine(struct tile *ptile)
 {
@@ -347,17 +386,20 @@ static void tile_mine(struct tile *ptile)
     tile_clear_special(ptile, S_IRRIGATION);
   } else if (ptile->terrain->mining_result) {
     tile_change_terrain(ptile, ptile->terrain->mining_result);
+    tile_convert_resource(ptile);
   }
 }
 
 /****************************************************************************
-  Transform (ACTIVITY_TRANSFORM) the tile.  This usually changes the tile's
-  terrain type.
+  Transform (ACTIVITY_TRANSFORM) the tile.
+
+  NB: Potentially modifies the tile's terrain, specials and resource!
 ****************************************************************************/
 static void tile_transform(struct tile *ptile)
 {
   if (ptile->terrain->transform_result != T_NONE) {
     tile_change_terrain(ptile, ptile->terrain->transform_result);
+    tile_convert_resource(ptile);
   }
 }
 
@@ -365,6 +407,8 @@ static void tile_transform(struct tile *ptile)
   Apply an activity (Activity_type_id, e.g., ACTIVITY_TRANSFORM) to a tile.
   Return false if there was a error or if the activity is not implemented
   by this function.
+
+  NB: Potentially modifies the tile's terrain, specials and resource!
 ****************************************************************************/
 bool tile_apply_activity(struct tile *ptile, Activity_type_id act) 
 {
diff --git a/common/tile.h b/common/tile.h
index 4dcb4f2..f97c127 100644
--- a/common/tile.h
+++ b/common/tile.h
@@ -92,6 +92,7 @@ int tile_activity_time(enum unit_activity activity,
 
 /* These are higher-level functions that handle side effects on the tile. */
 void tile_change_terrain(struct tile *ptile, struct terrain *pterrain);
+void tile_convert_resource(struct tile *ptile);
 void tile_add_special(struct tile *ptile, enum tile_special_type special);
 void tile_remove_special(struct tile *ptile, enum tile_special_type special);
 bool tile_apply_activity(struct tile *ptile, Activity_type_id act);
diff --git a/data/civ2/terrain.ruleset b/data/civ2/terrain.ruleset
index 8723e88..8355542 100644
--- a/data/civ2/terrain.ruleset
+++ b/data/civ2/terrain.ruleset
@@ -267,7 +267,7 @@ defense_bonus        = 0
 food                 = 2
 shield               = 0
 trade                = 0
-resources            = "Resources"
+resources            = "Animal Resources", "Plant Resources"
 road_trade_incr      = 1
 road_time            = 2
 irrigation_result    = "yes"
@@ -563,13 +563,20 @@ graphic_alt = "-"
 trade       = 3
 identifier  = "s"
 
-[resource_resources]
-name        = _("Resources")
+[resource_animal_resources]
+name        = _("Animal Resources")
 graphic     = "ts.grassland_resources"
 graphic_alt = "-"
 shield      = 1
 identifier  = "r"
 
+[resource_plant_resources]
+name        = _("Plant Resources")
+graphic     = "ts.grassland_resources"
+graphic_alt = "-"
+shield      = 1
+identifier  = "R"
+
 [resource_coal]
 name        = _("Coal")
 graphic     = "ts.coal"
diff --git a/data/default/terrain.ruleset b/data/default/terrain.ruleset
index 093c5ae..e356a54 100644
--- a/data/default/terrain.ruleset
+++ b/data/default/terrain.ruleset
@@ -226,7 +226,7 @@ defense_bonus        = 0
 food                 = 0
 shield               = 1
 trade                = 0
-resources            = "Oasis", "Oil"
+resources            = "Oasis", "Petroleum"
 road_trade_incr      = 1
 road_time            = 2
 irrigation_result    = "yes"
@@ -302,7 +302,7 @@ defense_bonus        = 0
 food                 = 2
 shield               = 0
 trade                = 0
-resources            = "Resources"
+resources            = "Animal Resources", "Plant Resources"
 road_trade_incr      = 1
 road_time            = 2
 irrigation_result    = "yes"
@@ -611,6 +611,13 @@ graphic_alt = "-"
 shield      = 3
 identifier  = "x"
 
+[resource_petroleum]
+name        = _("Petroleum")
+graphic     = "ts.oil"
+graphic_alt = "-"
+shield      = 3
+identifier  = "P"
+
 [resource_oasis]
 name        = _("Oasis")
 graphic     = "ts.oasis"
@@ -632,13 +639,20 @@ graphic_alt = "-"
 trade       = 3
 identifier  = "s"
 
-[resource_resources]
-name        = _("Resources")
+[resource_animal_resources]
+name        = _("Animal Resources")
 graphic     = "ts.grassland_resources"
 graphic_alt = "-"
 shield      = 1
 identifier  = "r"
 
+[resource_plant_resources]
+name        = _("Plant Resources")
+graphic     = "ts.grassland_resources"
+graphic_alt = "-"
+shield      = 1
+identifier  = "R"
+
 [resource_coal]
 name        = _("Coal")
 graphic     = "ts.coal"
diff --git a/server/generator/startpos.c b/server/generator/startpos.c
index ad1e62f..99522e9 100644
--- a/server/generator/startpos.c
+++ b/server/generator/startpos.c
@@ -43,6 +43,7 @@ static int *islands_index;
 static int get_tile_value(struct tile *ptile)
 {
   struct terrain *old_terrain;
+  struct resource *old_resource;
   bv_special old_special;
   int value, irrig_bonus, mine_bonus;
 
@@ -54,6 +55,7 @@ static int get_tile_value(struct tile *ptile)
 
   old_terrain = ptile->terrain;
   old_special = ptile->special;
+  old_resource = ptile->resource;
 
   tile_set_special(ptile, S_ROAD);
   tile_apply_activity(ptile, ACTIVITY_IRRIGATE);
@@ -64,6 +66,7 @@ static int get_tile_value(struct tile *ptile)
 
   ptile->terrain = old_terrain;
   ptile->special = old_special;
+  ptile->resource = old_resource;
   tile_set_special(ptile, S_ROAD);
   tile_apply_activity(ptile, ACTIVITY_MINE);
   mine_bonus = -value;
@@ -73,6 +76,7 @@ static int get_tile_value(struct tile *ptile)
 
   ptile->terrain = old_terrain;
   ptile->special = old_special;
+  ptile->resource = old_resource;
 
   value += MAX(0, MAX(mine_bonus, irrig_bonus)) / 2;
 
diff --git a/server/maphand.c b/server/maphand.c
index 6058d30..b885c95 100644
--- a/server/maphand.c
+++ b/server/maphand.c
@@ -235,6 +235,7 @@ void global_warming(int effect)
     if (new != T_NONE && old != new) {
       effect--;
       tile_change_terrain(ptile, new);
+      tile_convert_resource(ptile);
       check_terrain_change(ptile, old);
       update_tile_knowledge(ptile);
       unit_list_iterate(ptile->units, punit) {
@@ -279,6 +280,7 @@ void nuclear_winter(int effect)
     if (new != T_NONE && old != new) {
       effect--;
       tile_change_terrain(ptile, new);
+      tile_convert_resource(ptile);
       check_terrain_change(ptile, old);
       update_tile_knowledge(ptile);
       unit_list_iterate(ptile->units, punit) {
diff --git a/server/ruleset.c b/server/ruleset.c
index d614b9a..8972d11 100644
--- a/server/ruleset.c
+++ b/server/ruleset.c
@@ -1546,6 +1546,13 @@ static void load_ruleset_terrain(struct section_file *file)
 
   tsec = secfile_get_secnames_prefix(file, TERRAIN_SECTION_PREFIX, &nval);
 
+  /* Reset resource tiers here, since they will be set
+   * below when loading terrains' resource lists.
+   * See resource_get_tier() for tier explanation. */
+  resource_type_iterate(presource) {
+    presource->tier = -1;
+  } resource_type_iterate_end;
+
   /* terrain details */
 
   terrain_type_iterate(pterrain) {
@@ -1600,8 +1607,12 @@ static void load_ruleset_terrain(struct section_file *file)
 		filename, tsec[i], res[j]);
 	exit(EXIT_FAILURE);
       }
+      if (pterrain->resources[j]->tier == -1) {
+        pterrain->resources[j]->tier = j;
+      }
     }
     pterrain->resources[nval] = NULL;
+    pterrain->num_resources = nval;
     free(res);
     res = NULL;
 
diff --git a/server/settlers.c b/server/settlers.c
index b9add20..af891a2 100644
--- a/server/settlers.c
+++ b/server/settlers.c
@@ -305,6 +305,7 @@ static int ai_calc_irrigate(struct city *pcity, struct player *pplayer,
 {
   int goodness;
   struct terrain *old_terrain = ptile->terrain;
+  struct resource *old_resource = ptile->resource;
   bv_special old_special = ptile->special;
   struct terrain *new_terrain = old_terrain->irrigation_result;
 
@@ -315,10 +316,12 @@ static int ai_calc_irrigate(struct city *pcity, struct player *pplayer,
       return -1;
     }
     tile_change_terrain(ptile, new_terrain);
+    tile_convert_resource(ptile);
     tile_clear_special(ptile, S_MINE);
     goodness = city_tile_value(pcity, city_x, city_y, 0, 0);
     ptile->terrain = old_terrain;
     ptile->special = old_special;
+    ptile->resource = old_resource;
     return goodness;
   } else if (old_terrain == new_terrain
 	     && !tile_has_special(ptile, S_IRRIGATION)
@@ -369,6 +372,7 @@ static int ai_calc_mine(struct city *pcity,
 {
   int goodness;
   struct terrain *old_terrain = ptile->terrain;
+  struct resource *old_resource = ptile->resource;
   bv_special old_special = ptile->special;
   struct terrain *new_terrain = old_terrain->mining_result;
 
@@ -379,11 +383,13 @@ static int ai_calc_mine(struct city *pcity,
       return -1;
     }
     tile_change_terrain(ptile, new_terrain);
+    tile_convert_resource(ptile);
     tile_clear_special(ptile, S_IRRIGATION);
     tile_clear_special(ptile, S_FARMLAND);
     goodness = city_tile_value(pcity, city_x, city_y, 0, 0);
     ptile->terrain = old_terrain;
     ptile->special = old_special;
+    ptile->resource = old_resource;
     return goodness;
   } else if (old_terrain == new_terrain
 	     && !tile_has_special(ptile, S_MINE)) {
@@ -396,6 +402,7 @@ static int ai_calc_mine(struct city *pcity,
     goodness = city_tile_value(pcity, city_x, city_y, 0, 0);
     ptile->special = old_special;
     assert(ptile->terrain == old_terrain);
+    assert(ptile->resource == old_resource);
     return goodness;
   } else {
     return -1;
@@ -420,6 +427,7 @@ static int ai_calc_transform(struct city *pcity,
 {
   int goodness;
   struct terrain *old_terrain = ptile->terrain;
+  struct resource *old_resource = ptile->resource;
   bv_special old_special = ptile->special;
   struct terrain *new_terrain = old_terrain->transform_result;
 
@@ -443,10 +451,12 @@ static int ai_calc_transform(struct city *pcity,
   }
 
   tile_change_terrain(ptile, new_terrain);
+  tile_convert_resource(ptile);
   goodness = city_tile_value(pcity, city_x, city_y, 0, 0);
 
   ptile->terrain = old_terrain;
   ptile->special = old_special;
+  ptile->resource = old_resource;
 
   return goodness;
 }
@@ -1203,6 +1213,7 @@ void initialize_infrastructure_cache(struct player *pplayer)
 			     city_x, city_y, ptile) {
 #ifndef NDEBUG
       struct terrain *old_terrain = ptile->terrain;
+      struct resource *old_resource = ptile->resource;
       bv_special old_special = ptile->special;
 #endif
 
@@ -1227,6 +1238,7 @@ void initialize_infrastructure_cache(struct player *pplayer)
 
       /* Make sure nothing was accidentally changed by these calculations. */
       assert(old_terrain == ptile->terrain
+             && old_resource == ptile->resource
 	     && memcmp(&ptile->special, &old_special,
 		       sizeof(old_special)) == 0);
     } city_map_checked_iterate_end;
-- 
1.5.5.1

_______________________________________________
Freeciv-dev mailing list
Freeciv-dev@gna.org
https://mail.gna.org/listinfo/freeciv-dev

Reply via email to