diff -Nur -Xfreecivdiff.ignore freeciv-cvs/client/packhand.c freeciv-patched/client/packhand.c --- freeciv-cvs/client/packhand.c 2003-07-23 16:36:52.000000000 +0100 +++ freeciv-patched/client/packhand.c 2003-07-24 16:56:08.000000000 +0100 @@ -417,6 +417,9 @@ /* Initialise list of improvements with city/building wide equiv_range. */ improvement_status_init(pcity->improvements, ARRAY_SIZE(pcity->improvements)); + + /* Initialise city's vector of improvement effects. */ + ceff_vector_init(&pcity->effects); } copy_worklist(&pcity->worklist, &packet->worklist); pcity->did_buy=packet->did_buy; @@ -607,6 +610,9 @@ /* Initialise list of improvements with city/building wide equiv_range. */ improvement_status_init(pcity->improvements, ARRAY_SIZE(pcity->improvements)); + + /* Initialise city's vector of improvement effects. */ + ceff_vector_init(&pcity->effects); } update_improvement_from_packet(pcity, B_PALACE, packet->capital, @@ -1157,6 +1163,11 @@ improvement_status_init(game.improvements, ARRAY_SIZE(game.improvements)); + /* Free vector of effects with a worldwide range. */ + geff_vector_free(&game.effects); + /* Free vector of destroyed effects. */ + ceff_vector_free(&game.destroyed_effects); + game.player_idx = pinfo->player_idx; game.player_ptr = &game.players[game.player_idx]; } @@ -1325,6 +1336,14 @@ pplayer->gives_shared_vision = pinfo->gives_shared_vision; pplayer->city_style=pinfo->city_style; + if (!pplayer->island_effects && map.num_continents > 0) { + pplayer->island_effects = fc_calloc(map.num_continents + 1, + sizeof(struct geff_vector)); + for (i = 1; i <= map.num_continents; i++) { + geff_vector_init(&pplayer->island_effects[i]); + } + } + for (i = 0; i < MAX_NUM_PLAYERS + MAX_NUM_BARBARIANS; i++) { pplayer->diplstates[i].type = pinfo->diplstates[i].type; diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/city.c freeciv-patched/common/city.c --- freeciv-cvs/common/city.c 2003-07-21 13:58:26.000000000 +0100 +++ freeciv-patched/common/city.c 2003-07-24 16:56:08.000000000 +0100 @@ -2373,17 +2373,26 @@ **************************************************************************/ void city_add_improvement(struct city *pcity, Impr_Type_id impr) { - struct player *pplayer = city_owner(pcity); + struct ceff_vector *ceffs[2]; + struct geff_vector *geffs[4]; + int i; - if (improvement_obsolete(pplayer, impr)) { - mark_improvement(pcity, impr, I_OBSOLETE); - } else { - mark_improvement(pcity, impr, I_ACTIVE); - } + mark_improvement(pcity, impr, I_ACTIVE); - improvements_update_redundant(pplayer, pcity, - map_get_continent(pcity->x, pcity->y), - improvement_types[impr].equiv_range); + /* Add affects at all ranges. */ + get_effect_vectors(ceffs, geffs, impr, pcity); + for (i = 0; ceffs[i]; i++) { + struct eff_city *eff = append_ceff(ceffs[i]); + eff->impr = impr; + eff->active = 0; + } + for (i = 0; geffs[i]; i++) { + struct eff_global *eff = append_geff(geffs[i]); + eff->eff.impr = impr; + eff->eff.active = 0; + eff->cityid = pcity->id; + eff->plr = city_owner(pcity); + } } /************************************************************************** @@ -2393,16 +2402,48 @@ **************************************************************************/ void city_remove_improvement(struct city *pcity,Impr_Type_id impr) { - struct player *pplayer = city_owner(pcity); + struct ceff_vector *ceffs[2]; + struct geff_vector *geffs[4]; + int i, j; freelog(LOG_DEBUG,"Improvement %s removed from city %s", improvement_types[impr].name, pcity->name); - mark_improvement(pcity, impr, I_NONE); + + /* If the building had a larger equiv_range than just this city, there may + be other improvements in the same range - so make sure they are restored */ + if (improvement_types[impr].equiv_range > EFR_CITY) { + players_iterate(pothplayer) { + city_list_iterate(pothplayer->cities, pothcity) { + if (pothcity->improvements[impr] != I_NONE) { + mark_improvement(pothcity, impr, pothcity->improvements[impr]); + } + } city_list_iterate_end; + } players_iterate_end; + } - improvements_update_redundant(pplayer, pcity, - map_get_continent(pcity->x, pcity->y), - improvement_types[impr].equiv_range); + get_effect_vectors(ceffs, geffs, impr, pcity); + /* Remove the base city effects */ + for (j = 0; ceffs[j]; j++) { + for (i = 0; i < ceff_vector_size(ceffs[j]); i++) { + struct eff_city *eff = ceff_vector_get(ceffs[j], i); + if (eff->impr == impr) { + eff->impr = B_LAST; + break; + } + } + } + + /* Remove the global-range effects */ + for (j = 0; geffs[j]; ++j) { + for (i = 0; i < geff_vector_size(geffs[j]); ++i) { + struct eff_global *eff = geff_vector_get(geffs[j], i); + if (eff->eff.impr == impr) { + eff->eff.impr = B_LAST; + break; + } + } + } } /************************************************************************** @@ -2510,6 +2551,9 @@ improvement_status_init(pcity->improvements, ARRAY_SIZE(pcity->improvements)); + /* Initialise city's vector of improvement effects. */ + ceff_vector_init(&pcity->effects); + pcity->turn_last_built = game.year; pcity->changed_from_id = 0; pcity->changed_from_is_unit = FALSE; @@ -2543,5 +2587,8 @@ **************************************************************************/ void remove_city_virtual(struct city *pcity) { + /* Free vector of improvement effects */ + ceff_vector_free(&pcity->effects); + free(pcity); } diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/city.h freeciv-patched/common/city.h --- freeciv-cvs/common/city.h 2003-07-23 16:36:56.000000000 +0100 +++ freeciv-patched/common/city.h 2003-07-24 16:56:08.000000000 +0100 @@ -248,6 +248,7 @@ int currently_building; Impr_Status improvements[B_LAST]; + struct ceff_vector effects; struct worklist worklist; diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/game.c freeciv-patched/common/game.c --- freeciv-cvs/common/game.c 2003-07-23 16:36:56.000000000 +0100 +++ freeciv-patched/common/game.c 2003-07-24 16:56:08.000000000 +0100 @@ -173,6 +173,8 @@ freelog(LOG_DEBUG, "removing city %s, %s, (%d %d)", pcity->name, get_nation_name(city_owner(pcity)->nation), pcity->x, pcity->y); + ceff_vector_free(&pcity->effects); + city_map_checked_iterate(pcity->x, pcity->y, x, y, mx, my) { set_worker_city(pcity, x, y, C_TILE_EMPTY); } city_map_checked_iterate_end; @@ -250,6 +252,9 @@ game.nbarbarians = 0; game.occupychance= GAME_DEFAULT_OCCUPYCHANCE; + geff_vector_init(&game.effects); + ceff_vector_init(&game.destroyed_effects); + game.heating = 0; game.cooling = 0; sz_strlcpy(game.save_name, GAME_DEFAULT_SAVE_NAME); @@ -325,6 +330,8 @@ ***************************************************************/ void game_free(void) { + geff_vector_free(&game.effects); + ceff_vector_free(&game.destroyed_effects); game_remove_all_players(); map_free(); idex_free(); @@ -433,6 +440,7 @@ pplayer->attribute_block.data = NULL; } + geff_vector_free(&pplayer->effects); if (pplayer->island_improv) { free(pplayer->island_improv); pplayer->island_improv = NULL; @@ -574,3 +582,125 @@ #undef name_strlcpy } + +/*************************************************************** + Marks all effects in the given vector as inactive. +***************************************************************/ +static void deactivate_effects(struct ceff_vector *ceff) +{ + int i, efflen; + + assert(ceff != NULL); + efflen = ceff_vector_size(ceff); + for (i = 0; i < efflen; ++i) { + struct eff_city *effect = ceff_vector_get(ceff, i); + effect->active = 0; + } +} + +/*************************************************************** + Marks all effects in all effect vectors as inactive. +***************************************************************/ +static void deactivate_all_effects(void) +{ + deactivate_effects(get_geff_parent(get_eff_world())); + + players_iterate(pplayer) { + int contid; + + deactivate_effects(get_geff_parent(get_eff_player(pplayer))); + for (contid = 1; contid <= map.num_continents; ++contid) { + deactivate_effects(get_geff_parent(get_eff_island(contid, pplayer))); + } + city_list_iterate(pplayer->cities, pcity) { + deactivate_effects(get_eff_city(pcity)); + } city_list_iterate_end; + } players_iterate_end; + + freelog(LOG_DEBUG, "All effects deactivated"); +} + +/*************************************************************** + Mark as active all relevant effects in the given effect + vector. +***************************************************************/ +static void update_city_effects(struct ceff_vector *ceff, struct city *pcity, + struct player *pplayer, + bool *cond_eff, bool *changed) +{ + int i; + + for (i = 0; i < ceff_vector_size(ceff); i++) { + struct eff_city *effect = ceff_vector_get(ceff, i); + + /* Consider the effects of all active improvements */ + if (effect->impr != B_LAST + && (!pcity || pcity->improvements[effect->impr] == I_ACTIVE)) { + Eff_Status testbit; + struct impr_effect *imeff = get_base_effect(effect->impr, pplayer); + bool this_changed = FALSE; + + for (testbit = 1; + imeff && imeff->type != EFT_LAST; + imeff++, testbit <<= 1) { + + /* Do not activate destroyed effects that do not "survive" */ + if (!pcity && !imeff->survives) { + freelog(LOG_DEBUG, + "Not activating non-surviving destroyed effect %s (from %s)", + effect_type_name(imeff->type), + get_effect_cause_name(effect->impr, pplayer)); + + /* Do nothing if the effect is already active */ + } else if (effect->active & testbit) { + freelog(LOG_DEBUG,"Effect %s (from %s) already active in %s", + effect_type_name(imeff->type), + get_effect_cause_name(effect->impr, pplayer), + pcity ? pcity->name : "destroyed city"); + + /* Test to see if the effect is activated by prerequisites */ + } else if (is_effect_activated(effect->impr, pcity, pplayer, + imeff, cond_eff)) { + freelog(LOG_DEBUG,"Effect %s (from %s) active in %s!", + effect_type_name(imeff->type), + get_effect_cause_name(effect->impr, pplayer), + pcity ? pcity->name : "destroyed city"); + effect->active |= testbit; + this_changed = *changed = TRUE; + } + } + + /* Sync wide-range effect structures with the city structure */ + if (this_changed) { + update_global_effect(pcity, pplayer, effect); + } + } + } +} + +/*************************************************************** + Update all improvement effects. +***************************************************************/ +void update_all_effects(void) +{ + bool changed, cond_eff; + + improvements_update_obsolete(); + improvements_update_redundant(NULL, NULL, 0, IR_WORLD); + + deactivate_all_effects(); + + /* We may need to loop several times, if some effects are dependent on other + * effects (cond_eff=TRUE) _and_ one or more effects (which may satisfy this + * dependency) were activated in this pass (changed=TRUE) */ + do { + changed = cond_eff = FALSE; + freelog(LOG_DEBUG, "Effect test pass"); + players_iterate(pplayer) { + city_list_iterate(pplayer->cities, pcity) { + struct ceff_vector *ceff = get_eff_city(pcity); + update_city_effects(ceff, pcity, pplayer, &cond_eff, &changed); + } city_list_iterate_end; + } players_iterate_end; + } while (changed && cond_eff); +} diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/game.h freeciv-patched/common/game.h --- freeciv-cvs/common/game.h 2003-07-23 16:36:56.000000000 +0100 +++ freeciv-patched/common/game.h 2003-07-24 16:56:08.000000000 +0100 @@ -109,6 +109,9 @@ /* global_wonders[] may also be (-1), or the id of a city which no longer exists, if the wonder has been destroyed */ Impr_Status improvements[B_LAST]; /* impr. with equiv_range==World */ + struct geff_vector effects; /* effects with range==World */ + struct ceff_vector destroyed_effects; /* list of effects that have + survived building destruction */ int heating; /* Number of polluted squares. */ int globalwarming; /* Total damage done. (counts towards a warming event.) */ @@ -254,6 +257,7 @@ struct player *get_player(int player_id); int get_num_human_and_ai_players(void); +void update_all_effects(void); extern struct civ_game game; extern bool is_server; diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/improvement.c freeciv-patched/common/improvement.c --- freeciv-cvs/common/improvement.c 2003-07-10 11:53:40.000000000 +0100 +++ freeciv-patched/common/improvement.c 2003-07-24 16:56:08.000000000 +0100 @@ -28,6 +28,16 @@ #include "improvement.h" +/* get 'struct ceff_vector' functions: */ +#define SPECVEC_TAG ceff +#define SPECVEC_TYPE struct eff_city +#include "specvec_c.h" + +/* get 'struct geff_vector' functions: */ +#define SPECVEC_TAG geff +#define SPECVEC_TYPE struct eff_global +#include "specvec_c.h" + /* Names of impr ranges. * (These must correspond to enum impr_range_id in improvement.h.) * do not change these unless you know what you're doing! */ @@ -567,14 +577,56 @@ } /************************************************************************** + Rearranges the island-range effects to take account of continent number + changes. +**************************************************************************/ +static void allot_island_effects(struct player *pplayer) +{ + int cont; + struct geff_vector *new_island_effects; + + new_island_effects = fc_calloc(map.num_continents + 1, + sizeof(struct geff_vector)); + for (cont = 1; cont <= map.num_continents; cont++) { + geff_vector_init(&new_island_effects[cont]); + } + + for (cont = 1; cont <= map.old_num_continents; cont++) { + int i; + struct geff_vector *geff = get_eff_island(cont, pplayer); + for (i = 0; i < geff_vector_size(geff); i++) { + struct eff_global *eff = geff_vector_get(geff, i); + if (eff->eff.impr != B_LAST && eff->cityid >= 0) { + int newcont; + struct eff_global *neweff; + struct city *pcity = player_find_city_by_id(pplayer, eff->cityid); + assert(pcity); + newcont = map_get_continent(pcity->x, pcity->y); + neweff = append_geff(&new_island_effects[newcont]); + neweff->eff.impr = eff->eff.impr; + neweff->eff.active = eff->eff.active; + neweff->cityid = eff->cityid; + } + } + geff_vector_free(geff); + } + if (map.old_num_continents > 0) { + free(pplayer->island_effects); + } + pplayer->island_effects = new_island_effects; +} + +/************************************************************************** Redimensions the lists of island-range improvements when number of continents changes. **************************************************************************/ void allot_island_improvs(void) { int i; + bool changed = FALSE; players_iterate(pplayer) { + allot_island_effects(pplayer); pplayer->island_improv = fc_realloc(pplayer->island_improv, (map.num_continents + 1) * game.num_impr_types @@ -597,29 +649,26 @@ continue; } + changed = TRUE; improvs[id] = pcity->improvements[id]; } built_impr_iterate_end; } city_list_iterate_end; } players_iterate_end; - improvements_update_redundant(NULL, NULL, 0, IR_WORLD); + map.old_num_continents = map.num_continents; + if (changed) { + update_all_effects(); + } } /************************************************************************** - Update the obsolete status of all improvements. This needs to be done - when a tech is discovered. + Update the obsolete status of all improvements. For all players since wonders are obsoleted when anybody discovers the obsolescence tech. - - If we marked something as obsolete, we need to call - improvements_update_redundant(), since it might have made something - unredundant. **************************************************************************/ void improvements_update_obsolete(void) { - bool did_mark = FALSE; - players_iterate(pplayer) { city_list_iterate(pplayer->cities, pcity) { built_impr_iterate(pcity, i) { @@ -627,17 +676,10 @@ freelog(LOG_DEBUG,"%s in %s is obsolete", improvement_types[i].name, pcity->name); mark_improvement(pcity, i, I_OBSOLETE); - did_mark = TRUE; } } built_impr_iterate_end; } city_list_iterate_end; } players_iterate_end; - - /* Ideally, we could track at what max range and for which players, but - * that's overoptimizing by a bit */ - if (did_mark) { - improvements_update_redundant(NULL, NULL, 0, IR_WORLD); - } } /************************************************************************** @@ -713,3 +755,272 @@ #undef CHECK_CITY_IMPR } + +/************************************************************************** +... +**************************************************************************/ +struct geff_vector *get_eff_world(void) +{ + return (&game.effects); +} + +/************************************************************************** +... +**************************************************************************/ +struct geff_vector *get_eff_player(struct player *plr) +{ + return (&plr->effects); +} + +/************************************************************************** +... +**************************************************************************/ +struct geff_vector *get_eff_island(int cont, struct player *plr) +{ + return (&plr->island_effects[cont]); +} + +/************************************************************************** +... +**************************************************************************/ +struct ceff_vector *get_eff_city(struct city *pcity) +{ + return (&pcity->effects); +} + +/************************************************************************** + Converts the given geff_vector into a ceff_vector (struct eff_global is + a derived class of struct eff_city, and by the same token geff_vector + is derived from ceff_vector). The returned vector functions exactly as + the orginal geff_vector, but returns eff_city structures. +**************************************************************************/ +struct ceff_vector *get_geff_parent(struct geff_vector *geff) +{ + return (struct ceff_vector *)geff; +} + +/************************************************************************** + Fills in the efflist pointer array with the eff_global lists that could + be changed by the given improvement (in the given city) being built + or destroyed. +**************************************************************************/ +void get_effect_vectors(struct ceff_vector *ceffs[], + struct geff_vector *geffs[], + Impr_Type_id impr, struct city *pcity) +{ + struct impr_effect *ie; + int j, i, cont; + bool effects[EFR_LAST]; + struct player *plr; + + assert(pcity && impr >= 0 && impr < game.num_impr_types); + + for (i = 0; i < EFR_LAST; i++) { + effects[i] = FALSE; + } + + if ((ie = improvement_types[impr].effect)) { + for (; ie->type < EFT_LAST; ie++) { + effects[ie->range] = TRUE; + } + } + + plr = city_owner(pcity); + cont = map_get_continent(pcity->x, pcity->y); + + i = 0; + for (j = 0; j < EFR_LAST; j++) { + if (effects[j]) { + switch(j) { + case EFR_ISLAND: + geffs[i++] = get_eff_island(cont, plr); + break; + case EFR_PLAYER: + geffs[i++] = get_eff_player(plr); + break; + case EFR_WORLD: + geffs[i++] = get_eff_world(); + break; + default: + break; + } + } + } + geffs[i++] = NULL; + + ceffs[0] = get_eff_city(pcity); + ceffs[1] = NULL; +} + +/************************************************************************** + Updates the relevant global effect structures, to bring them into + line with the current activity status of their parent city structure, + which should be owned by the passed pcity and pplayer (either can be + NULL for destroyed effects). +**************************************************************************/ +void update_global_effect(struct city *pcity, struct player *pplayer, + struct eff_city *effect) +{ + struct geff_vector *effs[EFR_LAST]; + int i, j; + + for (i = 0; i < EFR_LAST; i++) { + effs[i] = NULL; + } + + if (pplayer) { + effs[EFR_PLAYER] = get_eff_player(pplayer); + if (pcity) { + int cont = map_get_continent(pcity->x, pcity->y); + effs[EFR_ISLAND] = get_eff_island(cont, pplayer); + } + } + effs[EFR_WORLD] = get_eff_world(); + + for (j = 0; j < EFR_LAST; j++) { + if (effs[j]) { + for (i = 0; i < geff_vector_size(effs[j]); i++) { + struct eff_global *eff = geff_vector_get(effs[j], i); + + if ((!pcity || eff->cityid == pcity->id) + && (!pplayer || eff->plr == pplayer) + && eff->eff.impr == effect->impr) { + eff->eff.active = effect->active; + break; + } + } + } + } +} + +/************************************************************************** +... +**************************************************************************/ +struct eff_city *append_ceff(struct ceff_vector *x) +{ + int i, n; + struct eff_city *eff; + + /* Try for an unused vector instance if possible. */ + n = ceff_vector_size(x); + for (i = 0; i < n; i++) { + eff = ceff_vector_get(x, i); + if (eff->impr == B_LAST) { + return eff; + } + } + + /* That didn't work, so add a new instance to the vector. */ + ceff_vector_reserve(x, n + 1); + return ceff_vector_get(x, n); +} + +/************************************************************************** +... +**************************************************************************/ +struct eff_global *append_geff(struct geff_vector *x) +{ + int i, n; + struct eff_global *eff; + + /* Try for an unused vector instance if possible. */ + n = geff_vector_size(x); + for (i = 0; i < n; i++) { + eff = geff_vector_get(x, i); + if (eff->eff.impr == B_LAST) { + return eff; + } + } + + /* That didn't work, so add a new instance to the vector. */ + geff_vector_reserve(x, n + 1); + return geff_vector_get(x, n); +} + +/************************************************************************** + Fills in the efflist pointer array with the relevant ceff_vectors for + the given improvement, in the given city, on the given island, and the + given player. B_LAST, NULL, -1 and NULL respectively cause those ranges + to be skipped (i.e. returned as NULL). +**************************************************************************/ +void get_all_effect_vectors(struct ceff_vector *effs[EFR_LAST], + Impr_Type_id impr, struct city *pcity, + int cont, struct player *plr) +{ + int i; + + for (i = 0; i < EFR_LAST; i++) { + effs[i] = NULL; + } + + if (pcity) { + effs[EFR_CITY] = get_eff_city(pcity); + if (impr != B_LAST) { + effs[EFR_LOCAL] = effs[EFR_CITY]; + } + } + + if (plr) { + effs[EFR_PLAYER] = get_geff_parent(get_eff_player(plr)); + if (cont != -1) { + effs[EFR_ISLAND] = get_geff_parent(get_eff_island(cont, plr)); + } + } + + effs[EFR_WORLD] = get_geff_parent(get_eff_world()); +} + +/************************************************************************** + Returns TRUE if the given building (if not B_LAST), city or player + are being affected by the given effect; the player should be the owner of + the city (and will be filled in if city is specified and player is NULL) +**************************************************************************/ +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"); + return FALSE; +} + +/************************************************************************** + Returns TRUE if the given effect (in the given city, owned by the given + player) is activated by all of the necessary prerequisites; + *cond_eff is set to TRUE if the effect could not be activated because it + depends on another effect + Note: the cond_bldg field, and the various aff_xxx fields, are not + checked, as one effect may affect several cities, terrains, etc. + Use the is_city_affected and is_unit_terrain_affected functions to + determine if a so-called "active" effect really is. +**************************************************************************/ +bool is_effect_activated(Impr_Type_id impr, struct city *pcity, + struct player *pplayer, struct impr_effect *imeff, + bool *cond_eff) +{ + if (imeff->cond_gov != game.government_count + && pplayer->government != imeff->cond_gov) { + freelog(LOG_DEBUG, "Effect requires a government"); + return FALSE; + } + if (get_invention(pplayer, imeff->cond_adv) != TECH_KNOWN) { + freelog(LOG_DEBUG, "Effect requires a future tech"); + return FALSE; + } + if (imeff->cond_eff != EFT_LAST + && !is_under_effect(impr, pcity, pplayer, imeff->cond_eff)) { + freelog(LOG_DEBUG, "Effect requires another effect"); + *cond_eff = TRUE; + return FALSE; + } + return TRUE; +} + +struct impr_effect *get_base_effect(Impr_Type_id impr, struct player *pplayer) +{ + return improvement_types[impr].effect; +} + +const char *get_effect_cause_name(Impr_Type_id impr, struct player *pplayer) +{ + return get_improvement_name(impr); +} diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/improvement.h freeciv-patched/common/improvement.h --- freeciv-cvs/common/improvement.h 2003-07-10 11:53:40.000000000 +0100 +++ freeciv-patched/common/improvement.h 2003-07-24 16:56:08.000000000 +0100 @@ -81,7 +81,7 @@ * These must correspond to effect_range_names[] in improvement.c. */ enum effect_range { EFR_NONE, - EFR_BUILDING, + EFR_LOCAL, EFR_CITY, EFR_ISLAND, EFR_PLAYER, @@ -181,10 +181,35 @@ enum tile_special_type aff_spec; /* S_* bit mask of specials affected */ }; +/* Status of a city's improvement effects + * (a bitfield: bit 0 set = first effect active, etc.) */ +typedef unsigned Eff_Status; + /* Maximum number of effects per improvement * (this should not be more than the number of bits in the Eff_Status type) */ #define MAX_EFFECTS 16 +/* Keeps track of which improvement effects are active in a city. */ +struct eff_city { + Impr_Type_id impr; /* The ID of the improvement that confers the effects; + * if B_LAST, then this instance is unused and ready + * to be freed (or replaced with a new improvement */ + Eff_Status active; /* Which of the actual impr_effect effects are active */ +}; + +/* Copy of eff_city effect activity for effects with Player-, Island-, + * and World- ranges */ +struct eff_global { + struct eff_city eff; /* Should be updated whenever the corresponding + * structure in the improvement's home city is + * modified. N.B. Keep this first in the structure + * so that a (struct eff_city) cast works */ + int cityid; /* ID of the city that owns the improvment (if -1, + * then the effect has survived the city destruction, + * and should therefore be placed in the savefile) */ + struct player *plr; /* Player that owns whatever confers this effect */ +}; + /* Type of improvement. (Read from buildings.ruleset file.) */ struct impr_type { char name[MAX_LEN_NAME]; @@ -214,6 +239,16 @@ extern struct impr_type improvement_types[B_LAST]; +/* get 'struct ceff_vector' and related functions: */ +#define SPECVEC_TAG ceff +#define SPECVEC_TYPE struct eff_city +#include "specvec.h" + +/* get 'struct geff_vector' and related functions: */ +#define SPECVEC_TAG geff +#define SPECVEC_TYPE struct eff_global +#include "specvec.h" + /* impr range id/string converters */ enum impr_range impr_range_from_str(const char *str); const char *impr_range_name(enum impr_range id); @@ -224,6 +259,23 @@ enum effect_type effect_type_from_str(const char *str); const char *effect_type_name(enum effect_type id); +void get_effect_vectors(struct ceff_vector *ceffs[], + struct geff_vector *geffs[], + Impr_Type_id impr, struct city *pcity); +void get_all_effect_vectors(struct ceff_vector *effs[EFR_LAST], + Impr_Type_id impr, struct city *pcity, + int cont, struct player *plr); +bool is_under_effect(Impr_Type_id impr, struct city *pcity, + struct player *pplayer, enum effect_type cond_eff); +bool is_effect_activated(Impr_Type_id impr, struct city *pcity, + struct player *pplayer, struct impr_effect *imeff, + bool *cond_eff); +struct ceff_vector *get_geff_parent(struct geff_vector *geff); +void update_global_effect(struct city *pcity, struct player *pplayer, + struct eff_city *effect); +struct eff_city *append_ceff(struct ceff_vector *x); +struct eff_global *append_geff(struct geff_vector *x); + /* improvement functions */ void improvements_free(void); struct impr_type *get_improvement_type(Impr_Type_id id); @@ -250,10 +302,17 @@ /* city related improvement functions */ void mark_improvement(struct city *pcity,Impr_Type_id id,Impr_Status status); +struct geff_vector *get_eff_world(void); +struct geff_vector *get_eff_player(struct player *plr); +struct geff_vector *get_eff_island(int cont, struct player *plr); +struct ceff_vector *get_eff_city(struct city *pcity); + void allot_island_improvs(void); void improvements_update_obsolete(void); void improvements_update_redundant(struct player *pplayer, struct city *pcity, int cont, enum impr_range range); +struct impr_effect *get_base_effect(Impr_Type_id impr, struct player *pplayer); +const char *get_effect_cause_name(Impr_Type_id impr, struct player *pplayer); /* Iterates over all improvements. Creates a new variable names m_i * with type Impr_Type_id which holds the id of the current improvement. */ diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/map.c freeciv-patched/common/map.c --- freeciv-cvs/common/map.c 2003-07-23 16:36:56.000000000 +0100 +++ freeciv-patched/common/map.c 2003-07-24 16:56:08.000000000 +0100 @@ -193,6 +193,7 @@ map.separatepoles = MAP_DEFAULT_SEPARATE_POLES; map.tiles = NULL; map.num_continents = 0; + map.old_num_continents = -1; map.num_start_positions = 0; map.fixed_start_positions = FALSE; map.have_specials = FALSE; diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/map.h freeciv-patched/common/map.h --- freeciv-cvs/common/map.h 2003-07-24 11:56:51.000000000 +0100 +++ freeciv-patched/common/map.h 2003-07-24 16:56:08.000000000 +0100 @@ -174,6 +174,7 @@ bool have_huts; bool have_rivers_overlay; /* only applies if !have_specials */ int num_continents; + int old_num_continents; struct tile *tiles; /* Only used by server. */ diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/player.c freeciv-patched/common/player.c --- freeciv-cvs/common/player.c 2003-07-10 11:53:40.000000000 +0100 +++ freeciv-patched/common/player.c 2003-07-24 16:56:08.000000000 +0100 @@ -130,14 +130,20 @@ /* Initialise list of improvements with Player-wide equiv_range */ improvement_status_init(plr->improvements, ARRAY_SIZE(plr->improvements)); + /* Initialise vector of effects with player range. */ + geff_vector_init(&plr->effects); /* Initialise list of improvements with Island-wide equiv_range */ plr->island_improv = NULL; + plr->island_effects = NULL; if (map.num_continents > 0) { plr->island_improv = fc_malloc((map.num_continents + 1) * game.num_impr_types * sizeof(Impr_Status)); + plr->island_effects = fc_calloc(map.num_continents + 1, + sizeof(struct geff_vector)); for (i = 1; i <= map.num_continents; i++) { + geff_vector_init(&plr->island_effects[i]); improvement_status_init(&plr->island_improv[i * game.num_impr_types], game.num_impr_types); } diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/player.h freeciv-patched/common/player.h --- freeciv-cvs/common/player.h 2003-07-10 11:53:40.000000000 +0100 +++ freeciv-patched/common/player.h 2003-07-24 16:56:08.000000000 +0100 @@ -205,6 +205,8 @@ Impr_Status improvements[B_LAST]; /* improvements with equiv_range==Player */ Impr_Status *island_improv; /* improvements with equiv_range==Island, dimensioned to [map.num_continents][game.num_impr_types] */ + struct geff_vector effects; /* effects with range==Player */ + struct geff_vector *island_effects; /* effects with range==Island */ struct { int length; diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/tech.c freeciv-patched/common/tech.c --- freeciv-cvs/common/tech.c 2003-07-21 13:58:26.000000000 +0100 +++ freeciv-patched/common/tech.c 2003-07-24 16:56:08.000000000 +0100 @@ -67,7 +67,7 @@ if (value == TECH_KNOWN) { game.global_advances[tech]++; - improvements_update_obsolete(); + update_all_effects(); } } diff -Nur -Xfreecivdiff.ignore freeciv-cvs/server/citytools.c freeciv-patched/server/citytools.c --- freeciv-cvs/server/citytools.c 2003-07-23 16:36:56.000000000 +0100 +++ freeciv-patched/server/citytools.c 2003-07-24 16:56:08.000000000 +0100 @@ -1773,6 +1773,7 @@ if (!is_wonder(id)) { pplayer->economic.gold += improvement_value(id); building_lost(pcity, id); + update_all_effects(); } } diff -Nur -Xfreecivdiff.ignore freeciv-cvs/server/gamehand.c freeciv-patched/server/gamehand.c --- freeciv-cvs/server/gamehand.c 2003-07-10 11:53:40.000000000 +0100 +++ freeciv-patched/server/gamehand.c 2003-07-24 16:56:08.000000000 +0100 @@ -154,6 +154,12 @@ /* Initialise list of improvements with world-wide equiv_range */ improvement_status_init(game.improvements, ARRAY_SIZE(game.improvements)); + + /* Free vector of effects with world-wide range. */ + geff_vector_free(&game.effects); + + /* Free vector of destroyed effects */ + ceff_vector_free(&game.destroyed_effects); } /************************************************************************** diff -Nur -Xfreecivdiff.ignore freeciv-cvs/server/savegame.c freeciv-patched/server/savegame.c --- freeciv-cvs/server/savegame.c 2003-07-23 16:36:57.000000000 +0100 +++ freeciv-patched/server/savegame.c 2003-07-24 16:56:08.000000000 +0100 @@ -948,6 +948,9 @@ improvement_status_init(pcity->improvements, ARRAY_SIZE(pcity->improvements)); + /* Initialise city's vector of improvement effects. */ + ceff_vector_init(&pcity->effects); + impr_type_iterate(x) { if (*p != '\0' && *p++=='1') { city_add_improvement(pcity,x); @@ -2207,7 +2210,7 @@ if (!game.is_new_game) { /* Set active city improvements/wonders and their effects */ - improvements_update_obsolete(); + update_all_effects(); } game.player_idx=0;