diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/improvement.c freeciv-patched/common/improvement.c --- freeciv-cvs/common/improvement.c 2003-07-24 16:57:16.000000000 +0100 +++ freeciv-patched/common/improvement.c 2003-07-24 16:57:16.000000000 +0100 @@ -978,8 +978,16 @@ bool is_under_effect(Impr_Type_id impr, struct city *pcity, struct player *pplayer, enum effect_type cond_eff) { - freelog(LOG_DEBUG, "is_under_effect() currently unsupported; please apply " - "the effect-iterator patch to correct this"); + if (!pplayer && pcity) { + pplayer = city_owner(pcity); + } + + impr_effects_iterate(iter, impr, pcity, pplayer) { + if (iter.imeff->type == cond_eff) { + return TRUE; + } + } impr_effects_iterate_end; + return FALSE; } @@ -1024,3 +1032,229 @@ { return get_improvement_name(impr); } + +/************************************************************************** + Sets an effect iterator to the start of the effect lists - common + function used by other effect iterator initialisation functions +**************************************************************************/ +static void eff_iterator_start(struct eff_iter *iter) +{ + iter->imeff = NULL; + iter->range = 0; + iter->index = 0; + iter->bitmask = 1; +} + +/************************************************************************** + Initialises an effect iterator structure (a pointer to which should be + passed in as the first argument). The iterator will return all improvement + effects which affect the designated improvement in the given city owned + by the given player (if impr is B_LAST, and/or pcity/pplayer is NULL, + the respective ranges are skipped) +**************************************************************************/ +void eff_iterator_impr_init(struct eff_iter *iter, Impr_Type_id impr, + struct city *pcity, struct player *pplayer) +{ + int cont = -1; + + assert(iter != NULL); + + if (pcity) + cont = map_get_continent(pcity->x, pcity->y); + + get_all_effect_vectors(iter->effs, impr, pcity, cont, pplayer); + iter->impr = impr; + iter->pcity = pcity; + iter->aff_unit = UCL_LAST; + + if (pcity) { + iter->aff_terr = map_get_terrain(pcity->x, pcity->y); + iter->aff_spec = map_get_special(pcity->x, pcity->y); + } else { + iter->aff_terr = T_UNKNOWN; + iter->aff_spec = S_ALL; + } + + eff_iterator_start(iter); +} + +/************************************************************************** + Similar to the above function, this initialises an effect iterator for + considering effects that influence a unit that is owned by the given + player and is at the given map coordinates (if pplayer and/or mp are NULL, + the unit is considered unowned and/or unpositioned) +**************************************************************************/ +void eff_iterator_unit_init(struct eff_iter *iter, Unit_Type_id utype, + struct player *pplayer, struct map_position *mp) +{ + int cont = -1; + struct city *pcity = NULL; + + assert(iter != NULL); + + if (mp) { + pcity = map_get_city(mp->x, mp->y); + cont = map_get_continent(mp->x, mp->y); + } + + get_all_effect_vectors(iter->effs, B_LAST, pcity, cont, pplayer); + + iter->impr = B_LAST; + iter->pcity = pcity; + iter->aff_unit = get_unittype_class(utype); + + if (mp) { + iter->aff_terr = map_get_terrain(mp->x, mp->y); + iter->aff_spec = map_get_special(mp->x, mp->y); + } else { + iter->aff_terr = T_UNKNOWN; + iter->aff_spec = S_ALL; + } + + eff_iterator_start(iter); +} + +/************************************************************************** + Given an initialised effect iterator, returns the next active effect. + iter->imeff also points to this effect (the data is in the ruleset, so + should NOT be freed when you're done with it). NULL is returned when no + more effects can be found. Does _not_ consider the various aff_xxx fields. + + N.B. Don't go adding/removing effects while an iteration is in progress! +**************************************************************************/ +static struct impr_effect *eff_iterator_next_internal(struct eff_iter *iter) +{ + struct ceff_vector *ceff; + struct eff_city *effect; + int efflen; + + assert(iter != NULL); + + /* Return immediately if the iterator is exhausted */ + if (!iter->imeff && iter->range != 0) + return NULL; + + /* Loop over all valid ranges */ + for (; iter->range < EFR_LAST; iter->range++) { + ceff = iter->effs[iter->range]; + if (ceff) { + efflen = ceff_vector_size(ceff); + for (; iter->index < efflen; iter->index++) { + effect = ceff_vector_get(ceff, iter->index); + if (effect->impr != B_LAST) { + if (iter->imeff) { + /* Make sure we don't return the same effect again */ + iter->imeff++; iter->bitmask <<= 1; + } else { + iter->imeff = improvement_types[effect->impr].effect; + iter->bitmask = 1; + } + for (; iter->imeff && iter->imeff->type != EFT_LAST; + iter->imeff++, iter->bitmask <<= 1) { + if (effect->active & iter->bitmask + && iter->imeff->range == iter->range + && (iter->imeff->range != EFR_LOCAL + || effect->impr == iter->impr) + && is_city_affected(iter->imeff, iter->pcity)) { + return iter->imeff; + } + } + /* Force reinitialisation of imeff next time round */ + iter->imeff = NULL; + } + } + } + /* Make sure we restart at zero for the next range out */ + iter->index = 0; + } + /* We didn't match any more effects, so signal that we're done */ + iter->imeff = NULL; + return NULL; +} + +/************************************************************************** + Returns the next active effect that satisfies the aff_xxx fields set for + the city/unit etc. when the iterator was initialised. +**************************************************************************/ +struct impr_effect *eff_iterator_next(struct eff_iter *iter) +{ + struct impr_effect *imeff; + + do { + imeff = eff_iterator_next_internal(iter); + if (imeff && is_unit_terrain_affected(imeff, iter->aff_unit, + iter->aff_terr, iter->aff_spec)) { + return imeff; + } + } while(imeff); + return NULL; +} + +/************************************************************************** + Returns the next active effect that satisfies the various aff_xxx fields + passed (use UCL_LAST, T_UNKNOWN and/or S_ALL respectively if you want + any value - except "None" - to match) +**************************************************************************/ +struct impr_effect *eff_iterator_next_full(struct eff_iter *iter, + Unit_Class_id aff_unit, + enum tile_terrain_type aff_terr, + enum tile_special_type aff_spec) +{ + struct impr_effect *imeff; + + do { + imeff = eff_iterator_next_internal(iter); + if (imeff && is_unit_terrain_affected(imeff, aff_unit, aff_terr, + aff_spec)) { + return imeff; + } + } while(imeff); + return NULL; +} + +/************************************************************************** + Returns the ID of the improvement that is conferring the last effect + returned from the iterator, or B_LAST if no building is doing so +**************************************************************************/ +Impr_Type_id eff_iterator_get_improvement(struct eff_iter *iter) +{ + struct ceff_vector *ceff; + struct eff_city *effect; + + assert(iter != NULL); + + if (!iter->imeff) + return B_LAST; + + ceff = iter->effs[iter->range]; + effect = ceff_vector_get(ceff, iter->index); + + return effect->impr; +} + +/************************************************************************** + Returns TRUE if the given effect should affect the given city (i.e. it + has the building required for the effect to be active) +**************************************************************************/ +int is_city_affected(struct impr_effect *imeff, struct city *pcity) +{ + return (imeff->cond_bldg == B_LAST + || (pcity && city_got_building(pcity, imeff->cond_bldg))); +} + +/************************************************************************** + Returns TRUE if the given effect should affect the given + unit/terrain/special, by checking the relevant aff_xxx fields + (Pass in UCL_LAST, T_UNKNOWN, and S_ALL respectively if you don't care + about that field; these values will match everything except "None".) +**************************************************************************/ +int is_unit_terrain_affected(struct impr_effect *imeff, Unit_Class_id aff_unit, + enum tile_terrain_type aff_terr, + enum tile_special_type aff_spec) +{ + return (aff_unit == UCL_LAST || imeff->aff_unit == UCL_LAST + || imeff->aff_unit == aff_unit) + && (((aff_terr == T_UNKNOWN && imeff->aff_terr != T_LAST) + || imeff->aff_terr == T_UNKNOWN || imeff->aff_terr == aff_terr) + || imeff->aff_spec & aff_spec); +} diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/improvement.h freeciv-patched/common/improvement.h --- freeciv-cvs/common/improvement.h 2003-07-24 16:57:16.000000000 +0100 +++ freeciv-patched/common/improvement.h 2003-07-24 16:57:16.000000000 +0100 @@ -21,6 +21,7 @@ #include "unittype.h" /* Unit_Class_id, Unit_Type_id */ struct player; +struct map_position; /* Improvement types. @@ -253,6 +254,20 @@ enum impr_range impr_range_from_str(const char *str); const char *impr_range_name(enum impr_range id); +/* An iterator to look at the effects affecting a building/city/player. */ +struct eff_iter { + struct impr_effect *imeff; /* The next active effect */ + struct city *pcity; + Impr_Type_id impr; + struct ceff_vector *effs[EFR_LAST]; + enum effect_range range; + int index; + Eff_Status bitmask; + Unit_Class_id aff_unit; + enum tile_terrain_type aff_terr; + enum tile_special_type aff_spec; +}; + /* improvement effect functions */ enum effect_range effect_range_from_str(const char *str); const char *effect_range_name(enum effect_range id); @@ -275,6 +290,72 @@ struct eff_city *effect); struct eff_city *append_ceff(struct ceff_vector *x); struct eff_global *append_geff(struct geff_vector *x); +void eff_iterator_impr_init(struct eff_iter *iter, Impr_Type_id impr, + struct city *pcity, struct player *pplayer); +void eff_iterator_unit_init(struct eff_iter *iter, Unit_Type_id utype, + struct player *pplayer, struct map_position *mp); +struct impr_effect *eff_iterator_next(struct eff_iter *iter); +struct impr_effect *eff_iterator_next_full(struct eff_iter *iter, + Unit_Class_id aff_unit, + enum tile_terrain_type aff_terr, + enum tile_special_type aff_spec); +Impr_Type_id eff_iterator_get_improvement(struct eff_iter *iter); +int is_unit_terrain_affected(struct impr_effect *imeff, Unit_Class_id aff_unit, + enum tile_terrain_type aff_terr, + enum tile_special_type aff_spec); +int is_city_affected(struct impr_effect *imeff, struct city *pcity); + +#define city_effects_iterate(EI_iter, pcity) \ + { \ + struct eff_iter EI_iter; \ + eff_iterator_impr_init(&EI_iter, B_LAST, pcity, city_owner(pcity)); \ + while (eff_iterator_next(&EI_iter)) { + +#define city_effects_iterate_end \ + } \ + } + +#define city_effects_iterate_full(EI_iter, pcity, uclass, terr, spec) \ + { \ + struct eff_iter EI_iter; \ + eff_iterator_impr_init(&EI_iter, B_LAST, pcity, city_owner(pcity)); \ + while (eff_iterator_next_full(&EI_iter, uclass, terr, spec)) { + +#define city_effects_iterate_full_end \ + } \ + } + +#define impr_effects_iterate(EI_iter, impr, pcity, pplayer) \ + { \ + struct eff_iter EI_iter; \ + eff_iterator_impr_init(&EI_iter, impr, pcity, pplayer); \ + while (eff_iterator_next(&EI_iter)) { + +#define impr_effects_iterate_end \ + } \ + } + +#define impr_effects_iterate_full(EI_iter, impr, pcity, pplayer, uclass, terr, spec) \ + { \ + struct eff_iter EI_iter; \ + eff_iterator_impr_init(&EI_iter, impr, pcity, pplayer); \ + while (eff_iterator_next_full(&EI_iter, uclass, terr, spec)) { + +#define impr_effects_iterate_full_end \ + } \ + } + +#define unit_effects_iterate(EI_iter, punit) \ + { \ + struct eff_iter EI_iter; \ + struct map_position EI_mappos; \ + EI_mappos.x = (punit)->x; EI_mappos.y = (punit)->y; \ + eff_iterator_unit_init(&iter, (punit)->type, unit_owner(punit), &EI_mappos); \ + while (eff_iterator_next(&EI_iter)) { + +#define unit_effects_iterate_end \ + } \ + } /* improvement functions */ void improvements_free(void); diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/unit.c freeciv-patched/common/unit.c --- freeciv-cvs/common/unit.c 2003-07-21 13:58:26.000000000 +0100 +++ freeciv-patched/common/unit.c 2003-07-24 16:57:17.000000000 +0100 @@ -343,6 +343,15 @@ } /************************************************************************** + Gets the class of a given unit, by examining the unit type's move_type + and flags fields +**************************************************************************/ +Unit_Class_id get_unit_class(struct unit *punit) +{ + return get_unittype_class(punit->type); +} + +/************************************************************************** ... **************************************************************************/ bool is_military_unit(struct unit *punit) diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/unit.h freeciv-patched/common/unit.h --- freeciv-cvs/common/unit.h 2003-07-18 13:40:03.000000000 +0100 +++ freeciv-patched/common/unit.h 2003-07-24 16:57:17.000000000 +0100 @@ -226,6 +226,7 @@ bool is_military_unit(struct unit *punit); /* !set !dip !cara */ bool is_diplomat_unit(struct unit *punit); bool is_square_threatened(struct player *pplayer, int x, int y); +Unit_Class_id get_unit_class(struct unit *punit); bool is_field_unit(struct unit *punit); /* ships+aero */ bool is_hiding_unit(struct unit *punit); bool is_sailing_unit(struct unit *punit); diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/unittype.c freeciv-patched/common/unittype.c --- freeciv-cvs/common/unittype.c 2003-07-10 11:53:40.000000000 +0100 +++ freeciv-patched/common/unittype.c 2003-07-24 16:57:17.000000000 +0100 @@ -103,6 +103,29 @@ return (unit_types[id].move_type == LAND_MOVING); } + /************************************************************************** + Gets the class of a given unit type, by examining the unit type's + move_type and flags fields +**************************************************************************/ +Unit_Class_id get_unittype_class(Unit_Type_id type) +{ + if (unit_type_flag(type, F_NUCLEAR)) { + return UCL_NUCLEAR; + } else if (unit_type_flag(type, F_MISSILE)) { + return UCL_MISSILE; + } else switch (get_unit_type(type)->move_type) { + case LAND_MOVING: + return UCL_LAND; + case SEA_MOVING: + return UCL_SEA; + case HELI_MOVING: + return UCL_HELICOPTER; + case AIR_MOVING: + return UCL_AIR; + } + return UCL_LAST; +} + /************************************************************************** ... **************************************************************************/ diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/unittype.h freeciv-patched/common/unittype.h --- freeciv-cvs/common/unittype.h 2003-07-10 11:53:40.000000000 +0100 +++ freeciv-patched/common/unittype.h 2003-07-24 16:57:17.000000000 +0100 @@ -205,6 +205,7 @@ bool is_air_unittype(Unit_Type_id id); bool is_heli_unittype(Unit_Type_id id); bool is_ground_unittype(Unit_Type_id id); +Unit_Class_id get_unittype_class(Unit_Type_id type); int unit_value(Unit_Type_id id); int unit_pop_value(Unit_Type_id id); diff -Nur -Xfreecivdiff.ignore freeciv-cvs/doc/README.effects freeciv-patched/doc/README.effects --- freeciv-cvs/doc/README.effects 2003-07-10 11:53:40.000000000 +0100 +++ freeciv-patched/doc/README.effects 2003-07-24 16:57:17.000000000 +0100 @@ -213,7 +213,7 @@ (must be in the range -32767 to 32767) .survives = 1 if effect survives destruction (wonders only) (if unspecified, 0 (doesn't survive) is assumed) -.cond_bldg = must have this building in same city for effect +.cond_bldg = effect is only active in cities that have this building (if unspecified, effect not conditional on building) .cond_nat = must be this nation for effect (if unspecified, effect not conditional on nation) @@ -221,7 +221,8 @@ (if unspecified, effect not conditional on government) .cond_adv = must know this advance for effect (if unspecified, effect not conditional on advance) -.cond_eff = must be affected by this effect for effect +.cond_eff = the improvement conferring the effect must in turn be + first affected by the given effect (if unspecified, effect not conditional on other effect) .aff_unit = affects only those units of this class; one of: "Air", "Helicopter", "Land", "Missile", "Nuclear", "Sea"