Complete.Org: Mailing Lists: Archives: freeciv-dev: December 2004:
[Freeciv-Dev] (PR#11561) requirements for specialists
Home

[Freeciv-Dev] (PR#11561) requirements for specialists

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
Subject: [Freeciv-Dev] (PR#11561) requirements for specialists
From: "Jason Short" <jdorje@xxxxxxxxxxxxxxxxxxxxx>
Date: Thu, 16 Dec 2004 17:50:42 -0800
Reply-to: bugs@xxxxxxxxxxx

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

This patch adds requirements for specialists.  A specialist may have up 
to 2 requirements (tech, building, gov, terrain, special, etc.).

It includes several other patches: req2.diff ("requirements, part 2") 
and a separate bugfix patch.  So it's not ready for inclusion yet.  It 
also lacks any AI support (the AI should increase the want for the 
requirements of good specialists).  And it lacks any helptext support 
(there should be helptexts for specialists...specialists should go into 
a separate specialists.ruleset...etc.)

Also I note that specialists should have their own packet type, with one 
packet per specialist.  The multi-dimensional arrays we use now are just 
too ugly.

And I note that min_size should be another req_type.

Also attached is a demo patch to show this in action.

-jason

Index: ai/aicity.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aicity.c,v
retrieving revision 1.184
diff -u -r1.184 aicity.c
--- ai/aicity.c 16 Dec 2004 19:43:46 -0000      1.184
+++ ai/aicity.c 17 Dec 2004 01:39:52 -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 == REQ_RANGE_CITY && city == city_here)                    \
+        || (range == REQ_RANGE_LOCAL && city == city_here)                \
+        || (range == REQ_RANGE_CONTINENT                                  \
             && map_get_continent(city->tile) == continent)         \
-        || (range == EFR_PLAYER)) {
+        || (range == REQ_RANGE_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[REQ_RANGE_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[REQ_RANGE_PLAYER] = city_list_size(&pplayer->cities);
+  cities[REQ_RANGE_WORLD] = cities[REQ_RANGE_PLAYER]; /* kludge. */
 
-  cities[EFR_CONTINENT] = ai->stats.cities[ptile->continent];
+  cities[REQ_RANGE_CONTINENT] = ai->stats.cities[ptile->continent];
 
-  cities[EFR_CITY] = 1;
-  cities[EFR_LOCAL] = 0;
+  cities[REQ_RANGE_CITY] = 1;
+  cities[REQ_RANGE_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 17 Dec 2004 01:39:52 -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.451
diff -u -r1.451 packhand.c
--- client/packhand.c   17 Dec 2004 00:14:15 -0000      1.451
+++ client/packhand.c   17 Dec 2004 01:39:54 -0000
@@ -1381,8 +1381,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 == REQ_RANGE_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;
@@ -2581,7 +2581,7 @@
   game.rgame.num_specialist_types = packet->num_specialist_types;
   game.rgame.default_specialist = packet->default_specialist;
   specialist_type_iterate(sp) {
-    int *bonus = game.rgame.specialists[sp].bonus;
+    int *bonus = game.rgame.specialists[sp].bonus, j;
 
     sz_strlcpy(game.rgame.specialists[sp].name, packet->specialist_name[sp]);
     sz_strlcpy(game.rgame.specialists[sp].short_name,
@@ -2590,6 +2590,16 @@
     output_type_iterate(o) {
       bonus[o] = packet->specialist_bonus[sp * O_COUNT + o];
     } output_type_iterate_end;
+
+    for (j = 0; j < MAX_NUM_REQS; j++) {
+      int index = sp * MAX_NUM_REQS + j;
+
+      game.rgame.specialists[sp].req[j]
+       = req_from_values(packet->specialist_req_type[index],
+                         packet->specialist_req_range[index],
+                         packet->specialist_req_survives[index],
+                         packet->specialist_req_value[index]);
+    }
   } specialist_type_iterate_end;
   tilespec_setup_specialist_types();
 
@@ -2878,13 +2888,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, REQ_RANGE_CITY,
+                                          FALSE, packet->req_value);
 
   ruleset_cache_add(packet->id, packet->effect_type, packet->range,
                    packet->survives, packet->eff_value,
Index: common/city.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/city.c,v
retrieving revision 1.288
diff -u -r1.288 city.c
--- common/city.c       16 Dec 2004 23:00:12 -0000      1.288
+++ common/city.c       17 Dec 2004 01:39:54 -0000
@@ -530,7 +530,24 @@
 bool city_can_use_specialist(const struct city *pcity,
                             Specialist_type_id type)
 {
-  return pcity->size >= game.rgame.specialists[type].min_size;
+  int i;
+
+  if (pcity->size < game.rgame.specialists[type].min_size) {
+    return FALSE;
+  }
+
+  for (i = 0; i < MAX_NUM_REQS; i++) {
+    struct requirement *req = &game.rgame.specialists[type].req[i];
+
+    if (req->type == REQ_NONE) {
+      break; /* Short-circuit any more checks. */
+    } else if (!is_req_active(TARGET_CITY, city_owner(pcity), pcity,
+                             B_LAST, NULL, req)) {
+      return FALSE;
+    }
+  }
+
+  return TRUE;
 }
 
 /**************************************************************************
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    17 Dec 2004 01:39:54 -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    17 Dec 2004 01:39:54 -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/fc_types.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/fc_types.h,v
retrieving revision 1.12
diff -u -r1.12 fc_types.h
--- common/fc_types.h   16 Dec 2004 20:37:49 -0000      1.12
+++ common/fc_types.h   17 Dec 2004 01:39:54 -0000
@@ -40,5 +40,6 @@
 struct unit;
 
 #define SP_MAX 20
+#define MAX_NUM_REQS 2
 
 #endif /* FC__FC_TYPES_H */
Index: common/game.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/game.h,v
retrieving revision 1.166
diff -u -r1.166 game.h
--- common/game.h       17 Dec 2004 00:14:15 -0000      1.166
+++ common/game.h       17 Dec 2004 01:39:55 -0000
@@ -193,8 +193,9 @@
     struct specialist {
       char name[MAX_LEN_NAME];
       char short_name[MAX_LEN_NAME];
-      int min_size;
       int bonus[O_MAX];
+      int min_size;
+      struct requirement req[MAX_NUM_REQS];
     } specialists[SP_MAX];
 #define SP_COUNT game.rgame.num_specialist_types
 #define DEFAULT_SPECIALIST game.rgame.default_specialist
Index: common/packets.def
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/packets.def,v
retrieving revision 1.80
diff -u -r1.80 packets.def
--- common/packets.def  17 Dec 2004 00:14:15 -0000      1.80
+++ common/packets.def  17 Dec 2004 01:39:55 -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
@@ -973,6 +971,10 @@
   STRING specialist_short_name[SP_MAX][MAX_LEN_NAME];
   UINT8 specialist_min_size[SP_MAX];
   UINT8 specialist_bonus[SP_MAX * O_MAX];
+  UINT8 specialist_req_type[SP_MAX * MAX_NUM_REQS];
+  UINT8 specialist_req_range[SP_MAX * MAX_NUM_REQS];
+  UINT8 specialist_req_value[SP_MAX * MAX_NUM_REQS];
+  BOOL specialist_req_survives[SP_MAX * MAX_NUM_REQS];
   BOOL changable_tax;
   UINT8 forced_science;
   UINT8 forced_luxury;
@@ -1269,14 +1271,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       17 Dec 2004 01:39:55 -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,100 @@
   "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[REQ_RANGE_LAST] = {
+  "Local",
+  "City",
+  "Continent",
+  "Player",
+  "World"
+};
+
+/**************************************************************************
+  Convert a range name to an enumerated value.
+
+  The check is case insensitive and returns REQ_RANGE_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) == REQ_RANGE_LAST);
+
+  for (range = 0; range < REQ_RANGE_LAST; range++) {
+    if (0 == mystrcasecmp(req_range_names[range], str)) {
+      return range;
+    }
+  }
+
+  return REQ_RANGE_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.
+  Returns REQ_LAST on error.  Passing in a NULL type be considered REQ_NONE
+  (not an 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);
+  if (type) {
+    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;
+      }
+    }
+  } else {
+    req.type = REQ_NONE;
+  }
 
-  for (req.type = 0; req.type < ARRAY_SIZE(req_type_names); req.type++) {
-    if (0 == mystrcasecmp(req_type_names[req.type], type)) {
+  req.range = req_range_from_str(range);
+  if (req.range == REQ_RANGE_LAST) {
+    switch (req.type) {
+    case REQ_NONE:
+    case REQ_LAST:
+      break;
+    case REQ_BUILDING:
+    case REQ_SPECIAL:
+    case REQ_TERRAIN:
+      req.range = REQ_RANGE_CITY;
+      break;
+    case REQ_GOV:
+    case REQ_TECH:
+      req.range = REQ_RANGE_PLAYER;
       break;
     }
   }
 
+  req.survives = survives;
+
   switch (req.type) {
   case REQ_NONE:
     return req;
@@ -98,58 +178,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 >= REQ_RANGE_PLAYER);
+  case TARGET_CITY:
+    return (range >= REQ_RANGE_CITY);
+  case TARGET_BUILDING:
+    return (range >= REQ_RANGE_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 == REQ_RANGE_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 REQ_RANGE_WORLD:
+    return num_world_buildings(source);
+  case REQ_RANGE_PLAYER:
+    return num_player_buildings(target_player, source);
+  case REQ_RANGE_CONTINENT:
+    {
+      int continent = map_get_continent(target_city->tile);
+
+      return num_continent_buildings(target_player, continent, source);
+    }
+  case REQ_RANGE_CITY:
+    return num_city_buildings(target_city, source);
+  case REQ_RANGE_LOCAL:
+    if (target_building == source) {
+      return num_city_buildings(target_city, source);
+    } else {
+      return 0;
+    }
+  case REQ_RANGE_LAST:
+    break;
+  }
+  assert(0);
+  return 0;
 }
 
 /****************************************************************************
@@ -157,8 +430,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 +461,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, REQ_RANGE_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       17 Dec 2004 01:39:55 -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 {
+  REQ_RANGE_LOCAL,
+  REQ_RANGE_CITY,
+  REQ_RANGE_CONTINENT,
+  REQ_RANGE_PLAYER,
+  REQ_RANGE_WORLD,
+  REQ_RANGE_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      17 Dec 2004 01:39:56 -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.219
diff -u -r1.219 ruleset.c
--- server/ruleset.c    17 Dec 2004 00:14:16 -0000      1.219
+++ server/ruleset.c    17 Dec 2004 01:39:57 -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)) == REQ_RANGE_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 = REQ_RANGE_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)) == REQ_RANGE_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 = REQ_RANGE_CITY;
        }
 
        survives = secfile_lookup_bool_default(file, FALSE, 
"%s.effect%d.survives",
@@ -1383,11 +1383,12 @@
        }
 
        /* Sometimes the ruleset will have to list "" here. */
-       req_type = secfile_lookup_str_default(file, "", "%s.effect%d.req_type",
+       req_type = secfile_lookup_str_default(file, NULL,
+                                             "%s.effect%d.req_type",
                                              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,
@@ -2523,7 +2524,7 @@
 
   for (i = 0; i < nval; i++) {
     const char *name = specialist_names[i], *short_name;
-    int *bonus = game.rgame.specialists[i].bonus;
+    int *bonus = game.rgame.specialists[i].bonus, j;
 
     sz_strlcpy(game.rgame.specialists[i].name, name);
     short_name
@@ -2542,6 +2543,34 @@
        && game.rgame.default_specialist == -1) {
       game.rgame.default_specialist = i;
     }
+
+    for (j = 0; j < MAX_NUM_REQS; j++) {
+      const char *type
+       = secfile_lookup_str_default(file, NULL, "specialist.%s_req%d.type",
+                                    name, j);
+      const char *range
+       = secfile_lookup_str_default(file, "", "specialist.%s_req%d.range",
+                                    name, j);
+      bool survives
+       = secfile_lookup_bool_default(file, FALSE,
+                                     "specialist.%s_req%d.survives",
+                                     name, j);
+      const char *value
+       = secfile_lookup_str_default(file, "", "specialist.%s_req%d.value",
+                                    name, j);
+      struct requirement req = req_from_str(type, range, survives, value);
+
+      if (req.type == REQ_LAST) {
+       freelog(LOG_ERROR,
+               /* TRANS: Obscure ruleset error */
+               _("Specialist %s has unknown req: "
+                 "\"%s\" \"%s\" \"%s\" %d (%s)"),
+               name, type, range, value, survives, filename);
+       req.type = REQ_NONE;
+      }
+
+      game.rgame.specialists[i].req[j] = req;
+    }
   }
   if (game.rgame.default_specialist == -1) {
     freelog(LOG_FATAL, "You must give a min_size of 0 for at least one "
@@ -3116,7 +3145,7 @@
   misc_p.num_specialist_types = SP_COUNT;
   misc_p.default_specialist = DEFAULT_SPECIALIST;
   specialist_type_iterate(sp) {
-    int *bonus = game.rgame.specialists[sp].bonus;
+    int *bonus = game.rgame.specialists[sp].bonus, j;
 
     sz_strlcpy(misc_p.specialist_name[sp], game.rgame.specialists[sp].name);
     sz_strlcpy(misc_p.specialist_short_name[sp],
@@ -3126,6 +3155,16 @@
     output_type_iterate(o) {
       misc_p.specialist_bonus[sp * O_COUNT + o] = bonus[o];
     } output_type_iterate_end;
+
+    for (j = 0; j < MAX_NUM_REQS; j++) {
+      int index = sp * MAX_NUM_REQS + j;
+
+      req_get_values(&game.rgame.specialists[sp].req[j],
+                    &misc_p.specialist_req_type[index],
+                    &misc_p.specialist_req_range[index],
+                    &misc_p.specialist_req_survives[index],
+                    &misc_p.specialist_req_value[index]);
+    }
   } specialist_type_iterate_end;
   misc_p.changable_tax = game.rgame.changable_tax;
   misc_p.forced_science = game.rgame.forced_science;
Index: data/default/cities.ruleset
===================================================================
RCS file: /home/freeciv/CVS/freeciv/data/default/cities.ruleset,v
retrieving revision 1.14
diff -u -r1.14 cities.ruleset
--- data/default/cities.ruleset 8 Dec 2004 16:53:53 -0000       1.14
+++ data/default/cities.ruleset 17 Dec 2004 01:39:38 -0000
@@ -26,11 +26,17 @@
 elvis_min_size = 0
 elvis_bonus_luxury = 2
 scientist_short_name = _("?Scientist:S")
-scientist_min_size = 5
+scientist_min_size = 1
 scientist_bonus_science = 3
+scientist_req = {"type", "value", "range"
+  "tech", "Literacy", "player"
+}
 taxman_short_name = _("?Taxman:T")
-taxman_min_size = 5
+taxman_min_size = 1
 taxman_bonus_gold = 3
+taxman_req = {"type", "value", "range"
+  "building", "Marketplace", "city"
+}
 
 changable_tax = 1
 ;forced_science = 0

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