Complete.Org: Mailing Lists: Archives: freeciv-dev: January 2005:
[Freeciv-Dev] (PR#12019) Effects TNG
Home

[Freeciv-Dev] (PR#12019) Effects TNG

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
To: vasc@xxxxxxxxxxxxxx
Subject: [Freeciv-Dev] (PR#12019) Effects TNG
From: "Jason Short" <jdorje@xxxxxxxxxxxxxxxxxxxxx>
Date: Sun, 30 Jan 2005 21:26:58 -0800
Reply-to: bugs@xxxxxxxxxxx

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

Here is an updated patch.

I scrutinized the common/*.h code.  I looked over the effects.c and
requirements.c code pretty closely.  The rest I just skimmed.

- I added some function comments.  In particular some rather unobvious
functions lacked any documentation.  This is bad.  There may be other
functions missing comments.

- Fixed some style, blah blah...

- Changed is_effect_active.  It should first look for enabled effects
then disabled ones.  This should be faster.

- Changed the effects[].effects array/list to just be an effects[]
array/list.

- Renamed "place" as "range" in the ruleset since...that's what it is ;-).

However I couldn't test it (which is the obvious next step) because the
supplied effects.ruleset doesn't work:

0: sectionfile data/default/effects.ruleset doesn't contain a
'datafile.options' entry

Aside from the lack of rulesets (and pending some testing) I'm ready to
commit this.  However the ruleset problem is a major issue, possibly as
much work as writing the patch itself.  The history ruleset should just
copy the default one, but someone will have to go through and make the
civ1 and civ2 rulesets (probably from scratch, or it could be scripted).

BTW, we need a ruleset editor now that effects and sources are
completely separated in the rulesets.

-jason

? data/default/effects.ruleset
Index: ai/aicity.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aicity.c,v
retrieving revision 1.190
diff -u -r1.190 aicity.c
--- ai/aicity.c 22 Jan 2005 19:45:38 -0000      1.190
+++ ai/aicity.c 31 Jan 2005 05:24:43 -0000
@@ -245,6 +245,10 @@
   struct tile *ptile = pcity->tile;
   bool capital = is_capital(pcity);
   struct government *gov = get_gov_pplayer(pplayer);
+  struct req_source source = {
+    .type = REQ_BUILDING,
+    .value.building = id
+  };
 
   /* Base want is calculated above using a more direct approach. */
   v += base_want(pplayer, pcity, id);
@@ -262,277 +266,294 @@
   cities[REQ_RANGE_CITY] = 1;
   cities[REQ_RANGE_LOCAL] = 0;
 
-  /* Calculate desire value. */
-  effect_type_vector_iterate(get_building_effect_types(id), ptype) {
-    effect_list_iterate(get_building_effects(id, *ptype), peff) {
-      if (is_effect_useful(TARGET_BUILDING, pplayer, pcity, id,
-                          NULL, id, peff)) {
-       int amount = peff->value, c = cities[peff->range];
-        struct city *palace = find_palace(pplayer);
-
-       switch (*ptype) {
-         case EFT_PROD_TO_GOLD:
-            /* Since coinage contains some entirely spurious ruleset values,
-             * we need to return here with some spurious want. */
-            pcity->ai.building_want[id] = TRADE_WEIGHTING;
-            return;
-          /* These have already been evaluated in base_want() */
-          case EFT_CAPITAL_CITY:
-         case EFT_UPKEEP_FREE:
-         case EFT_POLLU_POP_PCT:
-         case EFT_POLLU_PROD_PCT:
-         case EFT_TRADE_PER_TILE:
-         case EFT_TRADE_INC_TILE:
-         case EFT_FOOD_INC_TILE:
-         case EFT_TRADE_ADD_TILE:
-         case EFT_PROD_INC_TILE:
-         case EFT_PROD_PER_TILE:
-         case EFT_PROD_ADD_TILE:
-         case EFT_FOOD_PER_TILE:
-         case EFT_FOOD_ADD_TILE:
-         case EFT_PROD_BONUS:
-          case EFT_TAX_BONUS:
-          case EFT_SCIENCE_BONUS:
-         case EFT_LUXURY_BONUS:
-          case EFT_CORRUPT_PCT:
-          case EFT_WASTE_PCT:
-           break;
-
-          /* WAG evaluated effects */
-         case EFT_INCITE_DIST_PCT:
-            if (palace) {
-              v += real_map_distance(pcity->tile, palace->tile);
-            }
-            break;
-         case EFT_MAKE_HAPPY:
-            /* TODO */
-            break;
-         case EFT_UNIT_RECOVER:
-            /* TODO */
-            break;
-         case EFT_NO_UNHAPPY:
-            v += (get_entertainers(pcity)
-                 + pcity->ppl_unhappy[4]) * 20;
-            break;
-         case EFT_FORCE_CONTENT:
-           if (!government_has_flag(gov, G_NO_UNHAPPY_CITIZENS)) {
-             v += (pcity->ppl_unhappy[4]
-                   + get_entertainers(pcity)) * 20;
-             v += 5 * c;
-           }
-           break;
-         case EFT_MAKE_CONTENT_MIL_PER:
-         case EFT_MAKE_CONTENT:
-           if (!government_has_flag(gov, G_NO_UNHAPPY_CITIZENS)) {
-              v += MIN(pcity->ppl_unhappy[4]
-                      + get_entertainers(pcity),
-                       amount) * 20;
-              v += MIN(amount, 5) * c;
-           }
-           break;
-         case EFT_MAKE_CONTENT_MIL:
-           if (!government_has_flag(gov, G_NO_UNHAPPY_CITIZENS)) {
-             v += pcity->ppl_unhappy[4] * amount
-               * MAX(unit_list_size(pcity->units_supported)
-                   - gov->free_happy, 0) * 2;
-             v += c * MAX(amount + 2 - gov->free_happy, 1);
-           }
-           break;
-         case EFT_TECH_PARASITE:
-           v += (total_bulbs_required(pplayer) * (100 - game.freecost)
-               * (nplayers - amount)) / (nplayers * amount * 100);
-           break;
-         case EFT_GROWTH_FOOD:
-           v += c * 4 + (amount / 7) * pcity->surplus[O_FOOD];
-           break;
-         case EFT_AIRLIFT:
-            /* FIXME: We need some smart algorithm here. The below is 
-             * totally braindead. */
-           v += c + MIN(ai->stats.units[UCL_LAND], 13);
-           break;
-         case EFT_ANY_GOVERNMENT:
-           if (!can_change_to_government(pplayer, ai->goal.govt.idx)) {
-             v += MIN(MIN(ai->goal.govt.val, 65),
-                 num_unknown_techs_for_goal(pplayer, ai->goal.govt.req) * 10);
-           }
-           break;
-         case EFT_ENABLE_NUKE:
-           /* Treat nuke as a Cruise Missile upgrade */
-           v += 20 + ai->stats.units[UCL_MISSILE] * 5;
-           break;
-         case EFT_ENABLE_SPACE:
-           if (game.spacerace) {
-             v += 5;
-             if (ai->diplomacy.production_leader == pplayer) {
-               v += 100;
+  effect_list_iterate(get_req_source_effects(&source), peffect) {
+    struct requirement *mypreq;
+    bool useful;
+
+    if (is_effect_disabled(TARGET_BUILDING, pplayer, pcity,
+                          id, NULL, peffect)) {
+      continue;
+    }
+
+    mypreq = NULL;
+    useful = TRUE;
+    requirement_list_iterate(peffect->reqs, preq) {
+      if (preq->source.type == REQ_BUILDING
+         && preq->source.value.building == id) {
+       mypreq = preq;
+      }
+      if (!is_req_active(TARGET_BUILDING, pplayer, pcity, id, NULL, preq)) {
+       useful = FALSE;
+       break;
+      }
+    } requirement_list_iterate_end;
+
+    if (useful) {
+      int amount = peffect->value, c = cities[mypreq->range];
+      struct city *palace = find_palace(pplayer);
+
+      switch (peffect->type) {
+       case EFT_PROD_TO_GOLD:
+         /* Since coinage contains some entirely spurious ruleset values,
+          * we need to return here with some spurious want. */
+         pcity->ai.building_want[id] = TRADE_WEIGHTING;
+         return;
+       /* These have already been evaluated in base_want() */
+       case EFT_CAPITAL_CITY:
+       case EFT_UPKEEP_FREE:
+       case EFT_POLLU_POP_PCT:
+       case EFT_POLLU_PROD_PCT:
+       case EFT_TRADE_PER_TILE:
+       case EFT_TRADE_INC_TILE:
+       case EFT_FOOD_INC_TILE:
+       case EFT_TRADE_ADD_TILE:
+       case EFT_PROD_INC_TILE:
+       case EFT_PROD_PER_TILE:
+       case EFT_PROD_ADD_TILE:
+       case EFT_FOOD_PER_TILE:
+       case EFT_FOOD_ADD_TILE:
+       case EFT_PROD_BONUS:
+       case EFT_TAX_BONUS:
+       case EFT_SCIENCE_BONUS:
+       case EFT_LUXURY_BONUS:
+       case EFT_CORRUPT_PCT:
+       case EFT_WASTE_PCT:
+         break;
+
+       /* WAG evaluated effects */
+       case EFT_INCITE_DIST_PCT:
+         if (palace) {
+           v += real_map_distance(pcity->tile, palace->tile);
+         }
+         break;
+       case EFT_MAKE_HAPPY:
+         /* TODO */
+         break;
+       case EFT_UNIT_RECOVER:
+         /* TODO */
+         break;
+       case EFT_NO_UNHAPPY:
+         v += (get_entertainers(pcity)
+               + pcity->ppl_unhappy[4]) * 20;
+         break;
+       case EFT_FORCE_CONTENT:
+         if (!government_has_flag(gov, G_NO_UNHAPPY_CITIZENS)) {
+           v += (pcity->ppl_unhappy[4]
+                 + get_entertainers(pcity)) * 20;
+           v += 5 * c;
+         }
+         break;
+       case EFT_MAKE_CONTENT_MIL_PER:
+       case EFT_MAKE_CONTENT:
+         if (!government_has_flag(gov, G_NO_UNHAPPY_CITIZENS)) {
+           v += MIN(pcity->ppl_unhappy[4]
+                    + get_entertainers(pcity),
+                    amount) * 20;
+           v += MIN(amount, 5) * c;
+         }
+         break;
+       case EFT_MAKE_CONTENT_MIL:
+         if (!government_has_flag(gov, G_NO_UNHAPPY_CITIZENS)) {
+           v += pcity->ppl_unhappy[4] * amount
+             * MAX(unit_list_size(pcity->units_supported)
+                 - gov->free_happy, 0) * 2;
+           v += c * MAX(amount + 2 - gov->free_happy, 1);
+         }
+         break;
+       case EFT_TECH_PARASITE:
+         v += (total_bulbs_required(pplayer) * (100 - game.freecost)
+             * (nplayers - amount)) / (nplayers * amount * 100);
+         break;
+       case EFT_GROWTH_FOOD:
+         v += c * 4 + (amount / 7) * pcity->surplus[O_FOOD];
+         break;
+       case EFT_AIRLIFT:
+         /* FIXME: We need some smart algorithm here. The below is 
+          * totally braindead. */
+         v += c + MIN(ai->stats.units[UCL_LAND], 13);
+         break;
+       case EFT_ANY_GOVERNMENT:
+         if (!can_change_to_government(pplayer, ai->goal.govt.idx)) {
+           v += MIN(MIN(ai->goal.govt.val, 65),
+               num_unknown_techs_for_goal(pplayer, ai->goal.govt.req) * 10);
+         }
+         break;
+       case EFT_ENABLE_NUKE:
+         /* Treat nuke as a Cruise Missile upgrade */
+         v += 20 + ai->stats.units[UCL_MISSILE] * 5;
+         break;
+       case EFT_ENABLE_SPACE:
+         if (game.spacerace) {
+           v += 5;
+           if (ai->diplomacy.production_leader == pplayer) {
+             v += 100;
+           }
+         }
+         break;
+       case EFT_GIVE_IMM_TECH:
+         v += ((total_bulbs_required(pplayer) * amount 
+               + game.researchcost)
+             * TRADE_WEIGHTING - pplayer->research.bulbs_researched 
+             * TRADE_WEIGHTING) / MORT;
+         break;
+       case EFT_HAVE_EMBASSIES:
+         v += 5 * nplayers;
+         break;
+       case EFT_REVEAL_CITIES:
+       case EFT_NO_ANARCHY:
+         break;  /* Useless for AI */
+       case EFT_NO_SINK_DEEP:
+         v += 15 + ai->stats.triremes * 5;
+         break;
+       case EFT_NUKE_PROOF:
+         if (ai->threats.nuclear) {
+           v += pcity->size * unit_list_size(ptile->units) * (capital + 1);
+         }
+         break;
+       case EFT_REVEAL_MAP:
+         if (!ai->explore.land_done || !ai->explore.sea_done) {
+           v += 10;
+         }
+         break;
+       case EFT_SIZE_UNLIMIT:
+         amount = 20; /* really big city */
+         /* there not being a break here is deliberate, mind you */
+       case EFT_SIZE_ADJ: 
+         if (!city_can_grow_to(pcity, pcity->size + 1)) {
+           v += pcity->surplus[O_FOOD] * ai->food_priority * amount;
+           if (pcity->size == game.aqueduct_size) {
+             v += 30 * pcity->surplus[O_FOOD];
+           }
+         }
+         v += c * amount * 4 / game.aqueduct_size;
+         break;
+       case EFT_SS_STRUCTURAL:
+       case EFT_SS_COMPONENT:
+       case EFT_SS_MODULE:
+         if (game.spacerace
+             /* If someone has started building spaceship already or
+              * we have chance to win a spacerace */
+             && (ai->diplomacy.spacerace_leader
+                 || ai->diplomacy.production_leader == pplayer)) {
+           v += 95;
+         }
+         break;
+       case EFT_SPY_RESISTANT:
+         /* Uhm, problem: City Wall has -50% here!! */
+         break;
+       case EFT_SEA_MOVE:
+         v += ai->stats.units[UCL_SEA] * 8 * amount;
+         break;
+       case EFT_UNIT_NO_LOSE_POP:
+         v += unit_list_size(ptile->units) * 2;
+         break;
+       case EFT_LAND_REGEN:
+         v += 15 * c + ai->stats.units[UCL_LAND] * 3;
+         break;
+       case EFT_SEA_REGEN:
+         v += 15 * c + ai->stats.units[UCL_SEA] * 3;
+         break;
+       case EFT_AIR_REGEN:
+         v += 15 * c + ai->stats.units[UCL_AIR] * 3;
+         break;
+       case EFT_LAND_VET_COMBAT:
+         v += 2 * c + ai->stats.units[UCL_LAND] * 2;
+         break;
+       case EFT_LAND_VETERAN:
+         v += 5 * c + ai->stats.units[UCL_LAND];
+         break;
+       case EFT_SEA_VETERAN:
+         v += 5 * c + ai->stats.units[UCL_SEA];
+         break;
+       case EFT_AIR_VETERAN:
+         v += 5 * c + ai->stats.units[UCL_AIR];
+         break;
+       case EFT_UPGRADE_UNIT:
+         v += ai->stats.units[UCL_LAST];
+         if (amount == 1) {
+           v *= 2;
+         } else if (amount == 2) {
+           v *= 3;
+         } else {
+           v *= 4;
+         }
+         break;
+       case EFT_SEA_DEFEND:
+         if (ai_handicap(pplayer, H_DEFENSIVE)) {
+           v += amount / 10; /* make AI slow */
+         }
+         if (is_ocean(map_get_terrain(pcity->tile))) {
+           v += ai->threats.ocean[-map_get_continent(pcity->tile)]
+                ? amount/5 : amount/20;
+         } else {
+           adjc_iterate(pcity->tile, tile2) {
+             if (is_ocean(map_get_terrain(tile2))) {
+               if (ai->threats.ocean[-map_get_continent(tile2)]) {
+                 v += amount/5;
+                 break;
+               }
+             }
+           } adjc_iterate_end;
+         }
+         v += (amount/20 + ai->threats.invasions - 1) * c; /* for wonder */
+         if (capital && ai->threats.invasions) {
+           v += amount; /* defend capital! */
+         }
+         break;
+       case EFT_AIR_DEFEND:
+         if (ai_handicap(pplayer, H_DEFENSIVE)) {
+           v += amount / 15; /* make AI slow */
+         }
+         v += (ai->threats.air && ai->threats.continent[ptile->continent]) 
+           ? amount/10 * 5 + amount/10 * c : c;
+         break;
+       case EFT_MISSILE_DEFEND:
+         if (ai->threats.missile
+             && (ai->threats.continent[ptile->continent] || capital)) {
+           v += amount/10 * 5 + (amount/10 - 1) * c;
+         }
+         break;
+       case EFT_LAND_DEFEND:
+         if (ai_handicap(pplayer, H_DEFENSIVE)) {
+           v += amount / 10; /* make AI slow */
+         }
+         if (ai->threats.continent[ptile->continent]
+             || capital
+             || (ai->threats.invasions
+               && is_water_adjacent_to_tile(pcity->tile))) {
+           v += amount / (!ai->threats.igwall ? (15 - capital * 5) : 15);
+         }
+         v += (1 + ai->threats.invasions + !ai->threats.igwall) * c;
+         break;
+       case EFT_NO_INCITE:
+         if (!government_has_flag(gov, G_UNBRIBABLE)) {
+           v += MAX((game.diplchance * 2 - game.incite_cost.total_factor) / 2
+               - game.incite_cost.improvement_factor * 5
+               - game.incite_cost.unit_factor * 5, 0);
+         }
+         break;
+       case EFT_REGEN_REPUTATION:
+         v += (GAME_MAX_REPUTATION - pplayer->reputation) * 50 / 
+                 GAME_MAX_REPUTATION + 
+               amount * 4;
+         break;
+       case EFT_GAIN_AI_LOVE:
+         players_iterate(aplayer) {
+           if (aplayer->ai.control) {
+             if (ai_handicap(pplayer, H_DEFENSIVE)) {
+               v += amount / 10;
+             } else {
+               v += amount / 20;
              }
            }
-           break;
-         case EFT_GIVE_IMM_TECH:
-           v += ((total_bulbs_required(pplayer) * amount 
-                 + game.researchcost)
-               * TRADE_WEIGHTING - pplayer->research.bulbs_researched 
-               * TRADE_WEIGHTING) / MORT;
-           break;
-         case EFT_HAVE_EMBASSIES:
-           v += 5 * nplayers;
-           break;
-         case EFT_REVEAL_CITIES:
-         case EFT_NO_ANARCHY:
-           break;  /* Useless for AI */
-         case EFT_NO_SINK_DEEP:
-           v += 15 + ai->stats.triremes * 5;
-           break;
-         case EFT_NUKE_PROOF:
-           if (ai->threats.nuclear) {
-             v += pcity->size * unit_list_size(ptile->units) * (capital + 1);
-           }
-           break;
-         case EFT_REVEAL_MAP:
-           if (!ai->explore.land_done || !ai->explore.sea_done) {
-             v += 10;
-           }
-           break;
-         case EFT_SIZE_UNLIMIT:
-           amount = 20; /* really big city */
-           /* there not being a break here is deliberate, mind you */
-         case EFT_SIZE_ADJ: 
-            if (!city_can_grow_to(pcity, pcity->size + 1)) {
-             v += pcity->surplus[O_FOOD] * ai->food_priority * amount;
-              if (pcity->size == game.aqueduct_size) {
-                v += 30 * pcity->surplus[O_FOOD];
-              }
-           }
-           v += c * amount * 4 / game.aqueduct_size;
-           break;
-         case EFT_SS_STRUCTURAL:
-         case EFT_SS_COMPONENT:
-         case EFT_SS_MODULE:
-           if (game.spacerace
-               /* If someone has started building spaceship already or
-                * we have chance to win a spacerace */
-               && (ai->diplomacy.spacerace_leader
-                   || ai->diplomacy.production_leader == pplayer)) {
-             v += 95;
-           }
-           break;
-         case EFT_SPY_RESISTANT:
-           /* Uhm, problem: City Wall has -50% here!! */
-           break;
-         case EFT_SEA_MOVE:
-           v += ai->stats.units[UCL_SEA] * 8 * amount;
-           break;
-         case EFT_UNIT_NO_LOSE_POP:
-           v += unit_list_size(ptile->units) * 2;
-           break;
-         case EFT_LAND_REGEN:
-           v += 15 * c + ai->stats.units[UCL_LAND] * 3;
-           break;
-         case EFT_SEA_REGEN:
-           v += 15 * c + ai->stats.units[UCL_SEA] * 3;
-           break;
-         case EFT_AIR_REGEN:
-           v += 15 * c + ai->stats.units[UCL_AIR] * 3;
-           break;
-         case EFT_LAND_VET_COMBAT:
-           v += 2 * c + ai->stats.units[UCL_LAND] * 2;
-           break;
-         case EFT_LAND_VETERAN:
-           v += 5 * c + ai->stats.units[UCL_LAND];
-           break;
-         case EFT_SEA_VETERAN:
-           v += 5 * c + ai->stats.units[UCL_SEA];
-           break;
-         case EFT_AIR_VETERAN:
-           v += 5 * c + ai->stats.units[UCL_AIR];
-           break;
-         case EFT_UPGRADE_UNIT:
-           v += ai->stats.units[UCL_LAST];
-           if (amount == 1) {
-             v *= 2;
-           } else if (amount == 2) {
-             v *= 3;
-           } else {
-             v *= 4;
-           }
-           break;
-         case EFT_SEA_DEFEND:
-           if (ai_handicap(pplayer, H_DEFENSIVE)) {
-             v += amount / 10; /* make AI slow */
-           }
-            if (is_ocean(map_get_terrain(pcity->tile))) {
-              v += ai->threats.ocean[-map_get_continent(pcity->tile)]
-                   ? amount/5 : amount/20;
-            } else {
-              adjc_iterate(pcity->tile, tile2) {
-                if (is_ocean(map_get_terrain(tile2))) {
-                  if (ai->threats.ocean[-map_get_continent(tile2)]) {
-                    v += amount/5;
-                   break;
-                  }
-                }
-              } adjc_iterate_end;
-            }
-           v += (amount/20 + ai->threats.invasions - 1) * c; /* for wonder */
-           if (capital && ai->threats.invasions) {
-             v += amount; /* defend capital! */
-           }
-           break;
-         case EFT_AIR_DEFEND:
-           if (ai_handicap(pplayer, H_DEFENSIVE)) {
-             v += amount / 15; /* make AI slow */
-           }
-           v += (ai->threats.air && ai->threats.continent[ptile->continent]) 
-             ? amount/10 * 5 + amount/10 * c : c;
-           break;
-         case EFT_MISSILE_DEFEND:
-           if (ai->threats.missile
-               && (ai->threats.continent[ptile->continent] || capital)) {
-             v += amount/10 * 5 + (amount/10 - 1) * c;
-           }
-           break;
-         case EFT_LAND_DEFEND:
-           if (ai_handicap(pplayer, H_DEFENSIVE)) {
-             v += amount / 10; /* make AI slow */
-           }
-           if (ai->threats.continent[ptile->continent]
-               || capital
-               || (ai->threats.invasions
-                 && is_water_adjacent_to_tile(pcity->tile))) {
-              v += amount / (!ai->threats.igwall ? (15 - capital * 5) : 15);
-           }
-           v += (1 + ai->threats.invasions + !ai->threats.igwall) * c;
-           break;
-         case EFT_NO_INCITE:
-           if (!government_has_flag(gov, G_UNBRIBABLE)) {
-             v += MAX((game.diplchance * 2 - game.incite_cost.total_factor) / 2
-                 - game.incite_cost.improvement_factor * 5
-                 - game.incite_cost.unit_factor * 5, 0);
-           }
-           break;
-          case EFT_REGEN_REPUTATION:
-            v += (GAME_MAX_REPUTATION - pplayer->reputation) * 50 / 
-                   GAME_MAX_REPUTATION + 
-                 amount * 4;
-            break;
-         case EFT_GAIN_AI_LOVE:
-            players_iterate(aplayer) {
-              if (aplayer->ai.control) {
-                if (ai_handicap(pplayer, H_DEFENSIVE)) {
-                  v += amount / 10;
-                } else {
-                  v += amount / 20;
-                }
-              }
-            } players_iterate_end;
-            break;
-         case EFT_LAST:
-           freelog(LOG_ERROR, "Bad effect type.");
-           break;
-       }
+         } players_iterate_end;
+         break;
+       case EFT_LAST:
+         freelog(LOG_ERROR, "Bad effect type.");
+         break;
       }
-    } effect_list_iterate_end;
-  } effect_type_vector_iterate_end;
+    }
+  } effect_list_iterate_end;
 
   /* Reduce want if building gets obsoleted soon */
   if (tech_exists(pimpr->obsolete_by)) {
Index: ai/aidata.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aidata.c,v
retrieving revision 1.49
diff -u -r1.49 aidata.c
--- ai/aidata.c 22 Jan 2005 19:45:38 -0000      1.49
+++ ai/aidata.c 31 Jan 2005 05:24:43 -0000
@@ -59,11 +59,16 @@
   memset(count, 0, sizeof(count));
 
   impr_type_iterate(id) {
+    struct req_source source = {
+      .type = REQ_BUILDING,
+      .value.building = id
+    };
+
     ai->impr_calc[id] = AI_IMPR_ESTIMATE;
 
     /* Find largest extension */
-    effect_type_vector_iterate(get_building_effect_types(id), ptype) {
-      switch (*ptype) {
+    effect_list_iterate(get_req_source_effects(&source), peffect) {
+      switch (peffect->type) {
 #if 0
       /* TODO */
       case EFT_FORCE_CONTENT:
@@ -92,19 +97,21 @@
       case EFT_TRADE_INC_TILE:
       case EFT_TRADE_PER_TILE:
       case EFT_UPKEEP_FREE:
-      effect_list_iterate(get_building_effects(id, *ptype), peff) {
-        ai->impr_calc[id] = AI_IMPR_CALCULATE;
-        if (peff->range > ai->impr_range[id]) {
-          ai->impr_range[id] = peff->range;
-        }
-      } effect_list_iterate_end;
+       requirement_list_iterate(peffect->reqs, preq) {
+         if (preq->source.type == REQ_BUILDING
+             && preq->source.value.building == id) {
+           ai->impr_calc[id] = AI_IMPR_CALCULATE;
+           if (preq->range > ai->impr_range[id]) {
+             ai->impr_range[id] = preq->range;
+           }
+         }
+       } requirement_list_iterate_end;
       break;
       default:
       /* Nothing! */
       break;
       }
-    } effect_type_vector_iterate_end;
-    
+    } effect_list_iterate_end;
   } impr_type_iterate_end;
 }
 
Index: client/citydlg_common.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/citydlg_common.c,v
retrieving revision 1.57
diff -u -r1.57 citydlg_common.c
--- client/citydlg_common.c     22 Jan 2005 19:45:38 -0000      1.57
+++ client/citydlg_common.c     31 Jan 2005 05:24:44 -0000
@@ -439,22 +439,26 @@
 
   if (eft != EFT_LAST) {
     int base = total, bonus = 100;
-    struct effect_source_vector sources;
+    struct effect_list *plist = effect_list_new();
 
-    (void) get_city_bonus_sources(&sources, pcity, eft);
+    (void) get_city_bonus_effects(plist, pcity, eft);
 
-    effect_source_vector_iterate(&sources, s) {
+    effect_list_iterate(plist, peffect) {
+      char buf2[512];
       int new_total;
 
-      bonus += s->effect_value;
+      get_effect_req_text(peffect, buf2, sizeof(buf2));
+
+      bonus += peffect->value;
       new_total = bonus * base / 100;
       cat_snprintf(buf, bufsz,
                   _("%+4d : Bonus from %s (%+d%%)\n"),
-                  (new_total - total), get_improvement_name(s->building),
-                  s->effect_value);
+                  (new_total - total), buf2,
+                  peffect->value);
       total = new_total;
-    } effect_source_vector_iterate_end;
-    effect_source_vector_free(&sources);
+    } effect_list_iterate_end;
+    effect_list_unlink_all(plist);
+    effect_list_free(plist);
   }
 
   if (pcity->waste[otype] != 0) {
Index: client/packhand.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/packhand.c,v
retrieving revision 1.461
diff -u -r1.461 packhand.c
--- client/packhand.c   29 Jan 2005 17:58:17 -0000      1.461
+++ client/packhand.c   31 Jan 2005 05:24:44 -0000
@@ -2833,31 +2833,18 @@
 }
 
 /**************************************************************************
-  Add group data to ruleset cache.  
+  Add effect data to ruleset cache.  
 **************************************************************************/
-void handle_ruleset_cache_group(struct packet_ruleset_cache_group *packet)
+void handle_ruleset_effect(struct packet_ruleset_effect *packet)
 {
-  struct effect_group *pgroup;
-  int i;
-
-  pgroup = effect_group_new(packet->name);
-
-  for (i = 0; i < packet->num_elements; i++) {
-    effect_group_add_element(pgroup, packet->source_buildings[i],
-                            packet->ranges[i], packet->survives[i]);
-  }
+  recv_ruleset_effect(packet);
 }
 
 /**************************************************************************
-  Add effect data to ruleset cache.  
+  Add effect requirement data to ruleset cache.  
 **************************************************************************/
-void handle_ruleset_cache_effect(struct packet_ruleset_cache_effect *packet)
+void handle_ruleset_effect_req(struct packet_ruleset_effect_req *packet)
 {
-  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,
-                   &req, packet->group_id);
+  recv_ruleset_effect_req(packet);
 }
 
Index: client/text.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/text.c,v
retrieving revision 1.21
diff -u -r1.21 text.c
--- client/text.c       22 Jan 2005 19:45:39 -0000      1.21
+++ client/text.c       31 Jan 2005 05:24:44 -0000
@@ -724,17 +724,21 @@
 const char *get_happiness_buildings(const struct city *pcity)
 {
   int faces = 0;
-  struct effect_source_vector sources;
+  struct effect_list *plist = effect_list_new();
+  char buf[512];
   INIT;
 
   add_line(_("Buildings: "));
 
-  get_city_bonus_sources(&sources, pcity, EFT_MAKE_CONTENT);
-  effect_source_vector_iterate(&sources, src) {
+  get_city_bonus_effects(plist, pcity, EFT_MAKE_CONTENT);
+
+  effect_list_iterate(plist, peffect) {
     faces++;
-    add(_("%s. "), get_improvement_name(src->building));
-  } effect_source_vector_iterate_end;
-  effect_source_vector_free(&sources);
+    get_effect_req_text(peffect, buf, sizeof(buf));
+    add(_("%s. "), buf);
+  } effect_list_iterate_end;
+  effect_list_unlink_all(plist);
+  effect_list_free(plist);
 
   if (faces == 0) {
     add(_("None. "));
@@ -749,31 +753,23 @@
 const char *get_happiness_wonders(const struct city *pcity)
 {
   int faces = 0;
-  struct effect_source_vector sources;
+  struct effect_list *plist = effect_list_new();
+  char buf[512];
   INIT;
 
   add_line(_("Wonders: "));
+  get_city_bonus_effects(plist, pcity, EFT_MAKE_HAPPY);
+  get_city_bonus_effects(plist, pcity, EFT_FORCE_CONTENT);
+  get_city_bonus_effects(plist, pcity, EFT_NO_UNHAPPY);
 
-  get_city_bonus_sources(&sources, pcity, EFT_MAKE_HAPPY);
-  effect_source_vector_iterate(&sources, src) {
-    faces++;
-    add(_("%s. "), get_improvement_name(src->building));
-  } effect_source_vector_iterate_end;
-  effect_source_vector_free(&sources);
-
-  get_city_bonus_sources(&sources, pcity, EFT_FORCE_CONTENT);
-  effect_source_vector_iterate(&sources, src) {
+  effect_list_iterate(plist, peffect) {
     faces++;
-    add(_("%s. "), get_improvement_name(src->building));
-  } effect_source_vector_iterate_end;
-  effect_source_vector_free(&sources);
+    get_effect_req_text(peffect, buf, sizeof(buf));
+    add(_("%s. "), buf);
+  } effect_list_iterate_end;
 
-  get_city_bonus_sources(&sources, pcity, EFT_NO_UNHAPPY);
-  effect_source_vector_iterate(&sources, src) {
-    faces++;
-    add(_("%s. "), get_improvement_name(src->building));
-  } effect_source_vector_iterate_end;
-  effect_source_vector_free(&sources);
+  effect_list_unlink_all(plist);
+  effect_list_free(plist);
 
   if (faces == 0) {
     add(_("None. "));
Index: common/city.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/city.c,v
retrieving revision 1.308
diff -u -r1.308 city.c
--- common/city.c       29 Jan 2005 08:51:18 -0000      1.308
+++ common/city.c       31 Jan 2005 05:24:45 -0000
@@ -671,7 +671,7 @@
   for (i = 0; i < MAX_NUM_REQS; i++) {
     struct requirement *req = &game.rgame.specialists[type].req[i];
 
-    if (req->type == REQ_NONE) {
+    if (req->source.type == REQ_NONE) {
       break; /* Short-circuit any more checks. */
     } else if (!is_req_active(TARGET_CITY, city_owner(pcity), pcity,
                              B_LAST, NULL, req)) {
Index: common/effects.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/effects.c,v
retrieving revision 1.21
diff -u -r1.21 effects.c
--- common/effects.c    22 Jan 2005 19:45:41 -0000      1.21
+++ common/effects.c    31 Jan 2005 05:24:45 -0000
@@ -212,152 +212,114 @@
   number of possible sources increases.
 **************************************************************************/
 
-/*
- * A group lists which sources are in it and at what range.
- * Each effect also lists which group it is in.  So an effect is in the
- * group if its source is listed in the group, and the effect lists the
- * group as its own.
- *
- * Only the first applicable effect in a group will be active.  Any others
- * are simply ignored.
- *
- * Actually this isn't quite true.  The check is done on buildings, not
- * on effects.  A building in a group may obsolete the effect of a building
- * later in the group, even if the first building doesn't have any effects
- * that are actually in the group.  However only the effects of the second
- * building that are actually in the group will be obsoleted.
- */
-struct effect_group_element {
-  Impr_Type_id source_building;
-  enum req_range range;
-  bool survives;
-};
-
-#define SPECLIST_TAG effect_group_element
-#define SPECLIST_TYPE struct effect_group_element
-#include "speclist.h"
-
-#define effect_group_element_list_iterate(list, elt) \
-  TYPED_LIST_ITERATE(struct effect_group_element, list, elt)
-#define effect_group_element_list_iterate_end  LIST_ITERATE_END
-
-struct effect_group {
-  char *name;
-  int id;
-  struct effect_group_element_list *elements;
-};
-
-#define SPECLIST_TAG effect_group
-#define SPECLIST_TYPE struct effect_group
-#include "speclist.h"
-
-#define effect_group_list_iterate(list, pgroup) \
-  TYPED_LIST_ITERATE(struct effect_group, list, pgroup)
-#define effect_group_list_iterate_end  LIST_ITERATE_END
-
 /**************************************************************************
   Ruleset cache. The cache is created during ruleset loading and the data
   is organized to enable fast queries.
 **************************************************************************/
 static struct {
-  struct {
-    /* This cache shows for each effect, which buildings provide it. */
-    struct building_vector buildings;
+  /* A single list containing every effect. */
+  struct effect_list *tracker;
 
-    /* This array provides a full list of the effects of this type provided
-     * by each building.  (It's not really a cache, it's the real data.) */
-    struct effect_list *buckets[B_LAST];
-  } effects[EFT_LAST];
+  /* This array provides a full list of the effects of this type
+   * (It's not really a cache, it's the real data.) */
+  struct effect_list *effects[EFT_LAST];
 
-  /* This cache shows for each building, which effect types it provides. */
   struct {
-    struct effect_type_vector types;
-  } buildings[B_LAST];
+    /* This cache shows for each building, which effects it provides. */
+    struct effect_list *buildings[B_LAST];
+  } reqs;
 } ruleset_cache;
 
-static struct effect_group_list *groups;
-static int next_group_id;
-
 
 /**************************************************************************
-  Get a vector of buildings which grant the effect type.
+  Get a list of effects of this type.
 **************************************************************************/
-static struct building_vector *get_buildings_with_effect(enum effect_type e)
+static struct effect_list *get_effects(enum effect_type effect_type)
 {
-  return &ruleset_cache.effects[e].buildings;
+  return ruleset_cache.effects[effect_type];
 }
 
 /**************************************************************************
-  Get a list of effects of this type granted by a building.
-**************************************************************************/
-struct effect_list *get_building_effects(Impr_Type_id building,
-                                        enum effect_type effect_type)
-{
-  return ruleset_cache.effects[effect_type].buckets[building];
-}
+  Get a list of effects with this requirement source.
 
-/**************************************************************************
-  Get a vector of effect types granted by a building.
+  Note: currently only buildings are supported.
 **************************************************************************/
-struct effect_type_vector *get_building_effect_types(Impr_Type_id id)
+struct effect_list *get_req_source_effects(struct req_source *psource)
 {
-  return &ruleset_cache.buildings[id].types;
+  switch (psource->type) {
+  case REQ_BUILDING:
+    return ruleset_cache.reqs.buildings[psource->value.building];
+  default:
+    return NULL;
+  }
 }
 
 /**************************************************************************
-  Create a new effects group.
+  Add effect to ruleset cache.
 **************************************************************************/
-struct effect_group *effect_group_new(const char *name)
+struct effect *effect_new(enum effect_type type, int value)
 {
-  struct effect_group *group;
+  struct effect *peffect;
 
-  /* Create the group. */
-  group = fc_malloc(sizeof(*group));
-  group->name = mystrdup(name);
-  group->id = next_group_id++;
-  group->elements = effect_group_element_list_new();
+  /* Create the effect. */
+  peffect = fc_malloc(sizeof(*peffect));
+  peffect->type = type;
+  peffect->value = value;
 
-  /* Add this group to the global list of groups. */
-  effect_group_list_append(groups, group);
+  peffect->reqs = requirement_list_new();
+  peffect->nreqs = requirement_list_new();
 
-  return group;
+  /* Now add the effect to the ruleset cache. */
+  effect_list_append(ruleset_cache.tracker, peffect);
+  effect_list_append(get_effects(type), peffect);
+  return peffect;
 }
 
 /**************************************************************************
-  Add a source to an existing effects group.
+  Free effect.
 **************************************************************************/
-void effect_group_add_element(struct effect_group *group,
-                             Impr_Type_id source_building,
-                             enum req_range range, bool survives)
+static void effect_free(struct effect *peffect)
 {
-  struct effect_group_element *elt;
+  requirement_list_iterate(peffect->reqs, preq) {
+    free(preq);
+  } requirement_list_iterate_end;
+  requirement_list_unlink_all(peffect->reqs);
+  requirement_list_free(peffect->reqs);
 
-  /* Create the element. */
-  elt = fc_malloc(sizeof(*elt));
-  elt->source_building = source_building;
-  elt->range = range;
-  elt->survives = survives;
+  requirement_list_iterate(peffect->nreqs, preq) {
+    free(preq);
+  } requirement_list_iterate_end;
+  requirement_list_unlink_all(peffect->nreqs);
+  requirement_list_free(peffect->nreqs);
 
-  /* Append it to the group. */
-  effect_group_element_list_append(group->elements, elt);
+  free(peffect);
 }
 
 /**************************************************************************
-  Find the id of an effects group by name.  Returns -1 if the group is not
-  found.
+  Append requirement to effect.
 **************************************************************************/
-int find_effect_group_id(const char *name)
+void effect_req_append(struct effect *peffect, bool neg,
+                      struct requirement *preq)
 {
-  int group_id = 0;
+  struct requirement_list *req_list;
 
-  effect_group_list_iterate(groups, pgroup) {
-    if (0 == mystrcasecmp(pgroup->name, name)) {
-      return group_id;
-    }
-    group_id++;
-  } effect_group_list_iterate_end;
+  if (neg) {
+    req_list = peffect->nreqs;
+  } else {
+    req_list = peffect->reqs;
+  }
+
+  /* Append requirement to the effect. */
+  requirement_list_append(req_list, preq);
 
-  return -1;
+  /* Add effect to the source's effect list. */
+  if (!neg) {
+    struct effect_list *eff_list = get_req_source_effects(&preq->source);
+
+    if (eff_list) {
+      effect_list_append(eff_list, peffect);
+    }
+  }
 }
 
 /**************************************************************************
@@ -367,21 +329,15 @@
 **************************************************************************/
 void ruleset_cache_init(void)
 {
-  int i, j;
-
-  groups = effect_group_list_new();
-  next_group_id = 0;
+  int i;
 
-  for (i = 0; i < ARRAY_SIZE(ruleset_cache.buildings); i++) {
-    effect_type_vector_init(get_building_effect_types(i));
-  }
+  ruleset_cache.tracker = effect_list_new();
 
   for (i = 0; i < ARRAY_SIZE(ruleset_cache.effects); i++) {
-    building_vector_init(get_buildings_with_effect(i));
-
-    for (j = 0; j < ARRAY_SIZE(ruleset_cache.effects[i].buckets); j++) {
-      ruleset_cache.effects[i].buckets[j] = effect_list_new();
-    }
+    ruleset_cache.effects[i] = effect_list_new();
+  }
+  for (i = 0; i < ARRAY_SIZE(ruleset_cache.reqs.buildings); i++) {
+    ruleset_cache.reqs.buildings[i] = effect_list_new();
   }
 }
 
@@ -391,166 +347,71 @@
 **************************************************************************/
 void ruleset_cache_free(void)
 {
-  int i, j;
+  int i;
+  struct effect_list *plist = ruleset_cache.tracker;
 
-  for (i = 0; i < ARRAY_SIZE(ruleset_cache.buildings); i++) {
-    effect_type_vector_free(get_building_effect_types(i));
+  if (plist) {
+    effect_list_iterate(plist, peffect) {
+      effect_free(peffect);
+    } effect_list_iterate_end;
+    effect_list_unlink_all(plist);
+    effect_list_free(plist);
+    ruleset_cache.tracker = NULL;
   }
 
   for (i = 0; i < ARRAY_SIZE(ruleset_cache.effects); i++) {
-    building_vector_free(get_buildings_with_effect(i));
-
-    for (j = 0; j < ARRAY_SIZE(ruleset_cache.effects[i].buckets); j++) {
-      struct effect_list *plist = get_building_effects(j, i);
-
-      if (plist != NULL) {
-        effect_list_iterate(plist, peffect) {
-          /* Allocated in ruleset_cache_add. */
-          free(peffect);
-        } effect_list_iterate_end;
-        effect_list_unlink_all(plist);
-        effect_list_free(plist);
-      }
-    }
-  }
-  if (groups != NULL) {
-    effect_group_list_unlink_all(groups);
-    effect_group_list_free(groups);
-  }
-}
-
-/**************************************************************************
-  Add effect to ruleset cache.
-**************************************************************************/
-void ruleset_cache_add(Impr_Type_id source, enum effect_type effect_type,
-                      enum req_range range, bool survives, int eff_value,
-                      struct requirement *req,
-                      int group_id)
-{
-  struct effect *peffect;
-
-  /* Create the effect. */
-  peffect = fc_malloc(sizeof(*peffect));
-  peffect->range = range;
-  peffect->survives = survives;
-  peffect->value = eff_value;
-
-  /* Set effect's requirement data. */
-  peffect->req = *req;
-
-  /* Find the effect's group. */
-  if (group_id >= 0) {
-    peffect->group = effect_group_list_get(groups, group_id);
-  } else {
-    peffect->group = NULL;
-  }
-
-  /* Now add the effect to the ruleset cache. */
-  effect_list_append(get_building_effects(source, effect_type),
-                         peffect);
+    struct effect_list *plist = get_effects(i);
 
-  /* Add building type to the effect type's buildings vector. */
-  {
-    struct building_vector *vec;
-    Impr_Type_id *pbldg;
-
-    vec = get_buildings_with_effect(effect_type);
-
-    /* Append this building to the list of buildings providing the effect.
-     * There is a sanity check to prevent it from being added more than
-     * once.  (It is possible the same building would have multiple effects
-     * of the same type, in which case we don't want to add the building
-     * to the list twice.  However this does assume that all effects from
-     * one building are processed before moving on to the next building. */
-    if (!(pbldg = building_vector_get(vec, -1)) || *pbldg != source) {
-      building_vector_append(vec, &source);
+    if (plist) {
+      effect_list_unlink_all(plist);
+      effect_list_free(plist);
+      ruleset_cache.effects[i] = NULL;
     }
   }
 
-  /* Add effect type to the building's effect types vector. */
-  {
-    struct effect_type_vector *vec;
-    bool exists = FALSE;
-
-    vec = get_building_effect_types(source);
-
-    /* See if it's already in the list. */
-    effect_type_vector_iterate(vec, ptype) {
-      if (*ptype == effect_type) {
-       exists = TRUE;
-       break;
-      }
-    } effect_type_vector_iterate_end;
-
-    /* And if not, append it. */
-    if (!exists) {
-      effect_type_vector_append(vec, &effect_type);
+  for (i = 0; i < ARRAY_SIZE(ruleset_cache.reqs.buildings); i++) {
+    struct req_source source = {
+      .type = REQ_BUILDING,
+      .value.building = i
+    };
+    struct effect_list *plist = get_req_source_effects(&source);
+
+    if (plist) {
+      effect_list_unlink_all(plist);
+      effect_list_free(plist);
+      ruleset_cache.reqs.buildings[i] = NULL;
     }
   }
 }
 
 /**************************************************************************
-  Send the ruleset cache groups data.
+  Receives a new effect.  This is called by the client when the packet
+  arrives.
 **************************************************************************/
-static void send_ruleset_cache_groups(struct conn_list *dest)
+void recv_ruleset_effect(struct packet_ruleset_effect *packet)
 {
-  struct packet_ruleset_cache_group packet;
-  int i;
-
-  effect_group_list_iterate(groups, pgroup) {
-    sz_strlcpy(packet.name, pgroup->name);
-
-    packet.num_elements = effect_group_element_list_size(pgroup->elements);
-    for (i = 0; i < packet.num_elements; i++) {
-      struct effect_group_element *elt;
-
-      elt = effect_group_element_list_get(pgroup->elements, i);
-      packet.source_buildings[i] = elt->source_building;
-      packet.ranges[i] = elt->range;
-      packet.survives[i] = elt->survives;
-    }
-
-    lsend_packet_ruleset_cache_group(dest, &packet);
-  } effect_group_list_iterate_end;
+  effect_new(packet->effect_type, packet->effect_value);
 }
 
 /**************************************************************************
-  Send the ruleset cache effects data.
+  Receives a new effect *requirement*.  This is called by the client when
+  the packet arrives.
 **************************************************************************/
-static void send_ruleset_cache_effects(struct conn_list *dest)
+void recv_ruleset_effect_req(struct packet_ruleset_effect_req *packet)
 {
-  struct packet_ruleset_cache_effect packet;
-  enum effect_type effect_type;
-
-  for (effect_type = 0; effect_type < EFT_LAST; effect_type++) {
-    packet.effect_type = effect_type;
-
-    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),
-                         peffect) {
-       packet.range = peffect->range;
-       packet.survives = peffect->survives;
-        packet.eff_value = peffect->value;
-       packet.req_type = peffect->req.type;
+  if (packet->effect_id != effect_list_size(ruleset_cache.tracker) - 1) {
+    freelog(LOG_ERROR, "Bug in recv_ruleset_effect_req.");
+  } else {
+    struct effect *peffect = effect_list_get(ruleset_cache.tracker, -1);
+    struct requirement req, *preq;
 
-       if (peffect->group) {
-         packet.group_id = peffect->group->id;
-       } else {
-         packet.group_id = -1;
-       }
+    req = req_from_values(packet->source_type, packet->range, packet->survives,
+       packet->source_value);
 
-       req_get_values(&peffect->req, &dummy_type,
-                      &dummy_range, &dummy_survives, &packet.req_value);
+    preq = fc_malloc(sizeof(*preq));
+    *preq = req;
 
-       lsend_packet_ruleset_cache_effect(dest, &packet);
-      } effect_list_iterate_end;
-    } building_vector_iterate_end;
+    effect_req_append(peffect, packet->neg, preq);
   }
 }
 
@@ -559,8 +420,50 @@
 **************************************************************************/
 void send_ruleset_cache(struct conn_list *dest)
 {
-  send_ruleset_cache_groups(dest);
-  send_ruleset_cache_effects(dest);
+  unsigned id = 0;
+
+  effect_list_iterate(ruleset_cache.tracker, peffect) {
+    struct packet_ruleset_effect packet;
+
+    packet.effect_type = peffect->type;
+    packet.effect_value = peffect->value;
+
+    lsend_packet_ruleset_effect(dest, &packet);
+
+    requirement_list_iterate(peffect->reqs, preq) {
+      struct packet_ruleset_effect_req packet;
+      int type, range, value;
+      bool survives;
+
+      req_get_values(preq, &type, &range, &survives, &value);
+      packet.effect_id = id;
+      packet.neg = FALSE;
+      packet.source_type = type;
+      packet.source_value = value;
+      packet.range = range;
+      packet.survives = survives;
+
+      lsend_packet_ruleset_effect_req(dest, &packet);
+    } requirement_list_iterate_end;
+
+    requirement_list_iterate(peffect->nreqs, preq) {
+      struct packet_ruleset_effect_req packet;
+      int type, range, value;
+      bool survives;
+
+      req_get_values(preq, &type, &range, &survives, &value);
+      packet.effect_id = id;
+      packet.neg = TRUE;
+      packet.source_type = type;
+      packet.source_value = value;
+      packet.range = range;
+      packet.survives = survives;
+
+      lsend_packet_ruleset_effect_req(dest, &packet);
+    } requirement_list_iterate_end;
+
+    id++;
+  } effect_list_iterate_end;
 }
 
 /**************************************************************************
@@ -574,13 +477,19 @@
 {
   /* FIXME: this just returns the first building. it should return the best
    * building instead. */
-  building_vector_iterate(get_buildings_with_effect(effect_type), pbldg) {
-    if (can_player_build_improvement(pplayer, *pbldg)
-       && !improvement_obsolete(pplayer, *pbldg)
-       && is_improvement(*pbldg)) {
-      return *pbldg;
-    }
-  } building_vector_iterate_end;
+  effect_list_iterate(get_effects(effect_type), peffect) {
+    requirement_list_iterate(peffect->reqs, preq) {
+      if (preq->source.type == REQ_BUILDING) {
+       Impr_Type_id id = preq->source.value.building;
+
+       if (can_player_build_improvement(pplayer, id)
+           && !improvement_obsolete(pplayer, id)
+           && is_improvement(id)) {
+         return id;
+       }
+      }
+    } requirement_list_iterate_end;
+  } effect_list_iterate_end;
   return B_LAST;
 }
 
@@ -589,9 +498,13 @@
 **************************************************************************/
 Impr_Type_id get_building_for_effect(enum effect_type effect_type)
 {
-  building_vector_iterate(get_buildings_with_effect(effect_type), pbldg) {
-    return *pbldg;
-  } building_vector_iterate_end;
+  effect_list_iterate(get_effects(effect_type), peffect) {
+    requirement_list_iterate(peffect->reqs, preq) {
+      if (preq->source.type == REQ_BUILDING) {
+       return preq->source.value.building;
+      }
+    } requirement_list_iterate_end;
+  } effect_list_iterate_end;
   return B_LAST;
 }
 
@@ -605,65 +518,86 @@
 **************************************************************************/
 bool building_has_effect(Impr_Type_id id, enum effect_type effect)
 {
-  return (effect_list_size(get_building_effects(id, effect)) > 0);
-}
+  struct req_source source;
+  struct effect_list *plist;
 
-/**************************************************************************
-  Is the effect from the source building redundant on the given target
-  (i.e. are its effects replaced by other sources in the group)?
+  source.type = REQ_BUILDING;
+  source.value.building = id;
 
-  target gives the type of the target
-  (player,pcity,building) gives the exact target
-  source is the source type of the effect
-  peffect is the exact effect
-**************************************************************************/
-static bool is_effect_redundant(enum target_type target,
-                               const struct player *target_player,
-                               const struct city *target_city,
-                               Impr_Type_id target_building,
-                               Impr_Type_id source,
-                               const struct effect *peffect)
-{
-  if (!peffect->group) {
-    /* No group: the effect can't be redundant. */
+  plist = get_req_source_effects(&source);
+
+  if (!plist) {
     return FALSE;
   }
 
-  /* If there is more than one building in the same effects "group", then
-   * only the first one that exists can be active. */
-  effect_group_element_list_iterate(peffect->group->elements, elt) {
-    if (elt->source_building == source) {
-      return FALSE;
-    } else {
-      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! */
-       return TRUE;
-      }
+  effect_list_iterate(plist, peffect) {
+    if (peffect->type == effect) {
+      return TRUE;
     }
-  } effect_group_element_list_iterate_end;
+  } effect_list_iterate_end;
+  return FALSE;
+}
 
+/**************************************************************************
+  Return TRUE iff any of the disabling requirements for this effect are
+  active (an effect is active if all of its enabling requirements and
+  none of its disabling ones are active).
+**************************************************************************/
+bool is_effect_disabled(enum target_type target,
+                       const struct player *target_player,
+                       const struct city *target_city,
+                       Impr_Type_id target_building,
+                       const struct tile *target_tile,
+                       const struct effect *peffect)
+{
+  requirement_list_iterate(peffect->nreqs, preq) {
+    if (is_req_active(target, target_player, target_city, target_building,
+                     target_tile, preq)) {
+      return TRUE;
+    }
+  } requirement_list_iterate_end;
   return FALSE;
 }
 
 /**************************************************************************
-  Checks the requirements of the effect to see if it is active on the
-  given target. (If the requirements are not met the effect should be
-  ignored.)
-**************************************************************************/
-static bool are_effect_reqs_active(enum target_type target,
-                                  const struct player *target_player,
-                                  const struct city *target_city,
-                                  Impr_Type_id target_building,
-                                  const struct tile *target_tile,
-                                  Impr_Type_id source,
-                                  const struct effect *peffect)
+  Return TRUE iff any of the disabling requirements for this effect are
+  active (an effect is active if all of its enabling requirements and
+  none of its disabling ones are active).
+**************************************************************************/
+static bool is_effect_enabled(enum target_type target,
+                             const struct player *target_player,
+                             const struct city *target_city,
+                             Impr_Type_id target_building,
+                             const struct tile *target_tile,
+                             const struct effect *peffect)
+{
+  requirement_list_iterate(peffect->reqs, preq) {
+    if (!is_req_active(target, target_player, target_city, target_building,
+                      target_tile, preq)) {
+      return FALSE;
+    }
+  } requirement_list_iterate_end;
+  return TRUE;
+}
+
+/**************************************************************************
+  Is the effect active at a certain target (player, city or building)?
+
+  This checks whether an effect's requirements are met.
+
+  target gives the type of the target
+  (player,city,building,tile) give the exact target
+  peffect gives the exact effect value
+**************************************************************************/
+static bool is_effect_active(enum target_type target,
+                            const struct player *plr,
+                            const struct city *pcity,
+                            Impr_Type_id building,
+                            const struct tile *ptile,
+                            const struct effect *peffect)
 {
-  return is_req_active(target, target_player, target_city, target_building,
-                      target_tile, &peffect->req);
+  return is_effect_enabled(target, plr, pcity, building, ptile, peffect)
+    && !is_effect_disabled(target, plr, pcity, building, ptile, peffect);
 }
 
 /**************************************************************************
@@ -685,41 +619,21 @@
                      const struct tile *target_tile,
                      Impr_Type_id source, const struct effect *peffect)
 {
-  if (is_effect_redundant(target, target_player, target_city,
-                         target_building, source, peffect)) {
-    return FALSE;
-  }
-  return are_effect_reqs_active(target, target_player, target_city,
-                               target_building, target_tile,
-                               source, peffect);
-}
-
-/**************************************************************************
-  Is the effect from the source building active at a certain target (player,
-  city or building)?
-
-  This checks whether the source exists, whether it is made redundant by
-  another element in its group, and if its requirements are met.
-
-  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
-**************************************************************************/
-static bool is_effect_active(enum target_type target,
-                            const struct player *plr,
-                            const struct city *pcity,
-                            Impr_Type_id building,
-                            const struct tile *ptile,
-                            Impr_Type_id source,
-                            const struct effect *peffect)
-{
-  if (count_buildings_in_range(target, plr, pcity, building, peffect->range,
-                              peffect->survives, source) == 0) {
+  if (is_effect_disabled(target, target_player, target_city,
+                        target_building, target_tile, peffect)) {
     return FALSE;
   }
-  return is_effect_useful(target, plr, pcity, building,
-                         ptile, source, peffect);
+  requirement_list_iterate(peffect->reqs, preq) {
+    if (preq->source.type == REQ_BUILDING
+       && preq->source.value.building == source) {
+      continue;
+    }
+    if (!is_req_active(target, target_player, target_city, target_building,
+                      target_tile, preq)) {
+      return FALSE;
+    }
+  } requirement_list_iterate_end;
+  return TRUE;
 }
 
 /**************************************************************************
@@ -728,58 +642,29 @@
 **************************************************************************/
 bool is_building_replaced(const struct city *pcity, Impr_Type_id building)
 {
-  bool groups_present = FALSE;
+  struct req_source source;
+  struct effect_list *plist;
 
-  /* A building that has no effects is never redundant. */
-  effect_type_vector_iterate(get_building_effect_types(building), ptype) {
-    effect_list_iterate(get_building_effects(building, *ptype), peffect) {
-      /* We use TARGET_BUILDING as the lowest common denominator.  Note that
-       * the building is its own target - but whether this is actually
-       * checked depends on the range of the effect. */
-      if (!is_effect_redundant(TARGET_BUILDING, city_owner(pcity), pcity,
-                              building, building, peffect)) {
-       return FALSE;
-      }
-      if (peffect->group) {
-       groups_present = TRUE;
-      }
-    } effect_list_iterate_end;
-  } effect_type_vector_iterate_end;
-
-  return groups_present;
-}
+  source.type = REQ_BUILDING;
+  source.value.building = building;
 
-/**************************************************************************
-  Get the total value, for one effect type, of one source building type on
-  the given target.
+  plist = get_req_source_effects(&source);
 
-  target gives the type of the target
-  (player,city,building,tile) give the exact target
-  source gives the source type of the effect
-  effect_type gives the effect type to be considered
-**************************************************************************/
-static int get_effect_value(enum target_type target,
-                           const struct player *target_player,
-                           const struct city *target_city,
-                           Impr_Type_id target_building,
-                           const struct tile *target_tile,
-                           Impr_Type_id source,
-                           enum effect_type effect_type)
-{
-  int value = 0;
+  /* A building that has no effects is never redundant. */
+  if (!plist) {
+    return FALSE;
+  }
 
-  /* Loop over all effects of this type provided by the given source. */
-  effect_list_iterate(get_building_effects(source, effect_type), peffect) {
-    /* For each effect, see if it is active. */
-    if (is_effect_active(target, target_player, target_city,
-                        target_building, target_tile,
-                        source, peffect)) {
-      /* And if so add on the value. */
-      value += peffect->value;
+  effect_list_iterate(plist, peffect) {
+    /* We use TARGET_BUILDING as the lowest common denominator.  Note that
+     * the building is its own target - but whether this is actually
+     * checked depends on the range of the effect. */
+    if (!is_effect_disabled(TARGET_BUILDING, city_owner(pcity), pcity,
+                           building, NULL, peffect)) {
+      return FALSE;
     }
   } effect_list_iterate_end;
-
-  return value;
+  return TRUE;
 }
 
 /**************************************************************************
@@ -794,7 +679,7 @@
   The returned vector must be freed (building_vector_free) when the caller
   is done with it.
 **************************************************************************/
-static int get_target_bonus_sources(struct effect_source_vector *sources,
+static int get_target_bonus_effects(struct effect_list *plist,
                                    enum target_type target,
                                    const struct player *target_player,
                                    const struct city *target_city,
@@ -804,31 +689,19 @@
 {
   int bonus = 0;
 
-  if (sources) {
-    effect_source_vector_init(sources);
-  }
-
-  /* Loop over all sources that may provide this effect. */
-  building_vector_iterate(get_buildings_with_effect(effect_type), pbldg) {
-    int value;
-
-    /* And for each source, add on the amount of effect provided by it. */
-    value = get_effect_value(target, target_player, target_city,
-                            target_building, target_tile,
-                            *pbldg, effect_type);
-    bonus += value;
-
-    if (sources) {
-      struct effect_source e;
-
-      e.building = *pbldg;
-      e.effect_value = value;
+  /* Loop over all effects of this type. */
+  effect_list_iterate(get_effects(effect_type), peffect) {
+    /* For each effect, see if it is active. */
+    if (is_effect_active(target, target_player, target_city,
+                        target_building, target_tile, peffect)) {
+      /* And if so add on the value. */
+      bonus += peffect->value;
 
-      if (value != 0) {
-       effect_source_vector_append(sources, &e);
+      if (plist) {
+       effect_list_append(plist, peffect);
       }
     }
-  } building_vector_iterate_end;
+  } effect_list_iterate_end;
 
   return bonus;
 }
@@ -839,8 +712,8 @@
 int get_player_bonus(const struct player *pplayer,
                     enum effect_type effect_type)
 {
-  return get_target_bonus_sources(NULL, TARGET_PLAYER,
-                                 pplayer, NULL, B_LAST, NULL,
+  return get_target_bonus_effects(NULL, TARGET_PLAYER,
+                                 pplayer, NULL, B_LAST, NULL,
                                  effect_type);
 }
 
@@ -849,7 +722,7 @@
 **************************************************************************/
 int get_city_bonus(const struct city *pcity, enum effect_type effect_type)
 {
-  return get_target_bonus_sources(NULL, TARGET_CITY,
+  return get_target_bonus_effects(NULL, TARGET_CITY,
                                  city_owner(pcity), pcity, B_LAST, NULL,
                                  effect_type);
 }
@@ -860,8 +733,8 @@
 int get_city_tile_bonus(const struct city *pcity, const struct tile *ptile,
                        enum effect_type effect_type)
 {
-  return get_target_bonus_sources(NULL, TARGET_CITY,
-                                 city_owner(pcity), pcity, B_LAST, ptile,
+  return get_target_bonus_effects(NULL, TARGET_CITY,
+                                 city_owner(pcity), pcity, B_LAST, ptile,
                                  effect_type);
 }
 
@@ -871,8 +744,8 @@
 int get_building_bonus(const struct city *pcity, Impr_Type_id id,
                       enum effect_type effect_type)
 {
-  return get_target_bonus_sources(NULL, TARGET_BUILDING,
-                                 city_owner(pcity), pcity, id, NULL,
+  return get_target_bonus_effects(NULL, TARGET_CITY,
+                                 city_owner(pcity), pcity, id, NULL,
                                  effect_type);
 }
 
@@ -882,10 +755,11 @@
   The returned vector must be freed (building_vector_free) when the caller
   is done with it.
 **************************************************************************/
-int get_player_bonus_sources(struct effect_source_vector *sources,
-    const struct player *pplayer, enum effect_type effect_type)
+int get_player_bonus_effects(struct effect_list *plist,
+                            const struct player *pplayer,
+                            enum effect_type effect_type)
 {
-  return get_target_bonus_sources(sources, TARGET_PLAYER,
+  return get_target_bonus_effects(plist, TARGET_PLAYER,
                                  pplayer, NULL, B_LAST, NULL,
                                  effect_type);
 }
@@ -896,10 +770,11 @@
   The returned vector must be freed (building_vector_free) when the caller
   is done with it.
 **************************************************************************/
-int get_city_bonus_sources(struct effect_source_vector *sources,
-    const struct city *pcity, enum effect_type effect_type)
+int get_city_bonus_effects(struct effect_list *plist,
+                          const struct city *pcity,
+                          enum effect_type effect_type)
 {
-  return get_target_bonus_sources(sources, TARGET_CITY,
+  return get_target_bonus_effects(plist, TARGET_CITY,
                                  city_owner(pcity), pcity, B_LAST, NULL,
                                  effect_type);
 }
@@ -914,19 +789,64 @@
                                   enum effect_type effect_type)
 {
   if (!pcity->is_building_unit) {
-    Impr_Type_id bldg = pcity->currently_building;
+    Impr_Type_id id = pcity->currently_building;
     int power = 0;
 
-    effect_list_iterate(get_building_effects(bldg, effect_type), peffect) {
+    struct req_source source = {
+      .type = REQ_BUILDING,
+      .value.building = id
+    };
+    struct effect_list *plist = get_req_source_effects(&source);
+
+    effect_list_iterate(plist, peffect) {
+      if (peffect->type != effect_type) {
+       continue;
+      }
       if (is_effect_useful(TARGET_BUILDING, city_owner(pcity),
-                          pcity, bldg, NULL, bldg, peffect)) {
+                          pcity, id, NULL, id, peffect)) {
        power += peffect->value;
       }
     } effect_list_iterate_end;
 
     return power;
   }
-
   return 0;
 }
 
+/**************************************************************************
+**************************************************************************/
+void get_effect_req_text(struct effect *peffect, char *buf, size_t buf_len)
+{
+  buf[0] = '\0';
+
+  requirement_list_iterate(peffect->reqs, preq) {
+    struct req_source *psource = &preq->source;
+
+    if (buf[0] != '\0') {
+      mystrlcat(buf, "+", buf_len);
+    }
+
+    switch (psource->type) {
+      case REQ_NONE:
+       break;
+      case REQ_TECH:
+       mystrlcat(buf, advances[psource->value.tech].name, buf_len);
+       break;
+      case REQ_GOV:
+       mystrlcat(buf, get_government_name(psource->value.gov), buf_len);
+       break;
+      case REQ_BUILDING:
+       mystrlcat(buf, get_improvement_name(psource->value.building), buf_len);
+       break;
+      case REQ_SPECIAL:
+       mystrlcat(buf, get_special_name(psource->value.special), buf_len);
+       break;
+      case REQ_TERRAIN:
+       mystrlcat(buf, get_terrain_name(psource->value.terrain), buf_len);
+       break;
+      case REQ_LAST:
+       break;
+    }
+  } requirement_list_iterate_end;
+}
+
Index: common/effects.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/effects.h,v
retrieving revision 1.13
diff -u -r1.13 effects.h
--- common/effects.h    17 Dec 2004 08:21:20 -0000      1.13
+++ common/effects.h    31 Jan 2005 05:24:45 -0000
@@ -112,53 +112,27 @@
 enum effect_type effect_type_from_str(const char *str);
 const char *effect_type_name(enum effect_type effect_type);
 
-/* A building_vector is an array of building types. */
-#define SPECVEC_TAG building
-#define SPECVEC_TYPE Impr_Type_id
-#include "specvec.h"
-#define building_vector_iterate(vector, pbldg) \
-  TYPED_VECTOR_ITERATE(Impr_Type_id, vector, pbldg)
-#define building_vector_iterate_end  VECTOR_ITERATE_END
-
-/* An effect_type_vector is an array of effect types. */
-#define SPECVEC_TAG effect_type
-#define SPECVEC_TYPE enum effect_type
-#include "specvec.h"
-#define effect_type_vector_iterate(vector, ptype) \
-  TYPED_VECTOR_ITERATE(enum effect_type, vector, ptype)
-#define effect_type_vector_iterate_end VECTOR_ITERATE_END
-
-struct effect_group;
-
 /* An effect is provided by a source.  If the source is present, and the
  * other conditions (described below) are met, the effect will be active.
- * Note the difference between effect and effect_type.  The effect doesn't
- * give an effect_type because all effects in an effect_vector will be of
- * the same type. */
+ * Note the difference between effect and effect_type. */
 struct effect {
-  /* 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 req_range range;
+  enum effect_type type;
 
   /* The "value" of the effect.  The meaning of this varies between
    * effects.  When get_xxx_bonus() is called the value of all applicable
    * effects will be summed up. */
   int value;
 
-  /* The group this effect is in.  If more than one source in the group
-   * provides the effect, only one will be active. */
-  struct effect_group *group;
-
-  /* If true, this effect will survive even if its source is destroyed. */
-  bool survives;
-
-  /* An effect can have a single requirement.  The effect will only be
-   * active if this requirement is met.  The req is one of several types. */
-  struct requirement req;
+  /* An effect can have multiple requirements.  The effect will only be
+   * active if all of these requirement are met. */
+  struct requirement_list *reqs;
+
+  /* An effect can have multiple negated requirements.  The effect will
+   * only be active if none of these requirements are met. */
+  struct requirement_list *nreqs;
 };
 
-/* An effect_vector is an array of effects. */
+/* An effect_list is a list of effects. */
 #define SPECLIST_TAG effect
 #define SPECLIST_TYPE struct effect
 #include "speclist.h"
@@ -166,35 +140,22 @@
   TYPED_LIST_ITERATE(struct effect, effect_list, peffect)
 #define effect_list_iterate_end LIST_ITERATE_END
 
-/* This struct contains a source building along with the effect value.  It is
- * primarily useful for get_city_bonus_sources(). */
-struct effect_source {
-  Impr_Type_id building;
-  int effect_value;
-};
-#define SPECVEC_TAG effect_source
-#define SPECVEC_TYPE struct effect_source
-#include "specvec.h"
-#define effect_source_vector_iterate(vector, psource) \
-  TYPED_VECTOR_ITERATE(struct effect_source, vector, psource)
-#define effect_source_vector_iterate_end VECTOR_ITERATE_END
+struct effect *effect_new(enum effect_type type, int value);
+void effect_req_append(struct effect *peffect, bool neg,
+                      struct requirement *preq);
+
+void get_effect_req_text(struct effect *peffect, char *buf, size_t buf_len);
 
 /* ruleset cache creation and communication functions */
+struct packet_ruleset_effect;
+struct packet_ruleset_effect_req;
+
 void ruleset_cache_init(void);
 void ruleset_cache_free(void);
-void ruleset_cache_add(Impr_Type_id source, enum effect_type effect_type,
-                      enum req_range range, bool survives, int eff_value,
-                      struct requirement *req,
-                      int group_id);
+void recv_ruleset_effect(struct packet_ruleset_effect *packet);
+void recv_ruleset_effect_req(struct packet_ruleset_effect_req *packet);
 void send_ruleset_cache(struct conn_list *dest);
 
-/* equivalent effect group */
-struct effect_group *effect_group_new(const char *name);
-void effect_group_add_element(struct effect_group *group,
-                             Impr_Type_id source_building,
-                             enum req_range range, bool survives);
-int find_effect_group_id(const char *name);
-
 bool is_effect_useful(enum target_type target,
                      const struct player *target_player,
                      const struct city *target_pcity,
@@ -213,13 +174,17 @@
                       enum effect_type effect_type);
 
 /* miscellaneous auxiliary effects functions */
-struct effect_list *get_building_effects(Impr_Type_id building,
-                                        enum effect_type effect_type);
-struct effect_type_vector *get_building_effect_types(Impr_Type_id building);
+struct effect_list *get_req_source_effects(struct req_source *psource);
+bool is_effect_disabled(enum target_type target,
+                       const struct player *target_player,
+                       const struct city *target_city,
+                       Impr_Type_id target_building,
+                       const struct tile *target_tile,
+                       const struct effect *peffect);
 
-int get_player_bonus_sources(struct effect_source_vector *sources,
+int get_player_bonus_effects(struct effect_list *plist,
     const struct player *pplayer, enum effect_type effect_type);
-int get_city_bonus_sources(struct effect_source_vector *sources,
+int get_city_bonus_effects(struct effect_list *plist,
     const struct city *pcity, enum effect_type effect_type);
 
 bool building_has_effect(Impr_Type_id building,
Index: common/packets.def
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/packets.def,v
retrieving revision 1.91
diff -u -r1.91 packets.def
--- common/packets.def  29 Jan 2005 17:58:18 -0000      1.91
+++ common/packets.def  31 Jan 2005 05:24:45 -0000
@@ -181,7 +181,7 @@
 type DIRECTION         = uint8(enum direction8)
 type ORDERS            = uint8(enum unit_orders)
 type SSET_TYPE         = uint8(enum sset_type)
-type REQ_TYPE          = uint8(enum req_type)
+type REQ_TYPE          = uint8(enum req_source_type)
 type REQ_RANGE         = uint8(enum req_range)
 type EFFECT_TYPE       = uint8(enum effect_type)
 
@@ -1266,23 +1266,19 @@
 
 /************** Effects hash packets **********************/
 
-PACKET_RULESET_CACHE_GROUP=120;sc,lsend
-  STRING name[MAX_LEN_NAME];
-
-  UINT8 num_elements;
-  IMPROVEMENT source_buildings[255:num_elements];
-  REQ_RANGE ranges[255:num_elements];
-  BOOL survives[255:num_elements];
+PACKET_RULESET_EFFECT=122;sc,lsend
+  EFFECT_TYPE effect_type;
+  SINT32 effect_value;
 end
 
-PACKET_RULESET_CACHE_EFFECT=121;sc,lsend
-  IMPROVEMENT id;
-  EFFECT_TYPE effect_type;
+PACKET_RULESET_EFFECT_REQ=123;sc,lsend
+  UINT32 effect_id;
+  BOOL neg;
+       
+  REQ_TYPE source_type;
+  SINT32 source_value;
+
   REQ_RANGE range;
   BOOL survives;
-  SINT32 eff_value;
-  REQ_TYPE req_type;
-  SINT32 req_value;
-  SINT32 group_id;
 end
 
Index: common/requirements.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/requirements.c,v
retrieving revision 1.4
diff -u -r1.4 requirements.c
--- common/requirements.c       19 Dec 2004 16:47:09 -0000      1.4
+++ common/requirements.c       31 Jan 2005 05:24:45 -0000
@@ -25,9 +25,9 @@
 #include "map.h"
 #include "requirements.h"
 
-/* Names of requirement types.  These must correspond to enum req_type in
+/* Names of source types.  These must correspond to enum req_source_type in
  * requirements.h.  Do not change these unless you know what you're doing! */
-static const char *req_type_names[] = {
+static const char *req_source_type_names[] = {
   "None",
   "Tech",
   "Gov",
@@ -67,86 +67,63 @@
   return REQ_RANGE_LAST;
 }
 
-/****************************************************************************
-  Parse a requirement type and value string into a requrement structure.
-  Returns REQ_LAST on error.  Passing in a NULL type is considered REQ_NONE
-  (not an error).
+/**************************************************************************
+  Parse a requirement type and value string into a requirement source
+  structure.  Passing in a NULL type is considered REQ_NONE (not an error).
 
   Pass this some values like "Building", "Factory".
-****************************************************************************/
-struct requirement req_from_str(const char *type,
-                               const char *range, bool survives,
-                               const char *value)
+**************************************************************************/
+struct req_source req_source_from_str(const char *type, const char *value)
 {
-  struct requirement req;
+  struct req_source source;
   const struct government *pgov;
 
-  assert(ARRAY_SIZE(req_type_names) == REQ_LAST);
+  assert(ARRAY_SIZE(req_source_type_names) == REQ_LAST);
   if (type) {
-    for (req.type = 0; req.type < ARRAY_SIZE(req_type_names); req.type++) {
-      if (0 == mystrcasecmp(req_type_names[req.type], type)) {
+    for (source.type = 0;
+        source.type < ARRAY_SIZE(req_source_type_names);
+        source.type++) {
+      if (0 == mystrcasecmp(req_source_type_names[source.type], type)) {
        break;
       }
     }
   } else {
-    req.type = REQ_NONE;
-  }
-
-  /* Scan the range string to find the range.  If no range is given a
-   * default fallback is used rather than giving an error. */
-  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;
-    }
+    source.type = REQ_NONE;
   }
 
-  req.survives = survives;
-
-  /* Finally scan the value string based on the type of the req. */
-  switch (req.type) {
+  /* Finally scan the value string based on the type of the source. */
+  switch (source.type) {
   case REQ_NONE:
-    return req;
+    return source;
   case REQ_TECH:
-    req.value.tech = find_tech_by_name(value);
-    if (req.value.tech != A_LAST) {
-      return req;
+    source.value.tech = find_tech_by_name(value);
+    if (source.value.tech != A_LAST) {
+      return source;
     }
     break;
   case REQ_GOV:
     pgov = find_government_by_name(value);
     if (pgov != NULL) {
-      req.value.gov = pgov->index;
-      return req;
+      source.value.gov = pgov->index;
+      return source;
     }
     break;
   case REQ_BUILDING:
-    req.value.building = find_improvement_by_name(value);
-    if (req.value.building != B_LAST) {
-      return req;
+    source.value.building = find_improvement_by_name(value);
+    if (source.value.building != B_LAST) {
+      return source;
     }
     break;
   case REQ_SPECIAL:
-    req.value.special = get_special_by_name(value);
-    if (req.value.special != S_NO_SPECIAL) {
-      return req;
+    source.value.special = get_special_by_name(value);
+    if (source.value.special != S_NO_SPECIAL) {
+      return source;
     }
     break;
   case REQ_TERRAIN:
-    req.value.terrain = get_terrain_by_name(value);
-    if (req.value.terrain != T_UNKNOWN) {
-      return req;
+    source.value.terrain = get_terrain_by_name(value);
+    if (source.value.terrain != T_UNKNOWN) {
+      return source;
     }
     break;
   case REQ_LAST:
@@ -154,39 +131,74 @@
   }
 
   /* If we reach here there's been an error. */
-  req.type = REQ_LAST;
-  return req;
+  source.type = REQ_LAST;
+  return source;
 }
 
-/****************************************************************************
-  Return the value of a req as a serializable integer.  This is the opposite
-  of req_set_value.
-****************************************************************************/
-void req_get_values(struct requirement *req,
-                   int *type, int *range, bool *survives, int *value)
+/**************************************************************************
+  Parse some integer values into a req source.  This is for serialization
+  of req sources and is the opposite of req_source_get_values().
+**************************************************************************/
+struct req_source req_source_from_values(int type, int value)
 {
-  *type = req->type;
-  *range = req->range;
-  *survives = req->survives;
+  struct req_source source;
+
+  source.type = type;
+
+  switch (source.type) {
+  case REQ_NONE:
+    return source;
+  case REQ_TECH:
+    source.value.tech = value;
+    return source;
+  case REQ_GOV:
+    source.value.gov = value;
+    return source;
+  case REQ_BUILDING:
+    source.value.building = value;
+    return source;
+  case REQ_SPECIAL:
+    source.value.special = value;
+    return source;
+  case REQ_TERRAIN:
+    source.value.terrain = value;
+    return source;
+  case REQ_LAST:
+    return source;
+  }
+
+  source.type = REQ_LAST;
+  assert(0);
+  return source;
+}
+
+/**************************************************************************
+  Look at a req source and return some integer values describing it.  This
+  is for serialization of req sources and is the opposite of
+  req_source_from_values().
+**************************************************************************/
+void req_source_get_values(struct req_source *source, int *type, int *value)
+{
+  *type = source->type;
 
-  switch (req->type) {
+  switch (source->type) {
   case REQ_NONE:
     *value = 0;
     return;
   case REQ_TECH:
-    *value = req->value.tech;
+    *value = source->value.tech;
     return;
   case REQ_GOV:
-    *value = req->value.gov;
+    *value = source->value.gov;
     return;
   case REQ_BUILDING:
-    *value = req->value.building;
+    *value = source->value.building;
     return;
   case REQ_SPECIAL:
-    *value = req->value.special;
+    *value = source->value.special;
     return;
   case REQ_TERRAIN:
-    *value = req->value.terrain;
+    *value = source->value.terrain;
     return;
   case REQ_LAST:
     break;
@@ -197,6 +209,45 @@
 }
 
 /****************************************************************************
+  Parse a requirement type and value string into a requrement structure.
+  Returns REQ_LAST on error.  Passing in a NULL type is considered REQ_NONE
+  (not an error).
+
+  Pass this some values like "Building", "Factory".
+****************************************************************************/
+struct requirement req_from_str(const char *type,
+                               const char *range, bool survives,
+                               const char *value)
+{
+  struct requirement req;
+
+  req.source = req_source_from_str(type, value);
+
+  /* Scan the range string to find the range.  If no range is given a
+   * default fallback is used rather than giving an error. */
+  req.range = req_range_from_str(range);
+  if (req.range == REQ_RANGE_LAST) {
+    switch (req.source.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;
+  return req;
+}
+
+/****************************************************************************
   Set the values of a req from serializable integers.  This is the opposite
   of req_get_values.
 ****************************************************************************/
@@ -205,38 +256,25 @@
 {
   struct requirement req;
 
-  req.type = type;
+  req.source = req_source_from_values(type, value);
   req.range = range;
   req.survives = survives;
-
-  switch (req.type) {
-  case REQ_NONE:
-    return req;
-  case REQ_TECH:
-    req.value.tech = value;
-    return req;
-  case REQ_GOV:
-    req.value.gov = value;
-    return req;
-  case REQ_BUILDING:
-    req.value.building = value;
-    return req;
-  case REQ_SPECIAL:
-    req.value.special = value;
-    return req;
-  case REQ_TERRAIN:
-    req.value.terrain = value;
-    return req;
-  case REQ_LAST:
-    return req;
-  }
-
-  req.type = REQ_LAST;
-  assert(0);
   return req;
 }
 
 /****************************************************************************
+  Return the value of a req as a serializable integer.  This is the opposite
+  of req_set_value.
+****************************************************************************/
+void req_get_values(struct requirement *req,
+                   int *type, int *range, bool *survives, int *value)
+{
+  req_source_get_values(&req->source, type, value);
+  *range = req->range;
+  *survives = req->survives;
+}
+
+/****************************************************************************
   Is this target possible for the range involved?
 
   For instance a city-ranged requirement can't have a player as it's target.
@@ -452,29 +490,34 @@
    * have a REQ_SPECIAL or REQ_TERRAIN may often be passed to this function
    * with a city as their target.  In this case the requirement is simply
    * not met. */
-  switch (req->type) {
+  switch (req->source.type) {
   case REQ_NONE:
     return TRUE;
   case REQ_TECH:
     /* The requirement is filled if the player owns the tech. */
     return (target_player
-           && get_invention(target_player, req->value.tech) == TECH_KNOWN);
+           && get_invention(target_player,
+                            req->source.value.tech) == TECH_KNOWN);
   case REQ_GOV:
     /* The requirement is filled if the player is using the government. */
-    return target_player && (target_player->government == req->value.gov);
+    return (target_player
+           && (target_player->government == req->source.value.gov));
   case REQ_BUILDING:
     /* 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_buildings_in_range(target, target_player, target_city,
-                                    target_building, REQ_RANGE_CITY, FALSE,
-                                    req->value.building) > 0);
+                                    target_building,
+                                    req->range, req->survives,
+                                    req->source.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);
+    return (target_tile
+           && tile_has_special(target_tile, req->source.value.special));
   case REQ_TERRAIN:
     /* The requirement is filled if the tile has the terrain. */
-    return target_tile && (target_tile->terrain  == req->value.terrain);
+    return (target_tile
+           && (target_tile->terrain  == req->source.value.terrain));
   case REQ_LAST:
     break;
   }
Index: common/requirements.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/requirements.h,v
retrieving revision 1.2
diff -u -r1.2 requirements.h
--- common/requirements.h       17 Dec 2004 08:21:20 -0000      1.2
+++ common/requirements.h       31 Jan 2005 05:24:45 -0000
@@ -17,9 +17,9 @@
 #include "fc_types.h"
 #include "tech.h"
 
-/* The type of a requirement.  This must correspond to req_type_names[]
+/* The type of a requirement source.  This must correspond to req_type_names[]
  * in requirements.c. */
-enum req_type {
+enum req_source_type {
   REQ_NONE,
   REQ_TECH,
   REQ_GOV,
@@ -49,25 +49,41 @@
   TARGET_BUILDING 
 };
 
+/* A requirement source. */
+struct req_source {
+  enum req_source_type type;            /* source type */
+
+  union {
+    Tech_Type_id tech;                  /* source tech */
+    int gov;                            /* source government */
+    Impr_Type_id building;              /* source building */
+    enum tile_special_type special;     /* source special */
+    Terrain_type_id terrain;            /* source terrain type */
+  } value;                              /* source value */
+};
+
 /* A requirement. This requirement is basically a conditional; it may or
  * may not be active on a target.  If it is active then something happens.
  * For instance units and buildings have requirements to be built, techs
  * have requirements to be researched, and effects have requirements to be
  * active. */
 struct requirement {
-  enum req_type type;                  /* requirement type */
+  struct req_source source;            /* requirement source */
   enum req_range range;                        /* requirement range */
   bool survives; /* set if destroyed sources satisfy the req*/
-
-  union {
-    Tech_Type_id tech;                 /* requirement tech */
-    int gov;                           /* requirement government */
-    Impr_Type_id building;             /* requirement building */
-    enum tile_special_type special;    /* requirement special */
-    Terrain_type_id terrain;           /* requirement terrain type */
-  } value;                             /* requirement value */
 };
 
+#define SPECLIST_TAG requirement
+#define SPECLIST_TYPE struct requirement
+#include "speclist.h"
+#define requirement_list_iterate(req_list, preq) \
+  TYPED_LIST_ITERATE(struct requirement, req_list, preq)
+#define requirement_list_iterate_end LIST_ITERATE_END
+
+struct req_source req_source_from_str(const char *type, const char *value);
+struct req_source req_source_from_values(int type, int value);
+void req_source_get_values(struct req_source *source, int *type, 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);
Index: server/plrhand.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/plrhand.c,v
retrieving revision 1.355
diff -u -r1.355 plrhand.c
--- server/plrhand.c    22 Jan 2005 21:12:10 -0000      1.355
+++ server/plrhand.c    31 Jan 2005 05:24:46 -0000
@@ -130,21 +130,21 @@
 void do_tech_parasite_effect(struct player *pplayer)
 {
   int mod;
-  struct effect_source_vector sources;
+  struct effect_list *plist = effect_list_new();
 
   /* Note that two EFT_TECH_PARASITE effects will combine into a single,
    * much worse effect. */
-  if ((mod = get_player_bonus_sources(&sources, pplayer,
+  if ((mod = get_player_bonus_effects(plist, pplayer,
                                      EFT_TECH_PARASITE)) > 0) {
     char buf[512];
 
     buf[0] = '\0';
-    effect_source_vector_iterate(&sources, src) {
+    effect_list_iterate(plist, peffect) {
       if (buf[0] != '\0') {
        sz_strlcat(buf, ", ");
       }
-      sz_strlcat(buf, get_improvement_name(src->building));
-    } effect_source_vector_iterate_end;
+      get_effect_req_text(peffect, buf, sizeof(buf));
+    } effect_list_iterate_end;
 
     tech_type_iterate(i) {
       if (get_invention(pplayer, i) != TECH_KNOWN
@@ -165,7 +165,8 @@
       }
     } tech_type_iterate_end;
   }
-  effect_source_vector_free(&sources);
+  effect_list_unlink_all(plist);
+  effect_list_free(plist);
 }
 
 /****************************************************************************
Index: server/ruleset.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/ruleset.c,v
retrieving revision 1.229
diff -u -r1.229 ruleset.c
--- server/ruleset.c    29 Jan 2005 17:58:19 -0000      1.229
+++ server/ruleset.c    31 Jan 2005 05:24:47 -0000
@@ -90,6 +90,7 @@
 static void load_ruleset_terrain(struct section_file *file);
 static void load_ruleset_cities(struct section_file *file);
 static void load_ruleset_nations(struct section_file *file);
+static void load_ruleset_effects(struct section_file *file);
 
 static void load_ruleset_game(void);
 
@@ -1157,57 +1158,6 @@
 
   (void) check_ruleset_capabilities(file, "+1.10.1", filename);
 
-  /* Parse effect source equivalency groups. */
-  sec = secfile_get_secnames_prefix(file, "group_", &nval);
-  for (i = 0; i < nval; i++) {
-    struct effect_group *group;
-    char name[MAX_LEN_NAME];
-
-    item = secfile_lookup_str(file, "%s.name", sec[i]);
-    sz_strlcpy(name, item);
-
-    group = effect_group_new(name);
-
-    for (j = 0;
-        (item = secfile_lookup_str_default(file, NULL,
-                                           "%s.elements%d.building",
-                                           sec[i], j));
-        j++) {
-      Impr_Type_id id;
-      enum req_range range;
-      bool survives;
-
-      if ((id = find_improvement_by_name(item)) == B_LAST) {
-       freelog(LOG_ERROR,
-               /* TRANS: Obscure ruleset error */
-               _("Group %s lists unknown building: \"%s\" (%s)"),
-               name, item, filename);
-       continue;
-      }
-
-      item = secfile_lookup_str_default(file, NULL, "%s.elements%d.range",
-                                       sec[i], j);
-      if (item) {
-       if ((range = req_range_from_str(item)) == REQ_RANGE_LAST) {
-         freelog(LOG_ERROR,
-                 /* TRANS: Obscure ruleset error */
-                 _("Group %s lists bad range: \"%s\" (%s)"),
-                 name, item, filename);
-         continue;
-       }
-      } else {
-       range = REQ_RANGE_CITY;
-      }
-
-      survives
-       = secfile_lookup_bool_default(file, FALSE, "%s.elements%d.survives",
-                                     sec[i], j);
-
-      effect_group_add_element(group, id, range, survives);
-    }
-  }
-  free(sec);
-
   sec = secfile_get_secnames_prefix(file, "building_", &nval);
 
   for (i = 0; i < nval; i++) {
@@ -1279,83 +1229,6 @@
 
     b->sabotage = secfile_lookup_int(file, "%s.sabotage", sec[i]);
 
-    /* Parse building effects and add them to the effects ruleset cache. */
-    {
-      for (j = 0;
-          (item = secfile_lookup_str_default(file, NULL, "%s.effect%d.name",
-                                             sec[i], j));
-          j++) {
-       int value;
-       enum effect_type eff;
-       enum req_range range;
-       bool survives;
-       struct requirement req;
-       char *req_type, *req_value;
-       int equiv;
-
-       if ((eff = effect_type_from_str(item)) == EFT_LAST) {
-         freelog(LOG_ERROR,
-                 /* TRANS: Obscure ruleset error */
-                 _("Building %s lists unknown effect type: \"%s\" (%s)"),
-                 b->name, item, filename);
-         continue;
-       }
-
-       item = secfile_lookup_str_default(file, NULL, "%s.effect%d.range",
-                                         sec[i], j);
-       if (item) {
-         if ((range = req_range_from_str(item)) == REQ_RANGE_LAST) {
-           freelog(LOG_ERROR,
-                   /* TRANS: Obscure ruleset error */
-                   _("Building %s lists bad range: \"%s\" (%s)"),
-                   b->name, item, filename);
-           continue;
-         }
-       } else {
-         range = REQ_RANGE_CITY;
-       }
-
-       survives = secfile_lookup_bool_default(file, FALSE, 
"%s.effect%d.survives",
-                                              sec[i], j);
-
-       value = secfile_lookup_int_default(file, 1, "%s.effect%d.value",
-                                          sec[i], j);
-
-       /* Sometimes the ruleset will have to list "" here. */
-       item = secfile_lookup_str_default(file, "", "%s.effect%d.equiv",
-                                         sec[i], j);
-       if (item[0] != '\0') {
-         if ((equiv = find_effect_group_id(item)) == -1) {
-           freelog(LOG_ERROR,
-                   /* TRANS: Obscure ruleset error */
-                   _("Building %s lists bad effect group: \"%s\" (%s)"),
-                   b->name, item, filename);
-           continue;
-         }
-       } else {
-         equiv = -1;
-       }
-
-       /* Sometimes the ruleset will have to list "" here. */
-       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, "", FALSE, req_value);
-       if (req.type == REQ_LAST) {
-         /* Error.  Log it, clear the req and continue. */
-         freelog(LOG_ERROR,
-                 /* TRANS: Obscure ruleset error */
-                 _("Building %s has unknown req: \"%s\" \"%s\" (%s)"),
-                 b->name, req_type, req_value, filename);
-         req.type = REQ_NONE;
-       }
-
-       ruleset_cache_add(i, eff, range, survives, value, &req, equiv);
-      }
-    }
-
     sz_strlcpy(b->graphic_str,
               secfile_lookup_str_default(file, "-", "%s.graphic", sec[i]));
     sz_strlcpy(b->graphic_alt,
@@ -1369,26 +1242,6 @@
     b->helptext = lookup_helptext(file, sec[i]);
   }
 
-  /*
-   * Hack to allow code that explicitly checks for Palace or City Walls
-   * to work.
-   */
-  game.palace_building = get_building_for_effect(EFT_CAPITAL_CITY);
-  if (game.palace_building == B_LAST) {
-    freelog(LOG_FATAL,
-           /* TRANS: Obscure ruleset error */
-           _("Cannot find any palace building"));
-    exit(EXIT_FAILURE);
-  }
-
-  game.land_defend_building = get_building_for_effect(EFT_LAND_DEFEND);
-  if (game.land_defend_building == B_LAST) {
-    freelog(LOG_FATAL,
-           /* TRANS: Obscure ruleset error */
-           _("Cannot find any land defend building"));
-    exit(EXIT_FAILURE);
-  }
-
   /* Some more consistency checking: */
   impr_type_iterate(i) {
     b = &improvement_types[i];
@@ -2409,11 +2262,11 @@
                                     name, j);
       struct requirement req = req_from_str(type, range, survives, value);
 
-      if (req.type == REQ_LAST) {
+      if (req.source.type == REQ_LAST) {
        freelog(LOG_ERROR,
                "Specialist %s has unknown req: \"%s\" \"%s\" \"%s\" %d (%s)",
                name, type, range, value, survives, filename);
-       req.type = REQ_NONE;
+       req.source.type = REQ_NONE;
       }
 
       game.rgame.specialists[i].req[j] = req;
@@ -2490,9 +2343,118 @@
 }
 
 /**************************************************************************
+Load effects requirement list.
+**************************************************************************/
+static void load_req_list(struct section_file *file,
+                         const char *sec, const char *sub,
+                         bool neg, struct effect *peffect)
+{
+  char *type, *name;
+  int j;
+  const char *filename;
+
+  filename = secfile_filename(file);
+
+  for (j = 0;
+      (type = secfile_lookup_str_default(file, NULL, "%s.%s%d.type",
+                                        sec, sub, j));
+      j++) {
+    char *range;
+    bool survives;
+    struct requirement req;
+
+    name = secfile_lookup_str(file, "%s.%s%d.name", sec, sub, j);
+    range = secfile_lookup_str(file, "%s.%s%d.range", sec, sub, j);
+
+    survives = secfile_lookup_bool_default(file, FALSE,
+       "%s.%s%d.survives", sec, sub, j);
+
+    req = req_from_str(type, range, survives, name);
+    if (req.source.type == REQ_LAST) {
+      /* Error.  Log it, clear the req and continue. */
+      freelog(LOG_ERROR,
+         /* TRANS: Obscure ruleset error */
+         _("Section %s has unknown req: \"%s\" \"%s\" (%s)"),
+         sec, type, name, filename);
+      req.source.type = REQ_NONE;
+    } else {
+      struct requirement *preq;
+
+      preq = fc_malloc(sizeof(*preq));
+      *preq = req;
+
+      effect_req_append(peffect, neg, preq);
+    }
+  }
+}
+
+/**************************************************************************
+Load effects.ruleset file
+**************************************************************************/
+static void load_ruleset_effects(struct section_file *file)
+{
+  char **sec, *type;
+  int i, nval;
+  const char *filename;
+
+  filename = secfile_filename(file);
+  (void) check_ruleset_capabilities(file, "+1.0", filename);
+  (void) section_file_lookup(file, "datafile.description");    /* unused */
+
+  /* Parse effects and add them to the effects ruleset cache. */
+  sec = secfile_get_secnames_prefix(file, "effect_", &nval);
+  for (i = 0; i < nval; i++) {
+    enum effect_type eff;
+    int value;
+    struct effect *peffect;
+
+    type = secfile_lookup_str(file, "%s.name", sec[i]);
+
+    if ((eff = effect_type_from_str(type)) == EFT_LAST) {
+      freelog(LOG_ERROR,
+             /* TRANS: Obscure ruleset error */
+             _("Section %s lists unknown effect type: \"%s\" (%s)"),
+             sec[i], type, filename);
+      continue;
+    }
+
+    value = secfile_lookup_int_default(file, 1, "%s.value", sec[i]);
+
+    peffect = effect_new(eff, value);
+
+    load_req_list(file, sec[i], "reqs", FALSE, peffect);
+    load_req_list(file, sec[i], "nreqs", TRUE, peffect);
+  }
+  free(sec);
+
+  section_file_check_unused(file, filename);
+  section_file_free(file);
+
+  /*
+   * Hack to allow code that explicitly checks for Palace or City Walls
+   * to work.
+   */
+  game.palace_building = get_building_for_effect(EFT_CAPITAL_CITY);
+  if (game.palace_building == B_LAST) {
+    freelog(LOG_FATAL,
+           /* TRANS: Obscure ruleset error */
+           _("Cannot find any palace building"));
+    exit(EXIT_FAILURE);
+  }
+
+  game.land_defend_building = get_building_for_effect(EFT_LAND_DEFEND);
+  if (game.land_defend_building == B_LAST) {
+    freelog(LOG_FATAL,
+           /* TRANS: Obscure ruleset error */
+           _("Cannot find any land defend building"));
+    exit(EXIT_FAILURE);
+  }
+}
+
+/**************************************************************************
 Load ruleset file
 **************************************************************************/
-static void load_ruleset_game()
+static void load_ruleset_game(void)
 {
   struct section_file file;
   char *sval;
@@ -3061,7 +3023,7 @@
 void load_rulesets(void)
 {
   struct section_file techfile, unitfile, buildfile, govfile, terrfile;
-  struct section_file cityfile, nationfile;
+  struct section_file cityfile, nationfile, effectfile;
 
   freelog(LOG_NORMAL, _("Loading rulesets"));
 
@@ -3086,6 +3048,8 @@
   openload_ruleset_file(&nationfile, "nations");
   load_nation_names(&nationfile);
 
+  openload_ruleset_file(&effectfile, "effects");
+
   load_ruleset_techs(&techfile);
   load_ruleset_cities(&cityfile);
   load_ruleset_governments(&govfile);
@@ -3093,6 +3057,7 @@
   load_ruleset_terrain(&terrfile);    /* terrain must precede nations */
   load_ruleset_buildings(&buildfile);
   load_ruleset_nations(&nationfile);
+  load_ruleset_effects(&effectfile);
   load_ruleset_game();
   translate_data_names();
 }

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