Complete.Org: Mailing Lists: Archives: freeciv-dev: December 2004:
[Freeciv-Dev] (PR#11539) requirements, part 2
Home

[Freeciv-Dev] (PR#11539) requirements, part 2

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
Subject: [Freeciv-Dev] (PR#11539) requirements, part 2
From: "Jason Short" <jdorje@xxxxxxxxxxxxxxxxxxxxx>
Date: Wed, 15 Dec 2004 13:04:59 -0800
Reply-to: bugs@xxxxxxxxxxx

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

This patch simply moves more code into the requirement structure and files.

Added to the struct requirement are two fields: the range and survives 
values.  Each of these are unique to a specific requirement.  Just as an 
example, Nuclear Missiles should have as a requirement {"Building", 
"Manhattan...", "survives", "World"}.

Mostly this patch just moves code around and renames some of it.

-jason

Index: ai/aicity.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aicity.c,v
retrieving revision 1.183
diff -u -r1.183 aicity.c
--- ai/aicity.c 11 Dec 2004 10:34:45 -0000      1.183
+++ ai/aicity.c 15 Dec 2004 20:50:41 -0000
@@ -59,11 +59,11 @@
 {                                                                   \
   Continent_id continent = map_get_continent(city_here->tile);     \
   city_list_iterate(list, city) {                                   \
-    if ((range == EFR_CITY && city == city_here)                    \
-        || (range == EFR_LOCAL && city == city_here)                \
-        || (range == EFR_CONTINENT                                  \
+    if ((range == RQR_CITY && city == city_here)                    \
+        || (range == RQR_LOCAL && city == city_here)                \
+        || (range == RQR_CONTINENT                                  \
             && map_get_continent(city->tile) == continent)         \
-        || (range == EFR_PLAYER)) {
+        || (range == RQR_PLAYER)) {
 #define city_range_iterate_end \
   } } city_list_iterate_end; }
 
@@ -228,7 +228,7 @@
   struct player *pplayer = city_owner(pcity);
   struct impr_type *pimpr = get_improvement_type(id);
   int v = 0;
-  int cities[EFR_LAST];
+  int cities[RQR_LAST];
   int nplayers = game.nplayers
                  - game.nbarbarians
                  - team_count_members_alive(pplayer->team);
@@ -245,13 +245,13 @@
   }
 
   /* Find number of cities per range.  */
-  cities[EFR_PLAYER] = city_list_size(&pplayer->cities);
-  cities[EFR_WORLD] = cities[EFR_PLAYER]; /* kludge. */
+  cities[RQR_PLAYER] = city_list_size(&pplayer->cities);
+  cities[RQR_WORLD] = cities[RQR_PLAYER]; /* kludge. */
 
-  cities[EFR_CONTINENT] = ai->stats.cities[ptile->continent];
+  cities[RQR_CONTINENT] = ai->stats.cities[ptile->continent];
 
-  cities[EFR_CITY] = 1;
-  cities[EFR_LOCAL] = 0;
+  cities[RQR_CITY] = 1;
+  cities[RQR_LOCAL] = 0;
 
   /* Calculate desire value. */
   effect_type_vector_iterate(get_building_effect_types(id), ptype) {
Index: ai/aidata.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aidata.h,v
retrieving revision 1.20
diff -u -r1.20 aidata.h
--- ai/aidata.h 15 Dec 2004 03:59:21 -0000      1.20
+++ ai/aidata.h 15 Dec 2004 20:50:41 -0000
@@ -58,7 +58,7 @@
 struct ai_data {
   /* Precalculated info about city improvements */
   enum ai_improvement_status impr_calc[MAX_NUM_ITEMS];
-  enum effect_range impr_range[MAX_NUM_ITEMS];
+  enum req_range impr_range[MAX_NUM_ITEMS];
 
   /* AI diplomacy and opinions on other players */
   struct {
Index: client/packhand.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/packhand.c,v
retrieving revision 1.448
diff -u -r1.448 packhand.c
--- client/packhand.c   15 Dec 2004 19:28:43 -0000      1.448
+++ client/packhand.c   15 Dec 2004 20:50:42 -0000
@@ -1380,8 +1380,8 @@
 /* Only add in the improvement if it's in a "foreign" (i.e. unknown) city
    and has equiv_range==World - otherwise we deal with it in its home
    city anyway */
-    if (is_wonder(i) && improvement_types[i].equiv_range==EFR_WORLD &&
-        !find_city_by_id(game.global_wonders[i])) {
+    if (is_wonder(i) && improvement_types[i].equiv_range == RQR_WORLD
+        && !find_city_by_id(game.global_wonders[i])) {
       if (game.global_wonders[i] <= 0 && game.improvements[i] != I_NONE) {
         game.improvements[i] = I_NONE;
         need_effect_update = TRUE;
@@ -2879,13 +2879,8 @@
 **************************************************************************/
 void handle_ruleset_cache_effect(struct packet_ruleset_cache_effect *packet)
 {
-  struct requirement req;
-
-  req.type = packet->req_type;
-  if (req.type < 0 || req.type >= REQ_LAST) {
-    req.type = REQ_NONE;
-  }
-  req_set_value(&req, packet->req_value);
+  struct requirement req = req_from_values(packet->req_type, RQR_CITY,
+                                          FALSE, packet->req_value);
 
   ruleset_cache_add(packet->id, packet->effect_type, packet->range,
                    packet->survives, packet->eff_value,
Index: common/effects.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/effects.c,v
retrieving revision 1.18
diff -u -r1.18 effects.c
--- common/effects.c    15 Dec 2004 19:28:44 -0000      1.18
+++ common/effects.c    15 Dec 2004 20:50:42 -0000
@@ -34,17 +34,6 @@
 #include "player.h"
 #include "tech.h"
  
-/* Names of effect ranges.
- * (These must correspond to enum effect_range_id in effects.h.)
- * do not change these unless you know what you're doing! */
-static const char *effect_range_names[EFR_LAST] = {
-  "Local",
-  "City",
-  "Continent",
-  "Player",
-  "World"
-};
-
 /* Names of effect types.
  * (These must correspond to enum effect_type_id in effects.h.) */
 static const char *effect_type_names[EFT_LAST] = {
@@ -132,42 +121,6 @@
 };
 
 /**************************************************************************
-  Convert an effect range names to an enumerated value.  This is the
-  inverse of effect_range_name.
-
-  The check is case insensitive and returns EFR_LAST if no match is found.
-**************************************************************************/
-enum effect_range effect_range_from_str(const char *str)
-{
-  enum effect_range effect_range;
-
-  assert(ARRAY_SIZE(effect_range_names) == EFR_LAST);
-
-  for (effect_range = 0; effect_range < EFR_LAST; effect_range++) {
-    if (0 == mystrcasecmp(effect_range_names[effect_range], str)) {
-      return effect_range;
-    }
-  }
-
-  return EFR_LAST;
-}
-
-/**************************************************************************
-  Return the name for an effect range enumeration.  Returns NULL if the
-  effect_range is invalid.  This is the inverse of effect_range_from_str.
-**************************************************************************/
-const char *effect_range_name(enum effect_range effect_range)
-{
-  assert(ARRAY_SIZE(effect_range_names) == EFR_LAST);
-  if (effect_range >= 0 && effect_range < EFR_LAST) {
-    return effect_range_names[effect_range];
-  } else {
-    assert(0);
-    return NULL;
-  }
-}
-
-/**************************************************************************
   Convert effect type names to enum; case insensitive;
   returns EFT_LAST if can't match.
 **************************************************************************/
@@ -276,7 +229,7 @@
  */
 struct effect_group_element {
   Impr_Type_id source_building;
-  enum effect_range range;
+  enum req_range range;
   bool survives;
 };
 
@@ -375,7 +328,7 @@
 **************************************************************************/
 void effect_group_add_element(struct effect_group *group,
                              Impr_Type_id source_building,
-                             enum effect_range range, bool survives)
+                             enum req_range range, bool survives)
 {
   struct effect_group_element *elt;
 
@@ -461,7 +414,7 @@
   Add effect to ruleset cache.
 **************************************************************************/
 void ruleset_cache_add(Impr_Type_id source, enum effect_type effect_type,
-                      enum effect_range range, bool survives, int eff_value,
+                      enum req_range range, bool survives, int eff_value,
                       struct requirement *req,
                       int group_id)
 {
@@ -565,6 +518,9 @@
 
     building_vector_iterate(get_buildings_with_effect(effect_type),
                            building) {
+      int dummy_type, dummy_range;
+      bool dummy_survives;
+
       packet.id = *building;
 
       effect_list_iterate(*get_building_effects(*building, effect_type),
@@ -580,7 +536,8 @@
          packet.group_id = -1;
        }
 
-       packet.req_value = req_get_value(&peffect->req);
+       req_get_values(&peffect->req, &dummy_type,
+                      &dummy_range, &dummy_survives, &packet.req_value);
 
        lsend_packet_ruleset_cache_effect(dest, &packet);
       } effect_list_iterate_end;
@@ -643,178 +600,6 @@
 }
 
 /**************************************************************************
-  Returns the number of total world buildings (this includes buildings
-  that have been destroyed).
-**************************************************************************/
-static int num_world_buildings_total(Impr_Type_id building)
-{
-  if (is_wonder(building)) {
-    return (game.global_wonders[building] != 0) ? 1 : 0;
-  } else {
-    freelog(LOG_ERROR,
-           /* TRANS: Obscure ruleset error. */
-           _("World-ranged effects are only supported for wonders."));
-    return 0;
-  }
-}
-
-/**************************************************************************
-  Returns the number of buildings of a certain type in the world.
-**************************************************************************/
-static int num_world_buildings(Impr_Type_id id)
-{
-  if (is_wonder(id)) {
-    return find_city_by_id(game.global_wonders[id]) ? 1 : 0;
-  } else {
-    freelog(LOG_ERROR,
-           /* TRANS: Obscure ruleset error. */
-           _("World-ranged effects are only supported for wonders."));
-    return 0;
-  }
-}
-
-/**************************************************************************
-  Returns the number of buildings of a certain type owned by plr.
-**************************************************************************/
-static int num_player_buildings(const struct player *pplayer,
-                               Impr_Type_id building)
-{
-  if (is_wonder(building)) {
-    if (player_find_city_by_id(pplayer, game.global_wonders[building])) {
-      return 1;
-    } else {
-      return 0;
-    }
-  } else {
-    freelog(LOG_ERROR,
-           /* TRANS: Obscure ruleset error. */
-           _("Player-ranged effects are only supported for wonders."));
-    return 0;
-  }
-}
-
-/**************************************************************************
-  Returns the number of buildings of a certain type on a continent.
-**************************************************************************/
-static int num_continent_buildings(const struct player *pplayer,
-                                  int continent, Impr_Type_id building)
-{
-  if (is_wonder(building)) {
-    const struct city *pcity;
-
-    pcity = player_find_city_by_id(pplayer, game.global_wonders[building]);
-    if (pcity && map_get_continent(pcity->tile) == continent) {
-      return 1;
-    }
-  } else {
-    freelog(LOG_ERROR,
-           /* TRANS: Obscure ruleset error. */
-           _("Island-ranged effects are only supported for wonders."));
-  }
-  return 0;
-}
-
-/**************************************************************************
-  Returns the number of buildings of a certain type in a city.
-**************************************************************************/
-static int num_city_buildings(const struct city *pcity, Impr_Type_id id)
-{
-  return (city_got_building(pcity, id) ? 1 : 0);
-}
-
-/**************************************************************************
-  Is this target possible for the range involved?
-
-  For instance a city-ranged effect can't have a player as it's target.
-  See the comment at enum target_type.
-**************************************************************************/
-static bool is_target_possible(enum target_type target,
-                              enum effect_range range)
-{
-  switch (target) {
-  case TARGET_PLAYER:
-    return (range >= EFR_PLAYER);
-  case TARGET_CITY:
-    return (range >= EFR_CITY);
-  case TARGET_BUILDING:
-    return (range >= EFR_LOCAL);
-  }
-  assert(0);
-  return FALSE;
-}
-
-/**************************************************************************
-  How many of the source building are there within range of the target?
-
-  The target gives the type of the target.  The exact target is a player,
-  city, or building specified by the target_xxx arguments.
-
-  The range gives the range of the effect.
-
-  "Survives" specifies whether the effect is surviving.  If set then all
-  source buildings ever built are counted; if not then only living
-  buildings are counted.
-
-  source gives the building type of the source in question.
-
-  Note that this function does a lookup into the source caches to find
-  the number of available sources.  However not all source caches exist: if
-  the cache doesn't exist then we return 0.
-**************************************************************************/
-int count_sources_in_range(enum target_type target,
-                          const struct player *target_player,
-                          const struct city *target_city,
-                          Impr_Type_id target_building,
-                          enum effect_range range, bool survives,
-                          Impr_Type_id source)
-{
-  if (!is_target_possible(target, range)) {
-    return 0;
-  }
-
-  if (improvement_obsolete(target_player, source)) {
-    return 0;
-  }
-
-  if (survives) {
-    if (range == EFR_WORLD) {
-      return num_world_buildings_total(source);
-    } else {
-      /* There is no sources cache for this. */
-      freelog(LOG_ERROR,
-             /* TRANS: Obscure ruleset error. */
-             _("Surviving effects are only supported at world range."));
-      return 0;
-    }
-  }
-
-  switch (range) {
-  case EFR_WORLD:
-    return num_world_buildings(source);
-  case EFR_PLAYER:
-    return num_player_buildings(target_player, source);
-  case EFR_CONTINENT:
-    {
-      int continent = map_get_continent(target_city->tile);
-
-      return num_continent_buildings(target_player, continent, source);
-    }
-  case EFR_CITY:
-    return num_city_buildings(target_city, source);
-  case EFR_LOCAL:
-    if (target_building == source) {
-      return num_city_buildings(target_city, source);
-    } else {
-      return 0;
-    }
-  case EFR_LAST:
-    break;
-  }
-  assert(0);
-  return 0;
-}
-
-/**************************************************************************
   Is the effect from the source building redundant on the given target
   (i.e. are its effects replaced by other sources in the group)?
 
@@ -841,9 +626,9 @@
     if (elt->source_building == source) {
       return FALSE;
     } else {
-      if (count_sources_in_range(target, target_player, target_city,
-                                target_building, elt->range,
-                                elt->survives, elt->source_building) > 0) {
+      if (count_buildings_in_range(target, target_player, target_city,
+                                  target_building, elt->range,
+                                  elt->survives, elt->source_building) > 0) {
        /* The effect from this source in the group makes peffect
         * redundant.  Note this causes the redundancy even if the
         * elt->source_building has no effects actually in the group! */
@@ -920,8 +705,8 @@
                             Impr_Type_id source,
                             const struct effect *peffect)
 {
-  if (count_sources_in_range(target, plr, pcity, building, peffect->range,
-                            peffect->survives, source) == 0) {
+  if (count_buildings_in_range(target, plr, pcity, building, peffect->range,
+                              peffect->survives, source) == 0) {
     return FALSE;
   }
   return is_effect_useful(target, plr, pcity, building,
Index: common/effects.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/effects.h,v
retrieving revision 1.12
diff -u -r1.12 effects.h
--- common/effects.h    15 Dec 2004 19:28:44 -0000      1.12
+++ common/effects.h    15 Dec 2004 20:50:43 -0000
@@ -21,17 +21,6 @@
 #include "tech.h"
 #include "terrain.h"
 
-/* Range of effects (used in equiv_range and effect.range fields)
- * These must correspond to effect_range_names[] in improvement.c. */
-enum effect_range {
-  EFR_LOCAL,
-  EFR_CITY,
-  EFR_CONTINENT,
-  EFR_PLAYER,
-  EFR_WORLD,
-  EFR_LAST   /* keep this last */
-};
-
 /* Type of effects. (Used in effect.type field)
  * These must correspond to effect_type_names[] in effects.c. */
 enum effect_type {
@@ -120,8 +109,6 @@
 };
 
 /* lookups */
-enum effect_range effect_range_from_str(const char *str);
-const char *effect_range_name(enum effect_range effect_range);
 enum effect_type effect_type_from_str(const char *str);
 const char *effect_type_name(enum effect_type effect_type);
 
@@ -152,7 +139,7 @@
   /* The range the effect applies to, relative to the source.  For instance
    * if the source is a building an effect with range "city" will apply to
    * everything in that city. */
-  enum effect_range range;
+  enum req_range range;
 
   /* The "value" of the effect.  The meaning of this varies between
    * effects.  When get_xxx_bonus() is called the value of all applicable
@@ -196,7 +183,7 @@
 void ruleset_cache_init(void);
 void ruleset_cache_free(void);
 void ruleset_cache_add(Impr_Type_id source, enum effect_type effect_type,
-                      enum effect_range range, bool survives, int eff_value,
+                      enum req_range range, bool survives, int eff_value,
                       struct requirement *req,
                       int group_id);
 void send_ruleset_cache(struct conn_list *dest);
@@ -205,7 +192,7 @@
 struct effect_group *effect_group_new(const char *name);
 void effect_group_add_element(struct effect_group *group,
                              Impr_Type_id source_building,
-                             enum effect_range range, bool survives);
+                             enum req_range range, bool survives);
 int find_effect_group_id(const char *name);
 
 bool is_effect_useful(enum target_type target,
@@ -243,12 +230,6 @@
 Impr_Type_id ai_find_source_building(struct player *pplayer,
                                     enum effect_type effect_type);
 Impr_Type_id get_building_for_effect(enum effect_type effect_type);
-int count_sources_in_range(enum target_type target,
-                          const struct player *target_player,
-                          const struct city *target_city,
-                          Impr_Type_id target_building,
-                          enum effect_range range, bool survives,
-                          Impr_Type_id source);
 
 #endif  /* FC__EFFECTS_H */
 
Index: common/packets.def
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/packets.def,v
retrieving revision 1.77
diff -u -r1.77 packets.def
--- common/packets.def  15 Dec 2004 19:28:44 -0000      1.77
+++ common/packets.def  15 Dec 2004 20:50:45 -0000
@@ -181,10 +181,8 @@
 type ORDERS            = uint8(enum unit_orders)
 type SSET_TYPE         = uint8(enum sset_type)
 type REQ_TYPE          = uint8(enum req_type)
-
-# typedefs for effects
+type REQ_RANGE         = uint8(enum req_range)
 type EFFECT_TYPE       = uint8(enum effect_type)
-type EFFECT_RANGE      = uint8(enum effect_range)
 
 # typedefs for IDs
 type PLAYER            = UINT8
@@ -1271,14 +1269,14 @@
 
   UINT8 num_elements;
   IMPROVEMENT source_buildings[255:num_elements];
-  EFFECT_RANGE ranges[255:num_elements];
+  REQ_RANGE ranges[255:num_elements];
   BOOL survives[255:num_elements];
 end
 
 PACKET_RULESET_CACHE_EFFECT=121;sc,lsend
   IMPROVEMENT id;
   EFFECT_TYPE effect_type;
-  EFFECT_RANGE range;
+  REQ_RANGE range;
   BOOL survives;
   SINT32 eff_value;
   REQ_TYPE req_type;
Index: common/requirements.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/requirements.c,v
retrieving revision 1.1
diff -u -r1.1 requirements.c
--- common/requirements.c       15 Dec 2004 19:28:44 -0000      1.1
+++ common/requirements.c       15 Dec 2004 20:50:45 -0000
@@ -16,6 +16,8 @@
 
 #include <assert.h>
 
+#include "fcintl.h"
+#include "log.h"
 #include "support.h"
 
 #include "government.h"
@@ -23,6 +25,9 @@
 #include "map.h"
 #include "requirements.h"
 
+/* Names of requirement types.
+ * (These must correspond to enum req_type in requirements.h.)
+ * do not change these unless you know what you're doing! */
 static const char *req_type_names[] = {
   "None",
   "Tech",
@@ -32,25 +37,94 @@
   "Terrain"
 };
 
+/* Names of requirement ranges.
+ * (These must correspond to enum req_range in requirements.h.)
+ * do not change these unless you know what you're doing! */
+static const char *req_range_names[RQR_LAST] = {
+  "Local",
+  "City",
+  "Continent",
+  "Player",
+  "World"
+};
+
+/**************************************************************************
+  Convert a range name to an enumerated value.
+
+  The check is case insensitive and returns RQR_LAST if no match is found.
+**************************************************************************/
+enum req_range req_range_from_str(const char *str)
+{
+  enum req_range range;
+
+  assert(ARRAY_SIZE(req_range_names) == RQR_LAST);
+
+  for (range = 0; range < RQR_LAST; range++) {
+    if (0 == mystrcasecmp(req_range_names[range], str)) {
+      return range;
+    }
+  }
+
+  return RQR_LAST;
+}
+
+#if 0
+/**************************************************************************
+  Return the name for an effect range enumeration.  Returns NULL if the
+  effect_range is invalid.  This is the inverse of effect_range_from_str.
+**************************************************************************/
+const char *effect_range_name(enum effect_range effect_range)
+{
+  assert(ARRAY_SIZE(effect_range_names) == EFR_LAST);
+  if (effect_range >= 0 && effect_range < EFR_LAST) {
+    return effect_range_names[effect_range];
+  } else {
+    assert(0);
+    return NULL;
+  }
+}
+#endif
+
 /****************************************************************************
   Parse a requirement type and value string into a requrement structure.
   Returns REQ_LAST on error.
 
   Pass this some values like "Building", "Factory".
 ****************************************************************************/
-struct requirement req_from_str(const char *type, const char *value)
+struct requirement req_from_str(const char *type,
+                               const char *range, bool survives,
+                               const char *value)
 {
   struct requirement req;
   const struct government *pgov;
 
   assert(ARRAY_SIZE(req_type_names) == REQ_LAST);
-
   for (req.type = 0; req.type < ARRAY_SIZE(req_type_names); req.type++) {
     if (0 == mystrcasecmp(req_type_names[req.type], type)) {
       break;
     }
   }
 
+  req.range = req_range_from_str(range);
+  if (req.range == RQR_LAST) {
+    switch (req.type) {
+    case REQ_NONE:
+    case REQ_LAST:
+      break;
+    case REQ_BUILDING:
+    case REQ_SPECIAL:
+    case REQ_TERRAIN:
+      req.range = RQR_CITY;
+      break;
+    case REQ_GOV:
+    case REQ_TECH:
+      req.range = RQR_PLAYER;
+      break;
+    }
+  }
+
+  req.survives = survives;
+
   switch (req.type) {
   case REQ_NONE:
     return req;
@@ -98,58 +172,251 @@
   Return the value of a req as a serializable integer.  This is the opposite
   of req_set_value.
 ****************************************************************************/
-int req_get_value(struct requirement *req)
+void req_get_values(struct requirement *req,
+                   int *type, int *range, bool *survives, int *value)
 {
+  *type = req->type;
+  *range = req->range;
+  *survives = req->survives;
+
   switch (req->type) {
   case REQ_NONE:
-    return 0;
+    *value = 0;
+    return;
   case REQ_TECH:
-    return req->value.tech;
+    *value = req->value.tech;
+    return;
   case REQ_GOV:
-    return req->value.gov;
+    *value = req->value.gov;
+    return;
   case REQ_BUILDING:
-    return req->value.building;
+    *value = req->value.building;
+    return;
   case REQ_SPECIAL:
-    return req->value.special;
+    *value = req->value.special;
+    return;
   case REQ_TERRAIN:
-    return req->value.terrain;
+    *value = req->value.terrain;
+    return;
   case REQ_LAST:
     break;
   }
 
   assert(0);
-  return 0;
+  *value = 0;
 }
 
 /****************************************************************************
   Set the value of a req from a serializable integer.  This is the opposite
   of req_get_value.
 ****************************************************************************/
-void req_set_value(struct requirement *req, int value)
+struct requirement req_from_values(int type, int range,
+                                  bool survives, int value)
 {
-  switch (req->type) {
+  struct requirement req;
+
+  req.type = type;
+  req.range = range;
+  req.survives = survives;
+
+  switch (req.type) {
   case REQ_NONE:
-    return;
+    return req;
   case REQ_TECH:
-    req->value.tech = value;
-    return;
+    req.value.tech = value;
+    return req;
   case REQ_GOV:
-    req->value.gov = value;
-    return;
+    req.value.gov = value;
+    return req;
   case REQ_BUILDING:
-    req->value.building = value;
-    return;
+    req.value.building = value;
+    return req;
   case REQ_SPECIAL:
-    req->value.special = value;
-    return;
+    req.value.special = value;
+    return req;
   case REQ_TERRAIN:
-    req->value.terrain = value;
-    return;
+    req.value.terrain = value;
+    return req;
   case REQ_LAST:
-    return;
+    return req;
+  }
+
+  req.type = REQ_LAST;
+  assert(0);
+  return req;
+}
+
+/**************************************************************************
+  Is this target possible for the range involved?
+
+  For instance a city-ranged effect can't have a player as it's target.
+  See the comment at enum target_type.
+**************************************************************************/
+static bool is_target_possible(enum target_type target,
+                              enum req_range range)
+{
+  switch (target) {
+  case TARGET_PLAYER:
+    return (range >= RQR_PLAYER);
+  case TARGET_CITY:
+    return (range >= RQR_CITY);
+  case TARGET_BUILDING:
+    return (range >= RQR_LOCAL);
+  }
+  assert(0);
+  return FALSE;
+}
+
+/**************************************************************************
+  Returns the number of total world buildings (this includes buildings
+  that have been destroyed).
+**************************************************************************/
+static int num_world_buildings_total(Impr_Type_id building)
+{
+  if (is_wonder(building)) {
+    return (game.global_wonders[building] != 0) ? 1 : 0;
+  } else {
+    freelog(LOG_ERROR,
+           /* TRANS: Obscure ruleset error. */
+           _("World-ranged requirements are only supported for wonders."));
+    return 0;
+  }
+}
+
+/**************************************************************************
+  Returns the number of buildings of a certain type in the world.
+**************************************************************************/
+static int num_world_buildings(Impr_Type_id id)
+{
+  if (is_wonder(id)) {
+    return find_city_by_id(game.global_wonders[id]) ? 1 : 0;
+  } else {
+    freelog(LOG_ERROR,
+           /* TRANS: Obscure ruleset error. */
+           _("World-ranged requirements are only supported for wonders."));
+    return 0;
+  }
+}
+
+/**************************************************************************
+  Returns the number of buildings of a certain type owned by plr.
+**************************************************************************/
+static int num_player_buildings(const struct player *pplayer,
+                               Impr_Type_id building)
+{
+  if (is_wonder(building)) {
+    if (player_find_city_by_id(pplayer, game.global_wonders[building])) {
+      return 1;
+    } else {
+      return 0;
+    }
+  } else {
+    freelog(LOG_ERROR,
+           /* TRANS: Obscure ruleset error. */
+           _("Player-ranged requirements are only supported for wonders."));
+    return 0;
+  }
+}
+
+/**************************************************************************
+  Returns the number of buildings of a certain type on a continent.
+**************************************************************************/
+static int num_continent_buildings(const struct player *pplayer,
+                                  int continent, Impr_Type_id building)
+{
+  if (is_wonder(building)) {
+    const struct city *pcity;
+
+    pcity = player_find_city_by_id(pplayer, game.global_wonders[building]);
+    if (pcity && map_get_continent(pcity->tile) == continent) {
+      return 1;
+    }
+  } else {
+    freelog(LOG_ERROR,
+           /* TRANS: Obscure ruleset error. */
+           _("Island-ranged requirements are only supported for wonders."));
   }
+  return 0;
+}
+
+/**************************************************************************
+  Returns the number of buildings of a certain type in a city.
+**************************************************************************/
+static int num_city_buildings(const struct city *pcity, Impr_Type_id id)
+{
+  return (city_got_building(pcity, id) ? 1 : 0);
+}
+
+/**************************************************************************
+  How many of the source building are there within range of the target?
+
+  The target gives the type of the target.  The exact target is a player,
+  city, or building specified by the target_xxx arguments.
+
+  The range gives the range of the requirement.
 
+  "Survives" specifies whether the requirement allows destroyed sources.
+  If set then all source buildings ever built are counted; if not then only
+  living buildings are counted.
+
+  source gives the building type of the source in question.
+
+  Note that this function does a lookup into the source caches to find
+  the number of available sources.  However not all source caches exist: if
+  the cache doesn't exist then we return 0.
+**************************************************************************/
+int count_buildings_in_range(enum target_type target,
+                            const struct player *target_player,
+                            const struct city *target_city,
+                            Impr_Type_id target_building,
+                            enum req_range range, bool survives,
+                            Impr_Type_id source)
+{
+  if (!is_target_possible(target, range)) {
+    return 0;
+  }
+
+  if (improvement_obsolete(target_player, source)) {
+    return 0;
+  }
+
+  if (survives) {
+    if (range == RQR_WORLD) {
+      return num_world_buildings_total(source);
+    } else {
+      /* There is no sources cache for this. */
+      freelog(LOG_ERROR,
+             /* TRANS: Obscure ruleset error. */
+             _("Surviving requirements are only "
+               "supported at world range."));
+      return 0;
+    }
+  }
+
+  switch (range) {
+  case RQR_WORLD:
+    return num_world_buildings(source);
+  case RQR_PLAYER:
+    return num_player_buildings(target_player, source);
+  case RQR_CONTINENT:
+    {
+      int continent = map_get_continent(target_city->tile);
+
+      return num_continent_buildings(target_player, continent, source);
+    }
+  case RQR_CITY:
+    return num_city_buildings(target_city, source);
+  case RQR_LOCAL:
+    if (target_building == source) {
+      return num_city_buildings(target_city, source);
+    } else {
+      return 0;
+    }
+  case RQR_LAST:
+    break;
+  }
   assert(0);
+  return 0;
 }
 
 /****************************************************************************
@@ -157,8 +424,7 @@
 
   target gives the type of the target
   (player,city,building,tile) give the exact target
-  source gives the source type of the effect
-  peffect gives the exact effect value
+  req gives the requirement itself
 
   Make sure you give all aspects of the target when calling this function:
   for instance if you have TARGET_CITY pass the city's owner as the target
@@ -189,9 +455,9 @@
     /* The requirement is filled if there's at least one of the building
      * in the city.  (This is a slightly nonstandard use of
      * count_sources_in_range.) */
-    return (count_sources_in_range(target, target_player, target_city,
-                                  target_building, EFR_CITY, FALSE,
-                                  req->value.building) > 0);
+    return (count_buildings_in_range(target, target_player, target_city,
+                                    target_building, RQR_CITY, FALSE,
+                                    req->value.building) > 0);
   case REQ_SPECIAL:
     /* The requirement is filled if the tile has the special. */
     return target_tile && tile_has_special(target_tile, req->value.special);
Index: common/requirements.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/requirements.h,v
retrieving revision 1.1
diff -u -r1.1 requirements.h
--- common/requirements.h       15 Dec 2004 19:28:44 -0000      1.1
+++ common/requirements.h       15 Dec 2004 20:50:45 -0000
@@ -17,7 +17,8 @@
 #include "fc_types.h"
 #include "tech.h"
 
-/* The type of a requirement. */
+/* The type of a requirement.  This must correspond to req_type_names[]
+ * in requirements.c. */
 enum req_type {
   REQ_NONE,
   REQ_TECH,
@@ -28,6 +29,17 @@
   REQ_LAST
 };
 
+/* Range of requirements.  This must correspond to req_range_names[]
+ * in requirements.c. */
+enum req_range {
+  RQR_LOCAL,
+  RQR_CITY,
+  RQR_CONTINENT,
+  RQR_PLAYER,
+  RQR_WORLD,
+  RQR_LAST   /* keep this last */
+};
+
 /* An requirement is targeted at a certain target type.  For building and
  * unit reqs the target will be a city; for tech reqs it will be a player;
  * for effect reqs it may be anything. */
@@ -44,6 +56,8 @@
  * active. */
 struct requirement {
   enum req_type type;                  /* requirement type */
+  enum req_range range;                        /* requirement range */
+  bool survives; /* set if destroyed sources satisfy the req*/
 
   union {
     Tech_Type_id tech;                 /* requirement tech */
@@ -54,10 +68,14 @@
   } value;                             /* requirement value */
 };
 
-struct requirement req_from_str(const char *type, const char *value);
-
-int req_get_value(struct requirement *req);
-void req_set_value(struct requirement *req, int value);
+enum req_range req_range_from_str(const char *str);
+struct requirement req_from_str(const char *type, const char *range,
+                               bool survives, const char *value);
+
+void req_get_values(struct requirement *req,
+                   int *type, int *range, bool *survives, int *value);
+struct requirement req_from_values(int type, int range,
+                                  bool survives, int value);
 
 bool is_req_active(enum target_type target,
                   const struct player *target_player,
@@ -66,4 +84,11 @@
                   const struct tile *target_tile,
                   const struct requirement *req);
 
+int count_buildings_in_range(enum target_type target,
+                            const struct player *target_player,
+                            const struct city *target_city,
+                            Impr_Type_id target_building,
+                            enum req_range range, bool survives,
+                            Impr_Type_id source);
+
 #endif  /* FC__REQUIREMENTS_H */
Index: po/POTFILES.in
===================================================================
RCS file: /home/freeciv/CVS/freeciv/po/POTFILES.in,v
retrieving revision 1.81
diff -u -r1.81 POTFILES.in
--- po/POTFILES.in      22 Nov 2004 04:02:07 -0000      1.81
+++ po/POTFILES.in      15 Dec 2004 20:50:45 -0000
@@ -5,6 +5,7 @@
 common/map.c
 common/packets.c
 common/player.c
+common/requirements.c
 common/tech.c
 common/unit.c
 common/unittype.c
Index: server/ruleset.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/ruleset.c,v
retrieving revision 1.217
diff -u -r1.217 ruleset.c
--- server/ruleset.c    15 Dec 2004 19:28:44 -0000      1.217
+++ server/ruleset.c    15 Dec 2004 20:50:46 -0000
@@ -1186,7 +1186,7 @@
                                            sec[i], j));
         j++) {
       Impr_Type_id id;
-      enum effect_range range;
+      enum req_range range;
       bool survives;
 
       if ((id = find_improvement_by_name(item)) == B_LAST) {
@@ -1200,7 +1200,7 @@
       item = secfile_lookup_str_default(file, NULL, "%s.elements%d.range",
                                        sec[i], j);
       if (item) {
-       if ((range = effect_range_from_str(item)) == EFR_LAST) {
+       if ((range = req_range_from_str(item)) == RQR_LAST) {
          freelog(LOG_ERROR,
                  /* TRANS: Obscure ruleset error */
                  _("Group %s lists bad range: \"%s\" (%s)"),
@@ -1208,7 +1208,7 @@
          continue;
        }
       } else {
-       range = EFR_CITY;
+       range = RQR_CITY;
       }
 
       survives
@@ -1333,7 +1333,7 @@
           j++) {
        int value;
        enum effect_type eff;
-       enum effect_range range;
+       enum req_range range;
        bool survives;
        struct requirement req;
        char *req_type, *req_value;
@@ -1350,7 +1350,7 @@
        item = secfile_lookup_str_default(file, NULL, "%s.effect%d.range",
                                          sec[i], j);
        if (item) {
-         if ((range = effect_range_from_str(item)) == EFR_LAST) {
+         if ((range = req_range_from_str(item)) == RQR_LAST) {
            freelog(LOG_ERROR,
                    /* TRANS: Obscure ruleset error */
                    _("Building %s lists bad range: \"%s\" (%s)"),
@@ -1358,7 +1358,7 @@
            continue;
          }
        } else {
-         range = EFR_CITY;
+         range = RQR_CITY;
        }
 
        survives = secfile_lookup_bool_default(file, FALSE, 
"%s.effect%d.survives",
@@ -1387,7 +1387,7 @@
                                              sec[i], j);
        req_value = secfile_lookup_str_default(file, "", "%s.effect%d.req",
                                               sec[i], j);
-       req = req_from_str(req_type, req_value);
+       req = req_from_str(req_type, "", FALSE, req_value);
        if (req.type == REQ_LAST) {
          /* Error.  Log it, clear the req and continue. */
          freelog(LOG_ERROR,

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