diff -Nur -Xfreecivdiff.ignore freeciv-cvs/ai/advdomestic.c freeciv-patched/ai/advdomestic.c --- freeciv-cvs/ai/advdomestic.c 2003-07-24 17:06:34.000000000 +0100 +++ freeciv-patched/ai/advdomestic.c 2003-07-24 17:09:40.000000000 +0100 @@ -479,6 +479,11 @@ || !can_build_improvement(pcity, id)) continue; + if (id == B_CITY) { + /* if (!built_elsewhere(pcity, B_WALL)) was counterproductive -- Syela */ + values[id] = ai_eval_threat_land(pplayer, pcity); + } + switch (id) { /* food/growth production */ @@ -605,10 +610,6 @@ * anyway. It was so stupid, AI wouldn't start building walls until it was * in danger and it would have no chance to finish them before it was too * late */ - case B_CITY: - /* if (!built_elsewhere(pcity, B_WALL)) was counterproductive -- Syela */ - values[id] = ai_eval_threat_land(pplayer, pcity); - break; case B_COASTAL: values[id] = ai_eval_threat_sea(pplayer, pcity); break; @@ -711,7 +712,7 @@ break; case B_LEONARDO: unit_list_iterate(pcity->units_supported, punit) { - Unit_Type_id unit_id = can_upgrade_unittype(pplayer, punit->type); + Unit_Type_id unit_id = can_upgrade_unittype(pplayer, punit->type, TRUE); if (unit_id >= 0) { /* this is probably wrong -- Syela */ j = 8 * unit_upgrade_price(pplayer, punit->type, unit_id); diff -Nur -Xfreecivdiff.ignore freeciv-cvs/ai/aicity.c freeciv-patched/ai/aicity.c --- freeciv-cvs/ai/aicity.c 2003-07-24 17:06:34.000000000 +0100 +++ freeciv-patched/ai/aicity.c 2003-07-24 17:09:39.000000000 +0100 @@ -321,7 +321,7 @@ { struct player *pplayer = city_owner(pcity); unit_list_iterate(map_get_tile(pcity->x, pcity->y)->units, punit) { - int id = can_upgrade_unittype(pplayer, punit->type); + int id = can_upgrade_unittype(pplayer, punit->type, TRUE); if (military && (!is_military_unit(punit) || !is_ground_unit(punit))) { /* Only upgrade military units this round */ continue; diff -Nur -Xfreecivdiff.ignore freeciv-cvs/ai/aiunit.c freeciv-patched/ai/aiunit.c --- freeciv-cvs/ai/aiunit.c 2003-07-24 17:06:33.000000000 +0100 +++ freeciv-patched/ai/aiunit.c 2003-07-24 17:09:41.000000000 +0100 @@ -370,18 +370,30 @@ Is there a chance that a trireme would be lost, given information that the player actually has. ***************************************************************/ -static bool is_likely_trireme_loss(struct player *pplayer, int x, int y) +static bool is_likely_ship_loss(struct unit *punit, int x, int y) { + struct player *pplayer = unit_owner(punit); + /* * If we are in a city or next to land, we have no chance of losing * the ship. To make this really useful for ai planning purposes, we'd * need to confirm that we can exist/move at the x,y location we are given. */ - if ((likely_ocean(x, y, pplayer) < 50) || - is_likely_coastline(x, y, pplayer) || - (player_owns_active_wonder(pplayer, B_LIGHTHOUSE))) { + if (!unit_flag(punit, F_TRIREME) || likely_ocean(x, y, pplayer) < 50 + || is_likely_coastline(x, y, pplayer)) { return FALSE; } else { + struct eff_iter iter; + struct map_position mp; + mp.x = x; + mp.y = y; + eff_iterator_unit_init(&iter, punit->type, punit->type, + unit_owner(punit), &mp); + while (eff_iterator_next(&iter)) { + if (iter.imeff->type == EFT_NO_SINK_DEEP) { + return FALSE; + } + } return TRUE; } } @@ -424,8 +436,7 @@ * is a city on the tile, or if the tile is not accessible, or if the * tile is on a different continent, or if we're a barbarian and * the tile has a hut, don't go there. */ - if ((unit_flag(punit, F_TRIREME) && - is_likely_trireme_loss(pplayer, x, y)) + if (is_likely_ship_loss(punit, x, y) || map_get_city(x, y) || map_get_continent(x, y) != continent || (is_barbarian(pplayer) && map_has_special(x, y, S_HUT))) { diff -Nur -Xfreecivdiff.ignore freeciv-cvs/client/citydlg_common.c freeciv-patched/client/citydlg_common.c --- freeciv-cvs/client/citydlg_common.c 2003-07-24 17:06:29.000000000 +0100 +++ freeciv-patched/client/citydlg_common.c 2003-07-24 17:09:42.000000000 +0100 @@ -136,9 +136,9 @@ stock = pcity->shield_stock; if (pcity->is_building_unit) { - cost = get_unit_type(pcity->currently_building)->build_cost; + cost = unit_value_adjusted(pcity->currently_building, pcity); } else { - cost = get_improvement_type(pcity->currently_building)->build_cost; + cost = improvement_value_adjusted(pcity->currently_building, pcity); } if (!pcity->is_building_unit && pcity->currently_building == B_CAPITAL) { @@ -192,10 +192,10 @@ if (is_unit) { name = get_unit_name(id); - cost = get_unit_type(id)->build_cost; + cost = unit_value_adjusted(id, pcity); } else { name = get_impr_name_ex(pcity, id); - cost = get_improvement_type(id)->build_cost; + cost = improvement_value_adjusted(id, pcity); } if (turns < 999) { @@ -231,7 +231,7 @@ my_snprintf(buf[1], column_size, "%d/%d/%d", ptype->attack_strength, ptype->defense_strength, ptype->move_rate / 3); } - my_snprintf(buf[2], column_size, "%d", ptype->build_cost); + my_snprintf(buf[2], column_size, "%d", unit_value_adjusted(id, pcity)); } else { /* Total & turns left meaningless on capitalization */ if (id == B_CAPITAL) { @@ -260,7 +260,7 @@ } my_snprintf(buf[2], column_size, "%d", - get_improvement_type(id)->build_cost); + improvement_value_adjusted(id, pcity)); } } diff -Nur -Xfreecivdiff.ignore freeciv-cvs/client/cityrepdata.c freeciv-patched/client/cityrepdata.c --- freeciv-cvs/client/cityrepdata.c 2003-07-24 17:06:29.000000000 +0100 +++ freeciv-patched/client/cityrepdata.c 2003-07-24 17:09:42.000000000 +0100 @@ -374,10 +374,10 @@ if(pcity->is_building_unit) { name = get_unit_type(pcity->currently_building)->name; - cost = get_unit_type(pcity->currently_building)->build_cost; + cost = unit_value_adjusted(pcity->currently_building, pcity); } else { name = get_impr_name_ex(pcity, pcity->currently_building); - cost = get_improvement_type(pcity->currently_building)->build_cost; + cost = improvement_value_adjusted(pcity->currently_building, pcity); } my_snprintf(buf, sizeof(buf), "%s (%d/%d/%s/%d)%s", name, diff -Nur -Xfreecivdiff.ignore freeciv-cvs/client/climisc.c freeciv-patched/client/climisc.c --- freeciv-cvs/client/climisc.c 2003-07-24 17:06:31.000000000 +0100 +++ freeciv-patched/client/climisc.c 2003-07-24 17:09:42.000000000 +0100 @@ -132,7 +132,7 @@ built_impr_iterate(pcity, i) { effect_update = TRUE; - city_remove_improvement(pcity, i); + city_remove_improvement(pcity, i, TRUE); } built_impr_iterate_end; if (effect_update) { @@ -624,7 +624,7 @@ if (is_unit) { name = get_unit_name(id); - cost = get_unit_type(id)->build_cost; + cost = unit_value_adjusted(id, pcity); pitem->section = unit_type_flag(id, F_NONMIL) ? 2 : 3; } else { name = get_impr_name_ex(pcity, id); @@ -632,7 +632,7 @@ cost = -1; pitem->section = 1; } else { - cost = get_improvement_type(id)->build_cost; + cost = improvement_value_adjusted(id, pcity); if (is_wonder(id)) { pitem->section = 4; } else { diff -Nur -Xfreecivdiff.ignore freeciv-cvs/client/gui-gtk/citydlg.c freeciv-patched/client/gui-gtk/citydlg.c --- freeciv-cvs/client/gui-gtk/citydlg.c 2003-07-24 17:06:34.000000000 +0100 +++ freeciv-patched/client/gui-gtk/citydlg.c 2003-07-24 17:09:42.000000000 +0100 @@ -1901,7 +1901,7 @@ get_city_dialog_production(pcity, buf, sizeof(buf)); if (pcity->is_building_unit) { - cost = get_unit_type(pcity->currently_building)->build_cost; + cost = unit_value_adjusted(pcity->currently_building, pcity); descr = get_unit_type(pcity->currently_building)->name; } else { if (pcity->currently_building == B_CAPITAL) { @@ -1909,7 +1909,7 @@ gtk_widget_set_sensitive(pdialog->overview.buy_command, FALSE); cost = 0; } else { - cost = get_improvement_type(pcity->currently_building)->build_cost;; + cost = improvement_value_adjusted(pcity->currently_building, pcity); } descr = get_impr_name_ex(pcity, pcity->currently_building); } @@ -2525,7 +2525,7 @@ if (punit->homecity == pcity->id) { message_dialog_button_set_sensitive(wd, "button5", FALSE); } - if (can_upgrade_unittype(game.player_ptr, punit->type) == -1) { + if (can_upgrade_unittype(game.player_ptr, punit->type, TRUE) == -1) { message_dialog_button_set_sensitive(wd, "button6", FALSE); } } @@ -2679,7 +2679,7 @@ if ((punit = player_find_unit_by_id(game.player_ptr, (size_t) data))) { ut1 = punit->type; - ut2 = can_upgrade_unittype(game.player_ptr, ut1); + ut2 = can_upgrade_unittype(game.player_ptr, ut1, TRUE); if (ut2 == -1) { /* this shouldn't generally happen, but it is conceivable */ diff -Nur -Xfreecivdiff.ignore freeciv-cvs/client/gui-gtk/gotodlg.c freeciv-patched/client/gui-gtk/gotodlg.c --- freeciv-cvs/client/gui-gtk/gotodlg.c 2003-07-24 17:06:37.000000000 +0100 +++ freeciv-patched/client/gui-gtk/gotodlg.c 2003-07-24 17:09:38.000000000 +0100 @@ -198,7 +198,7 @@ if(!all_cities && i!=game.player_idx) continue; city_list_iterate(game.players[i].cities, pcity) { sz_strlcpy(name, pcity->name); - if (pcity->improvements[B_AIRPORT] == I_ACTIVE) + if (pcity->airlift) sz_strlcat(name, "(A)"); gtk_clist_append( GTK_CLIST( goto_list ), row ); } diff -Nur -Xfreecivdiff.ignore freeciv-cvs/client/gui-gtk/happiness.c freeciv-patched/client/gui-gtk/happiness.c --- freeciv-cvs/client/gui-gtk/happiness.c 2003-07-24 17:06:37.000000000 +0100 +++ freeciv-patched/client/gui-gtk/happiness.c 2003-07-24 17:09:37.000000000 +0100 @@ -78,8 +78,6 @@ *pdialog); static void happiness_dialog_update_units(struct happiness_dialog *pdialog); -static void happiness_dialog_update_wonders(struct happiness_dialog - *pdialog); /**************************************************************** ... @@ -199,7 +197,6 @@ happiness_dialog_update_luxury(pdialog); happiness_dialog_update_buildings(pdialog); happiness_dialog_update_units(pdialog); - happiness_dialog_update_wonders(pdialog); } /************************************************************************** @@ -275,59 +272,36 @@ } /************************************************************************** -... + Lists the buildings or Wonders that contribute to happiness **************************************************************************/ -static void happiness_dialog_update_buildings(struct happiness_dialog - *pdialog) +static void set_building_label(char *title, int improvs[B_LAST], + int numperline, char *linespacer, + struct city *pcity, GtkWidget *label) { int faces = 0; - char buf[512], *bptr = buf; - int nleft = sizeof(buf); - struct city *pcity = pdialog->pcity; - struct government *g = get_gov_pcity(pcity); + char buf[512], *bptr; + int nleft, i; + + nleft = sizeof(buf); + bptr = buf; - my_snprintf(bptr, nleft, _("Buildings: ")); + my_snprintf(bptr, nleft, title); bptr = end_of_strn(bptr, &nleft); - if (city_got_building(pcity, B_TEMPLE)) { - faces++; - my_snprintf(bptr, nleft, get_improvement_name(B_TEMPLE)); - bptr = end_of_strn(bptr, &nleft); - my_snprintf(bptr, nleft, _(". ")); - bptr = end_of_strn(bptr, &nleft); - } - if (city_got_building(pcity, B_COURTHOUSE) && g->corruption_level == 0) { - faces++; - my_snprintf(bptr, nleft, get_improvement_name(B_COURTHOUSE)); - bptr = end_of_strn(bptr, &nleft); - my_snprintf(bptr, nleft, _(". ")); - bptr = end_of_strn(bptr, &nleft); - } - if (city_got_building(pcity, B_COLOSSEUM)) { - faces++; - my_snprintf(bptr, nleft, get_improvement_name(B_COLOSSEUM)); - bptr = end_of_strn(bptr, &nleft); - my_snprintf(bptr, nleft, _(". ")); - bptr = end_of_strn(bptr, &nleft); - } - /* hack for eliminating gtk_set_line_wrap() -mck */ - if (faces > 2) { - /* sizeof("Buildings: ") */ - my_snprintf(bptr, nleft, _("\n ")); - bptr = end_of_strn(bptr, &nleft); - } - if (city_got_effect(pcity, B_CATHEDRAL)) { - faces++; - my_snprintf(bptr, nleft, get_improvement_name(B_CATHEDRAL)); - bptr = end_of_strn(bptr, &nleft); - if (!city_got_building(pcity, B_CATHEDRAL)) { - my_snprintf(bptr, nleft, _("(")); - bptr = end_of_strn(bptr, &nleft); - my_snprintf(bptr, nleft, get_improvement_name(B_MICHELANGELO)); - bptr = end_of_strn(bptr, &nleft); - my_snprintf(bptr, nleft, _(")")); + faces = 0; + for (i = 0;i < game.num_impr_types; i++) if (improvs[i]) { + /* hack for eliminating gtk_set_line_wrap() -mck */ + if (faces && faces % numperline == 0) { + my_snprintf(bptr, nleft, linespacer); bptr = end_of_strn(bptr, &nleft); } + faces++; + if (city_got_building(pcity,i)) { + my_snprintf(bptr, nleft, get_improvement_name(i)); + } else { + my_snprintf(bptr, nleft, _("(%s)"), get_improvement_name(i)); + } + bptr = end_of_strn(bptr, &nleft); my_snprintf(bptr, nleft, _(". ")); bptr = end_of_strn(bptr, &nleft); } @@ -337,7 +311,53 @@ bptr = end_of_strn(bptr, &nleft); } - gtk_set_label(pdialog->hlabels[BUILDINGS], buf); + gtk_set_label(label, buf); +} + +/************************************************************************** + Updates the lists of ordinary buildings and Wonders that affect the + number of happy/content citizens, using ruleset improvement effects +**************************************************************************/ +static void happiness_dialog_update_buildings(struct happiness_dialog + *pdialog) +{ + struct city *pcity = pdialog->pcity; + int i, improvs[B_LAST], forceimprovs[B_LAST]; + Impr_Type_id impr; + + for (i = 0; i < game.num_impr_types; i++) { + improvs[i] = forceimprovs[i] = FALSE; + } + + city_effects_iterate(iter, pcity) { + switch(iter.imeff->type) { + case EFT_MAKE_CONTENT: + case EFT_MAKE_CONTENT_PCT: + case EFT_MAKE_HAPPY: + impr = eff_iterator_get_improvement(&iter); + if (improvement_exists(impr)) { + improvs[impr] = TRUE; + } + break; + case EFT_FORCE_CONTENT: + case EFT_FORCE_CONTENT_PCT: + case EFT_FORCE_HAPPY: + impr = eff_iterator_get_improvement(&iter); + if (improvement_exists(impr)) { + forceimprovs[impr] = TRUE; + } + break; + default: + break; + } + } city_effects_iterate_end; + + set_building_label(_("Buildings: "), improvs, 3, + _("\n "), /* sizeof("Buildings: ") */ + pcity, pdialog->hlabels[BUILDINGS]); + set_building_label(_("Wonders: "), forceimprovs, 2, + _("\n "), /* sizeof("Wonders: ") */ + pcity, pdialog->hlabels[WONDERS]); } /************************************************************************** @@ -384,62 +404,6 @@ /************************************************************************** ... **************************************************************************/ -static void happiness_dialog_update_wonders(struct happiness_dialog - *pdialog) -{ - int faces = 0; - char buf[512], *bptr = buf; - int nleft = sizeof(buf); - struct city *pcity = pdialog->pcity; - - my_snprintf(bptr, nleft, _("Wonders: ")); - bptr = end_of_strn(bptr, &nleft); - - if (city_affected_by_wonder(pcity, B_HANGING)) { - faces++; - my_snprintf(bptr, nleft, get_improvement_name(B_HANGING)); - bptr = end_of_strn(bptr, &nleft); - my_snprintf(bptr, nleft, _(". ")); - bptr = end_of_strn(bptr, &nleft); - } - if (city_affected_by_wonder(pcity, B_BACH)) { - faces++; - my_snprintf(bptr, nleft, get_improvement_name(B_BACH)); - bptr = end_of_strn(bptr, &nleft); - my_snprintf(bptr, nleft, _(". ")); - bptr = end_of_strn(bptr, &nleft); - } - /* hack for eliminating gtk_set_line_wrap() -mck */ - if (faces > 1) { - /* sizeof("Wonders: ") */ - my_snprintf(bptr, nleft, _("\n ")); - bptr = end_of_strn(bptr, &nleft); - } - if (city_affected_by_wonder(pcity, B_SHAKESPEARE)) { - faces++; - my_snprintf(bptr, nleft, get_improvement_name(B_SHAKESPEARE)); - bptr = end_of_strn(bptr, &nleft); - my_snprintf(bptr, nleft, _(". ")); - bptr = end_of_strn(bptr, &nleft); - } - if (city_affected_by_wonder(pcity, B_CURE)) { - faces++; - my_snprintf(bptr, nleft, get_improvement_name(B_CURE)); - bptr = end_of_strn(bptr, &nleft); - my_snprintf(bptr, nleft, _(". ")); - bptr = end_of_strn(bptr, &nleft); - } - - if (faces == 0) { - my_snprintf(bptr, nleft, _("None. ")); - } - - gtk_set_label(pdialog->hlabels[WONDERS], buf); -} - -/************************************************************************** -... -**************************************************************************/ GtkWidget *get_top_happiness_display(struct city *pcity) { return create_happiness_dialog(pcity)->shell; diff -Nur -Xfreecivdiff.ignore freeciv-cvs/client/gui-gtk/repodlgs.c freeciv-patched/client/gui-gtk/repodlgs.c --- freeciv-cvs/client/gui-gtk/repodlgs.c 2003-07-24 17:06:35.000000000 +0100 +++ freeciv-patched/client/gui-gtk/repodlgs.c 2003-07-24 17:09:39.000000000 +0100 @@ -891,7 +891,8 @@ void activeunits_list_callback(GtkWidget *w, gint row, gint column) { if ((unit_type_exists(activeunits_type[row])) && - (can_upgrade_unittype(game.player_ptr, activeunits_type[row]) != -1)) + (can_upgrade_unittype(game.player_ptr, + activeunits_type[row], TRUE) != -1)) gtk_widget_set_sensitive(upgrade_command, can_client_issue_orders()); } @@ -932,7 +933,7 @@ return; /* puts(unit_types[ut1].name); */ - ut2 = can_upgrade_unittype(game.player_ptr, activeunits_type[row]); + ut2 = can_upgrade_unittype(game.player_ptr, activeunits_type[row], TRUE); my_snprintf(buf, sizeof(buf), _("Upgrade as many %s to %s as possible for %d gold each?\n" @@ -1014,7 +1015,7 @@ memset(&unittotals, '\0', sizeof(unittotals)); unit_type_iterate(i) { if ((unitarray[i].active_count > 0) || (unitarray[i].building_count > 0)) { - can = (can_upgrade_unittype(game.player_ptr, i) != -1); + can = (can_upgrade_unittype(game.player_ptr, i, TRUE) != -1); my_snprintf(buf[0], sizeof(buf[0]), "%-27s", unit_name(i)); my_snprintf(buf[1], sizeof(buf[1]), "%c", can ? '*': '-'); my_snprintf(buf[2], sizeof(buf[2]), "%9d", unitarray[i].building_count); diff -Nur -Xfreecivdiff.ignore freeciv-cvs/client/gui-gtk-2.0/citydlg.c freeciv-patched/client/gui-gtk-2.0/citydlg.c --- freeciv-cvs/client/gui-gtk-2.0/citydlg.c 2003-07-24 17:06:35.000000000 +0100 +++ freeciv-patched/client/gui-gtk-2.0/citydlg.c 2003-07-24 17:09:42.000000000 +0100 @@ -1505,7 +1505,7 @@ get_city_dialog_production(pcity, buf, sizeof(buf)); if (pcity->is_building_unit) { - cost = get_unit_type(pcity->currently_building)->build_cost; + cost = unit_value_adjusted(pcity->currently_building, pcity); descr = get_unit_type(pcity->currently_building)->name; } else { if (pcity->currently_building == B_CAPITAL) { @@ -1513,7 +1513,7 @@ gtk_widget_set_sensitive(pdialog->overview.buy_command, FALSE); cost = 0; } else { - cost = get_improvement_type(pcity->currently_building)->build_cost;; + cost = improvement_value_adjusted(pcity->currently_building, pcity); } descr = get_impr_name_ex(pcity, pcity->currently_building); } @@ -2043,7 +2043,7 @@ GINT_TO_POINTER(punit->id)); gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - if (can_upgrade_unittype(game.player_ptr, punit->type) == -1) { + if (can_upgrade_unittype(game.player_ptr, punit->type, TRUE) == -1) { gtk_widget_set_sensitive(item, FALSE); } @@ -2221,7 +2221,7 @@ if ((punit = player_find_unit_by_id(game.player_ptr, (size_t) data))) { ut1 = punit->type; - ut2 = can_upgrade_unittype(game.player_ptr, ut1); + ut2 = can_upgrade_unittype(game.player_ptr, ut1, TRUE); if (ut2 == -1) { /* this shouldn't generally happen, but it is conceivable */ diff -Nur -Xfreecivdiff.ignore freeciv-cvs/client/gui-gtk-2.0/gotodlg.c freeciv-patched/client/gui-gtk-2.0/gotodlg.c --- freeciv-cvs/client/gui-gtk-2.0/gotodlg.c 2003-07-24 17:06:37.000000000 +0100 +++ freeciv-patched/client/gui-gtk-2.0/gotodlg.c 2003-07-24 17:09:38.000000000 +0100 @@ -259,7 +259,7 @@ gtk_list_store_set(store, &it, 0, pcity->name, - 1, (pcity->improvements[B_AIRPORT] == I_ACTIVE), + 1, pcity->airlift, -1); } city_list_iterate_end; diff -Nur -Xfreecivdiff.ignore freeciv-cvs/client/gui-gtk-2.0/happiness.c freeciv-patched/client/gui-gtk-2.0/happiness.c --- freeciv-cvs/client/gui-gtk-2.0/happiness.c 2003-07-24 17:06:38.000000000 +0100 +++ freeciv-patched/client/gui-gtk-2.0/happiness.c 2003-07-24 17:09:37.000000000 +0100 @@ -77,8 +77,6 @@ *pdialog); static void happiness_dialog_update_units(struct happiness_dialog *pdialog); -static void happiness_dialog_update_wonders(struct happiness_dialog - *pdialog); /**************************************************************** ... @@ -193,7 +191,6 @@ happiness_dialog_update_luxury(pdialog); happiness_dialog_update_buildings(pdialog); happiness_dialog_update_units(pdialog); - happiness_dialog_update_wonders(pdialog); } /************************************************************************** @@ -269,59 +266,36 @@ } /************************************************************************** -... + Lists the buildings or Wonders that contribute to happiness **************************************************************************/ -static void happiness_dialog_update_buildings(struct happiness_dialog - *pdialog) +static void set_building_label(char *title, int improvs[B_LAST], + int numperline, char *linespacer, + struct city *pcity, GtkWidget *label) { int faces = 0; - char buf[512], *bptr = buf; - int nleft = sizeof(buf); - struct city *pcity = pdialog->pcity; - struct government *g = get_gov_pcity(pcity); + char buf[512], *bptr; + int nleft, i; + + nleft = sizeof(buf); + bptr = buf; - my_snprintf(bptr, nleft, _("Buildings: ")); + my_snprintf(bptr, nleft, title); bptr = end_of_strn(bptr, &nleft); - if (city_got_building(pcity, B_TEMPLE)) { - faces++; - my_snprintf(bptr, nleft, get_improvement_name(B_TEMPLE)); - bptr = end_of_strn(bptr, &nleft); - my_snprintf(bptr, nleft, _(". ")); - bptr = end_of_strn(bptr, &nleft); - } - if (city_got_building(pcity, B_COURTHOUSE) && g->corruption_level == 0) { - faces++; - my_snprintf(bptr, nleft, get_improvement_name(B_COURTHOUSE)); - bptr = end_of_strn(bptr, &nleft); - my_snprintf(bptr, nleft, _(". ")); - bptr = end_of_strn(bptr, &nleft); - } - if (city_got_building(pcity, B_COLOSSEUM)) { - faces++; - my_snprintf(bptr, nleft, get_improvement_name(B_COLOSSEUM)); - bptr = end_of_strn(bptr, &nleft); - my_snprintf(bptr, nleft, _(". ")); - bptr = end_of_strn(bptr, &nleft); - } - /* hack for eliminating gtk_set_line_wrap() -mck */ - if (faces > 2) { - /* sizeof("Buildings: ") */ - my_snprintf(bptr, nleft, _("\n ")); - bptr = end_of_strn(bptr, &nleft); - } - if (city_got_effect(pcity, B_CATHEDRAL)) { - faces++; - my_snprintf(bptr, nleft, get_improvement_name(B_CATHEDRAL)); - bptr = end_of_strn(bptr, &nleft); - if (!city_got_building(pcity, B_CATHEDRAL)) { - my_snprintf(bptr, nleft, _("(")); - bptr = end_of_strn(bptr, &nleft); - my_snprintf(bptr, nleft, get_improvement_name(B_MICHELANGELO)); - bptr = end_of_strn(bptr, &nleft); - my_snprintf(bptr, nleft, _(")")); + faces = 0; + for (i = 0;i < game.num_impr_types; i++) if (improvs[i]) { + /* hack for eliminating gtk_set_line_wrap() -mck */ + if (faces && faces % numperline == 0) { + my_snprintf(bptr, nleft, linespacer); bptr = end_of_strn(bptr, &nleft); } + faces++; + if (city_got_building(pcity,i)) { + my_snprintf(bptr, nleft, get_improvement_name(i)); + } else { + my_snprintf(bptr, nleft, _("(%s)"), get_improvement_name(i)); + } + bptr = end_of_strn(bptr, &nleft); my_snprintf(bptr, nleft, _(". ")); bptr = end_of_strn(bptr, &nleft); } @@ -331,7 +305,53 @@ bptr = end_of_strn(bptr, &nleft); } - gtk_label_set_text(GTK_LABEL(pdialog->hlabels[BUILDINGS]), buf); + gtk_set_label(label, buf); +} + +/************************************************************************** + Updates the lists of ordinary buildings and Wonders that affect the + number of happy/content citizens, using ruleset improvement effects +**************************************************************************/ +static void happiness_dialog_update_buildings(struct happiness_dialog + *pdialog) +{ + struct city *pcity = pdialog->pcity; + int i, improvs[B_LAST], forceimprovs[B_LAST]; + Impr_Type_id impr; + + for (i = 0; i < game.num_impr_types; i++) { + improvs[i] = forceimprovs[i] = FALSE; + } + + city_effects_iterate(iter, pcity) { + switch(iter.imeff->type) { + case EFT_MAKE_CONTENT: + case EFT_MAKE_CONTENT_PCT: + case EFT_MAKE_HAPPY: + impr = eff_iterator_get_improvement(&iter); + if (improvement_exists(impr)) { + improvs[impr] = TRUE; + } + break; + case EFT_FORCE_CONTENT: + case EFT_FORCE_CONTENT_PCT: + case EFT_FORCE_HAPPY: + impr = eff_iterator_get_improvement(&iter); + if (improvement_exists(impr)) { + forceimprovs[impr] = TRUE; + } + break; + default: + break; + } + } city_effects_iterate_end; + + set_building_label(_("Buildings: "), improvs, 3, + _("\n "), /* sizeof("Buildings: ") */ + pcity, pdialog->hlabels[BUILDINGS]); + set_building_label(_("Wonders: "), forceimprovs, 2, + _("\n "), /* sizeof("Wonders: ") */ + pcity, pdialog->hlabels[WONDERS]); } /************************************************************************** @@ -378,62 +398,6 @@ /************************************************************************** ... **************************************************************************/ -static void happiness_dialog_update_wonders(struct happiness_dialog - *pdialog) -{ - int faces = 0; - char buf[512], *bptr = buf; - int nleft = sizeof(buf); - struct city *pcity = pdialog->pcity; - - my_snprintf(bptr, nleft, _("Wonders: ")); - bptr = end_of_strn(bptr, &nleft); - - if (city_affected_by_wonder(pcity, B_HANGING)) { - faces++; - my_snprintf(bptr, nleft, get_improvement_name(B_HANGING)); - bptr = end_of_strn(bptr, &nleft); - my_snprintf(bptr, nleft, _(". ")); - bptr = end_of_strn(bptr, &nleft); - } - if (city_affected_by_wonder(pcity, B_BACH)) { - faces++; - my_snprintf(bptr, nleft, get_improvement_name(B_BACH)); - bptr = end_of_strn(bptr, &nleft); - my_snprintf(bptr, nleft, _(". ")); - bptr = end_of_strn(bptr, &nleft); - } - /* hack for eliminating gtk_set_line_wrap() -mck */ - if (faces > 1) { - /* sizeof("Wonders: ") */ - my_snprintf(bptr, nleft, _("\n ")); - bptr = end_of_strn(bptr, &nleft); - } - if (city_affected_by_wonder(pcity, B_SHAKESPEARE)) { - faces++; - my_snprintf(bptr, nleft, get_improvement_name(B_SHAKESPEARE)); - bptr = end_of_strn(bptr, &nleft); - my_snprintf(bptr, nleft, _(". ")); - bptr = end_of_strn(bptr, &nleft); - } - if (city_affected_by_wonder(pcity, B_CURE)) { - faces++; - my_snprintf(bptr, nleft, get_improvement_name(B_CURE)); - bptr = end_of_strn(bptr, &nleft); - my_snprintf(bptr, nleft, _(". ")); - bptr = end_of_strn(bptr, &nleft); - } - - if (faces == 0) { - my_snprintf(bptr, nleft, _("None. ")); - } - - gtk_label_set_text(GTK_LABEL(pdialog->hlabels[WONDERS]), buf); -} - -/************************************************************************** -... -**************************************************************************/ GtkWidget *get_top_happiness_display(struct city *pcity) { return create_happiness_dialog(pcity)->shell; diff -Nur -Xfreecivdiff.ignore freeciv-cvs/client/gui-gtk-2.0/repodlgs.c freeciv-patched/client/gui-gtk-2.0/repodlgs.c --- freeciv-cvs/client/gui-gtk-2.0/repodlgs.c 2003-07-24 17:06:35.000000000 +0100 +++ freeciv-patched/client/gui-gtk-2.0/repodlgs.c 2003-07-24 17:09:39.000000000 +0100 @@ -1001,7 +1001,8 @@ if (row < n-2 && unit_type_exists(activeunits_type[row]) && - can_upgrade_unittype(game.player_ptr, activeunits_type[row]) != -1) { + can_upgrade_unittype(game.player_ptr, + activeunits_type[row], TRUE) != -1) { gtk_widget_set_sensitive(upgrade_command, can_client_issue_orders()); } else { gtk_widget_set_sensitive(upgrade_command, FALSE); @@ -1030,7 +1031,7 @@ if (!unit_type_exists(ut1)) return; - ut2 = can_upgrade_unittype(game.player_ptr, activeunits_type[row]); + ut2 = can_upgrade_unittype(game.player_ptr, activeunits_type[row], TRUE); shell = gtk_message_dialog_new( GTK_WINDOW(activeunits_dialog_shell), @@ -1096,7 +1097,7 @@ unit_type_iterate(i) { if ((unitarray[i].active_count > 0) || (unitarray[i].building_count > 0)) { - can = (can_upgrade_unittype(game.player_ptr, i) != -1); + can = (can_upgrade_unittype(game.player_ptr, i, TRUE) != -1); gtk_list_store_append(activeunits_store, &it); gtk_list_store_set(activeunits_store, &it, diff -Nur -Xfreecivdiff.ignore freeciv-cvs/client/gui-mui/dialogs.c freeciv-patched/client/gui-mui/dialogs.c --- freeciv-cvs/client/gui-mui/dialogs.c 2003-07-24 17:06:36.000000000 +0100 +++ freeciv-patched/client/gui-mui/dialogs.c 2003-07-24 17:09:39.000000000 +0100 @@ -1794,7 +1794,7 @@ int value; ut1 = punit->type; - ut2 = can_upgrade_unittype(game.player_ptr,ut1); + ut2 = can_upgrade_unittype(game.player_ptr, ut1, TRUE); if (ut2 == -1) { diff -Nur -Xfreecivdiff.ignore freeciv-cvs/client/gui-mui/gotodlg.c freeciv-patched/client/gui-mui/gotodlg.c --- freeciv-cvs/client/gui-mui/gotodlg.c 2003-07-24 17:06:37.000000000 +0100 +++ freeciv-patched/client/gui-mui/gotodlg.c 2003-07-24 17:09:38.000000000 +0100 @@ -58,7 +58,7 @@ { static char name[80]; sz_strlcpy(name, pcity->name); - if (pcity->improvements[B_AIRPORT] == I_ACTIVE) + if (pcity->airlift) sz_strlcat(name, "(A)"); *array = name; } diff -Nur -Xfreecivdiff.ignore freeciv-cvs/client/gui-mui/mapclass.c freeciv-patched/client/gui-mui/mapclass.c --- freeciv-cvs/client/gui-mui/mapclass.c 2003-07-24 17:06:36.000000000 +0100 +++ freeciv-patched/client/gui-mui/mapclass.c 2003-07-24 17:09:39.000000000 +0100 @@ -2962,7 +2962,7 @@ } } - if (can_upgrade_unittype(game.player_ptr,punit->type) != -1) + if (can_upgrade_unittype(game.player_ptr, punit->type, TRUE) != -1) { if ((entry = MUI_MakeObject(MUIO_Menuitem, _("Upgrade"), NULL, MUIO_Menuitem_CopyStrings, 0))) { diff -Nur -Xfreecivdiff.ignore freeciv-cvs/client/gui-mui/repodlgs.c freeciv-patched/client/gui-mui/repodlgs.c --- freeciv-cvs/client/gui-mui/repodlgs.c 2003-07-24 17:06:36.000000000 +0100 +++ freeciv-patched/client/gui-mui/repodlgs.c 2003-07-24 17:09:39.000000000 +0100 @@ -702,7 +702,7 @@ my_snprintf(building_count, sizeof(building_count), "%5d", entry->building_count); *array++ = unit_name(i); - *array++ = can_upgrade_unittype(game.player_ptr, i) != -1 ? "*" : "-"; + *array++ = can_upgrade_unittype(game.player_ptr, i, TRUE) != -1 ? "*" : "-"; *array++ = building_count; *array++ = active_count; *array++ = upkeep_shield; @@ -729,7 +729,7 @@ DoMethod(actunit_units_listview, MUIM_NList_GetEntry, *newval, &entry); if (entry) { - if (can_upgrade_unittype(game.player_ptr, entry->type) != -1) + if (can_upgrade_unittype(game.player_ptr, entry->type, TRUE) != -1) set(actunit_upgrade_button, MUIA_Disabled, FALSE); else set(actunit_upgrade_button, MUIA_Disabled, TRUE); @@ -758,7 +758,7 @@ int ut1, ut2; ut1 = entry->type; - ut2 = can_upgrade_unittype(game.player_ptr, entry->type); + ut2 = can_upgrade_unittype(game.player_ptr, entry->type, TRUE); if (ut2 != -1) { diff -Nur -Xfreecivdiff.ignore freeciv-cvs/client/gui-sdl/repodlgs.c freeciv-patched/client/gui-sdl/repodlgs.c --- freeciv-cvs/client/gui-sdl/repodlgs.c 2003-07-24 17:06:36.000000000 +0100 +++ freeciv-patched/client/gui-sdl/repodlgs.c 2003-07-24 17:09:39.000000000 +0100 @@ -194,7 +194,7 @@ pUnits_Upg_Dlg = MALLOC(sizeof(struct SMALL_DLG)); - ut2 = can_upgrade_unittype(game.player_ptr, ut1); + ut2 = can_upgrade_unittype(game.player_ptr, ut1, TRUE); value = unit_upgrade_price(game.player_ptr, ut1, ut2); my_snprintf(cBuf, sizeof(cBuf), @@ -453,7 +453,7 @@ count = 0; unit_type_iterate(i) { if ((units[i].active_count > 0) || (units[i].building_count > 0)) { - upgrade = (can_upgrade_unittype(game.player_ptr, i) != -1); + upgrade = (can_upgrade_unittype(game.player_ptr, i, TRUE) != -1); pUnit = get_unit_type(i); /* ----------- */ @@ -844,7 +844,7 @@ pBuf = pWidget; if ((units[i].active_count > 0) || (units[i].building_count > 0)) { if (i == MAX_ID - pBuf->ID) { -UPD: upgrade = (can_upgrade_unittype(game.player_ptr, i) != -1); +UPD: upgrade = (can_upgrade_unittype(game.player_ptr, i, TRUE) != -1); pBuf = pBuf->prev; if(upgrade) { pBuf->string16->forecol = sellect; diff -Nur -Xfreecivdiff.ignore freeciv-cvs/client/gui-win32/citydlg.c freeciv-patched/client/gui-win32/citydlg.c --- freeciv-cvs/client/gui-win32/citydlg.c 2003-07-24 17:06:36.000000000 +0100 +++ freeciv-patched/client/gui-win32/citydlg.c 2003-07-24 17:09:39.000000000 +0100 @@ -1559,7 +1559,7 @@ ut1=punit->type; /* printf("upgrade_callback for %s\n", unit_types[ut1].name); */ - ut2=can_upgrade_unittype(game.player_ptr,ut1); + ut2 = can_upgrade_unittype(game.player_ptr, ut1, TRUE); if (ut2==-1) { /* this shouldn't generally happen, but it is conceivable */ @@ -1669,7 +1669,7 @@ if (punit->homecity == pcity->id) { message_dialog_button_set_sensitive(wd,5, FALSE); } - if (can_upgrade_unittype(game.player_ptr,punit->type) == -1) { + if (can_upgrade_unittype(game.player_ptr, punit->type, TRUE) == -1) { message_dialog_button_set_sensitive(wd,6, FALSE); } } diff -Nur -Xfreecivdiff.ignore freeciv-cvs/client/gui-win32/gotodlg.c freeciv-patched/client/gui-win32/gotodlg.c --- freeciv-cvs/client/gui-win32/gotodlg.c 2003-07-24 17:06:37.000000000 +0100 +++ freeciv-patched/client/gui-win32/gotodlg.c 2003-07-24 17:09:38.000000000 +0100 @@ -189,7 +189,7 @@ if(!show_all_cities && i!=game.player_idx) continue; city_list_iterate(game.players[i].cities, pcity) { sz_strlcpy(name, pcity->name); - if (pcity->improvements[B_AIRPORT] == I_ACTIVE) + if (pcity->airlift) sz_strlcat(name, "(A)"); j=ListBox_AddString(list,name); ListBox_SetItemData(list,j,pcity->id); diff -Nur -Xfreecivdiff.ignore freeciv-cvs/client/gui-win32/repodlgs.c freeciv-patched/client/gui-win32/repodlgs.c --- freeciv-cvs/client/gui-win32/repodlgs.c 2003-07-24 17:06:36.000000000 +0100 +++ freeciv-patched/client/gui-win32/repodlgs.c 2003-07-24 17:09:39.000000000 +0100 @@ -544,7 +544,7 @@ if (sel>=0) { if ((unit_type_exists(activeunits_type[sel])) && (can_upgrade_unittype(game.player_ptr, - activeunits_type[sel]) != -1)) + activeunits_type[sel], TRUE) != -1)) EnableWindow(GetDlgItem(activeunits_dlg,ID_MILITARY_UPGRADE), TRUE); else @@ -570,7 +570,8 @@ ut1 = activeunits_type[sel]; if (!(unit_type_exists (ut1))) break; - ut2=can_upgrade_unittype(game.player_ptr,activeunits_type[sel]); + ut2 = can_upgrade_unittype(game.player_ptr, + activeunits_type[sel], TRUE); my_snprintf(buf, sizeof(buf), _("Upgrade as many %s to %s as possible for %d gold each?\n" "Treasury contains %d gold."), @@ -649,7 +650,7 @@ unit_type_iterate(i) { if ((unitarray[i].active_count > 0) || (unitarray[i].building_count > 0)) { - can = (can_upgrade_unittype(game.player_ptr, i) != -1); + can = (can_upgrade_unittype(game.player_ptr, i, TRUE) != -1); my_snprintf(buf[0], sizeof(buf[0]), "%s", unit_name(i)); my_snprintf(buf[1], sizeof(buf[1]), "%c", can ? '*': '-'); my_snprintf(buf[2], sizeof(buf[2]), "%3d", unitarray[i].building_count); diff -Nur -Xfreecivdiff.ignore freeciv-cvs/client/gui-xaw/citydlg.c freeciv-patched/client/gui-xaw/citydlg.c --- freeciv-cvs/client/gui-xaw/citydlg.c 2003-07-24 17:06:36.000000000 +0100 +++ freeciv-patched/client/gui-xaw/citydlg.c 2003-07-24 17:09:39.000000000 +0100 @@ -1334,7 +1334,7 @@ if (punit->homecity == pcity->id) { XtSetSensitive(XtNameToWidget(wd, "*button5"), FALSE); } - if (can_upgrade_unittype(game.player_ptr,punit->type) == -1) { + if (can_upgrade_unittype(game.player_ptr, punit->type, TRUE) == -1) { XtSetSensitive(XtNameToWidget(wd, "*button6"), FALSE); } } @@ -2039,7 +2039,7 @@ ut1 = punit->type; /* printf("upgrade_callback for %s\n", unit_types[ut1].name); */ - ut2 = can_upgrade_unittype(game.player_ptr,ut1); + ut2 = can_upgrade_unittype(game.player_ptr, ut1, TRUE); if ( ut2 == -1 ) { /* this shouldn't generally happen, but it is conceivable */ diff -Nur -Xfreecivdiff.ignore freeciv-cvs/client/gui-xaw/gotodlg.c freeciv-patched/client/gui-xaw/gotodlg.c --- freeciv-cvs/client/gui-xaw/gotodlg.c 2003-07-24 17:06:37.000000000 +0100 +++ freeciv-patched/client/gui-xaw/gotodlg.c 2003-07-24 17:09:38.000000000 +0100 @@ -223,7 +223,7 @@ city_list_iterate(game.players[i].cities, pcity) { char name[MAX_LEN_NAME+3]; sz_strlcpy(name, pcity->name); - if (pcity->improvements[B_AIRPORT] == I_ACTIVE) + if (pcity->airlift) sz_strlcat(name, "(A)"); city_name_ptrs[j++]=mystrdup(name); } diff -Nur -Xfreecivdiff.ignore freeciv-cvs/client/gui-xaw/repodlgs.c freeciv-patched/client/gui-xaw/repodlgs.c --- freeciv-cvs/client/gui-xaw/repodlgs.c 2003-07-24 17:06:36.000000000 +0100 +++ freeciv-patched/client/gui-xaw/repodlgs.c 2003-07-24 17:09:39.000000000 +0100 @@ -970,7 +970,8 @@ may_upgrade = ((inx != XAW_LIST_NONE) && (unit_type_exists (activeunits_type[inx])) && - (can_upgrade_unittype (game.player_ptr, activeunits_type[inx]) != -1)); + (can_upgrade_unittype (game.player_ptr, + activeunits_type[inx], TRUE) != -1)); XtSetSensitive (upgrade_command, may_upgrade); } @@ -1015,7 +1016,7 @@ /* puts(unit_types[ut1].name); */ ut2 = can_upgrade_unittype(game.player_ptr, - activeunits_type[ret->list_index]); + activeunits_type[ret->list_index], TRUE); my_snprintf(buf, sizeof(buf), _("Upgrade as many %s to %s as possible for %d gold each?\n" @@ -1110,7 +1111,7 @@ sizeof(activeunits_list_names[k]), "%-27s%c%9d%9d%9d%9d", unit_name(i), - can_upgrade_unittype(game.player_ptr, i) != -1 ? '*': '-', + can_upgrade_unittype(game.player_ptr, i, TRUE) != -1 ? '*': '-', unitarray[i].building_count, unitarray[i].active_count, unitarray[i].upkeep_shield, diff -Nur -Xfreecivdiff.ignore freeciv-cvs/client/helpdata.c freeciv-patched/client/helpdata.c --- freeciv-cvs/client/helpdata.c 2003-07-24 17:06:33.000000000 +0100 +++ freeciv-patched/client/helpdata.c 2003-07-24 17:09:40.000000000 +0100 @@ -504,40 +504,16 @@ assert(buf&&user_text); buf[0] = '\0'; - if(which==B_AQUEDUCT) { - sprintf(buf+strlen(buf), _("Allows a city to grow larger than size %d. "), - game.aqueduct_size); - if(improvement_exists(B_SEWER)) { - char *s = improvement_types[B_SEWER].name; - sprintf(buf+strlen(buf), - _("(The %s improvement is also required for a city to grow" - " larger than size %d.) "), s, game.sewer_size); - } - strcat(buf,"\n"); - } - if(which==B_SEWER) { - sprintf(buf+strlen(buf), _("Allows a city to grow larger than size %d. "), - game.sewer_size); - } if (imp->helptext) { sprintf(buf+strlen(buf), "%s ", _(imp->helptext)); } - if(which==B_BARRACKS - && tech_exists(improvement_types[B_BARRACKS].obsolete_by) - && tech_exists(improvement_types[B_BARRACKS2].obsolete_by)) { - sprintf(buf+strlen(buf), - _("\n\nNote that discovering %s or %s will obsolete" - " any existing %s. "), - advances[improvement_types[B_BARRACKS].obsolete_by].name, - advances[improvement_types[B_BARRACKS2].obsolete_by].name, - improvement_types[B_BARRACKS].name); - } - if(which==B_BARRACKS2 - && tech_exists(improvement_types[B_BARRACKS2].obsolete_by)) { + if (improvement_types[which].obsolete_by != A_NONE && + tech_exists(improvement_types[which].obsolete_by)) { sprintf(buf+strlen(buf), - _("\n\nThe discovery of %s will make %s obsolete. "), - advances[improvement_types[B_BARRACKS2].obsolete_by].name, - improvement_types[B_BARRACKS2].name); + _("\n\nNote that discovering %s will obsolete" + " any existing %s. "), + advances[improvement_types[which].obsolete_by].name, + improvement_types[which].name); } if (strcmp(user_text, "")!=0) { sprintf(buf+strlen(buf), "\n\n%s", user_text); @@ -555,16 +531,6 @@ assert(buf&&user_text); buf[0] = '\0'; - if(which==B_MANHATTEN && num_role_units(F_NUCLEAR)>0) { - int u, t; - u = get_role_unit(F_NUCLEAR, 0); - assert(utech_requirement; - assert(tname); - } if (imp->helptext) { sprintf(buf+strlen(buf), "%s ", _(imp->helptext)); } diff -Nur -Xfreecivdiff.ignore freeciv-cvs/client/packhand.c freeciv-patched/client/packhand.c --- freeciv-cvs/client/packhand.c 2003-07-24 17:06:38.000000000 +0100 +++ freeciv-patched/client/packhand.c 2003-07-24 17:09:42.000000000 +0100 @@ -76,6 +76,16 @@ static int *reports_thaw_requests = NULL; static int reports_thaw_requests_size = 0; +/* Keeps track of whether we are in a turn update or not. This is used by + * the city packet handling routines to decide whether to update improvement + * effects immediately on receiving new improvements, or to defer the update + * until the start of the turn. */ +static enum { + EU_BEFORE_YEAR, + EU_UPDATE_DEFERRED, + EU_WITHIN_YEAR +} effect_update_state = EU_BEFORE_YEAR; + /************************************************************************** Unpackage the unit information into a newly allocated unit structure. **************************************************************************/ @@ -120,6 +130,9 @@ char msg[MAX_LEN_MSG]; char *s_capability = aconnection.capability; + /* Defer all effect updates until the start of the first year */ + effect_update_state = EU_BEFORE_YEAR; + sz_strlcpy(aconnection.capability, packet->capability); close_connection_dialog(); @@ -257,7 +270,7 @@ *impr_changed = TRUE; } } else if (!have_impr && pcity->improvements[impr] != I_NONE) { - city_remove_improvement(pcity, impr); + city_remove_improvement(pcity, impr, FALSE); if (impr_changed) { *impr_changed = TRUE; @@ -271,15 +284,33 @@ static void try_update_effects(bool need_update) { if (need_update) { - /* nothing yet... */ + if (effect_update_state == EU_WITHIN_YEAR) { + update_all_effects(); + } else { + effect_update_state = EU_UPDATE_DEFERRED; + } } } /************************************************************************** + If any effect updates were deferred by try_update_effects(), update them +**************************************************************************/ +static void do_deferred_effect_updates(void) +{ + if (effect_update_state == EU_UPDATE_DEFERRED) { + update_all_effects(); + } + effect_update_state = EU_WITHIN_YEAR; +} + +/************************************************************************** ... **************************************************************************/ void handle_game_state(struct packet_generic_integer *packet) { + /* If any effect updates were deferred, update effects now */ + do_deferred_effect_updates(); + if (get_client_state() == CLIENT_SELECT_RACE_STATE && packet->value == CLIENT_GAME_RUNNING_STATE && game.player_ptr->nation == NO_NATION_SELECTED) { @@ -391,6 +422,9 @@ pcity->trade[i]=packet->trade[i]; pcity->trade_value[i]=packet->trade_value[i]; } + + pcity->buildflags = packet->buildflags; + pcity->upkeep_free = packet->upkeep_free; pcity->food_prod=packet->food_prod; pcity->food_surplus=packet->food_surplus; @@ -417,6 +451,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; @@ -432,6 +469,10 @@ pcity->disbanded_shields=packet->disbanded_shields; pcity->caravan_shields=packet->caravan_shields; + if (city_is_new) { + update_city_bonuses(pcity); + } + i=0; for(y=0; yowner==game.player_idx && popup_new_cities) || packet->diplomat_investigate; + try_update_effects(need_effect_update); + if (city_is_new && !city_has_changed_owner) { agents_city_new(pcity); } else { @@ -471,8 +514,6 @@ } handle_city_packet_common(pcity, city_is_new, popup, packet->diplomat_investigate); - - try_update_effects(need_effect_update); } /************************************************************************** @@ -607,6 +648,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, @@ -652,11 +696,17 @@ pcity->did_sell = FALSE; pcity->was_happy = FALSE; + if (city_is_new) { + update_city_bonuses(pcity); + } + for(y=0; ycity_map[x][y] = C_TILE_EMPTY; } /* Dumb values */ + try_update_effects(need_effect_update); + if (city_is_new && !city_has_changed_owner) { agents_city_new(pcity); } else { @@ -669,8 +719,6 @@ if (update_descriptions && tile_visible_mapcanvas(pcity->x,pcity->y)) { queue_mapview_update(UPDATE_CITY_DESCRIPTIONS); } - - try_update_effects(need_effect_update); } /************************************************************************** @@ -678,6 +726,9 @@ **************************************************************************/ void handle_new_year(struct packet_new_year *ppacket) { + /* If any effect updates were deferred, update effects now */ + do_deferred_effect_updates(); + game.year = ppacket->year; /* * The turn was increased in handle_before_new_year() @@ -716,6 +767,9 @@ **************************************************************************/ void handle_before_new_year(void) { + /* Defer all effect updates until the start of the year */ + effect_update_state = EU_BEFORE_YEAR; + clear_notify_window(); /* * The local idea of the game turn is increased here since the @@ -734,6 +788,9 @@ **************************************************************************/ void handle_start_turn(void) { + /* If any effect updates were deferred, update effects now */ + do_deferred_effect_updates(); + agents_start_turn(); non_ai_unit_focus = FALSE; @@ -1132,6 +1189,43 @@ /************************************************************************** ... **************************************************************************/ +static bool update_wonder(Impr_Type_id impr, int cityid, int plrno) +{ + struct player *pplayer; + bool retval = FALSE; + + if (!is_wonder(impr)) + return FALSE; + + if (plrno == -1) { + pplayer = NULL; + } else { + pplayer = &game.players[plrno]; + } + + /* Newly destroyed wonder */ + if (cityid == -1 && game.global_wonders[impr] >= 0) { + add_destroyed_improvement(pplayer, impr); + retval = TRUE; + } + + if (cityid >= 0 && improvement_types[impr].equiv_range == EFR_WORLD) { + if (cityid == -1 && game.improvements[impr] != I_NONE) { + game.improvements[impr] = I_NONE; + retval = TRUE; + } else if (cityid >= 0 && game.improvements[impr] == I_NONE) { + game.improvements[impr] = I_ACTIVE; + retval = TRUE; + } + } + game.global_wonders[impr] = cityid; + game.destroyed_owner[impr] = pplayer; + return retval; +} + +/************************************************************************** +... +**************************************************************************/ void handle_game_info(struct packet_game_info *pinfo) { int i; @@ -1154,29 +1248,35 @@ game.nuclearwinter=pinfo->nuclearwinter; game.cooling=pinfo->cooling; if (!can_client_change_view()) { + /* Nasty kludge to let old code which assumes B_PALACE and B_CITY work */ + B_PALACE = get_improvement_for_effect(NULL, EFT_CAPITAL_CITY, NULL); + if (B_PALACE == B_LAST) { + freelog(LOG_ERROR, "Cannot find any capital city improvement"); + exit(EXIT_FAILURE); + } + + B_CITY = get_improvement_for_effect(NULL, EFT_UNIT_DEFEND, NULL); + if (B_CITY == B_LAST) { + freelog(LOG_ERROR, "Cannot find any city walls improvement"); + exit(EXIT_FAILURE); + } + 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]; } for(i=0; iglobal_advances[i]; for(i=0; iglobal_wonders[i]; -/* Only add in the improvement if it's in a "foreign" (i.e. unknown) city - and has equiv_range==World - otherwise we deal with it in its home - city anyway */ - if (is_wonder(i) && improvement_types[i].equiv_range==EFR_WORLD && - !find_city_by_id(game.global_wonders[i])) { - if (game.global_wonders[i] <= 0 && game.improvements[i] != I_NONE) { - game.improvements[i] = I_NONE; - need_effect_update = TRUE; - } else if (game.global_wonders[i] > 0 && game.improvements[i] == I_NONE) { - game.improvements[i] = I_ACTIVE; - need_effect_update = TRUE; - } - } + need_effect_update |= update_wonder(i, pinfo->global_wonders[i], + pinfo->destroyed_owner[i]); } /* Only update effects if a new wonder appeared or was destroyed */ @@ -1309,9 +1409,14 @@ bool poptechup, new_tech = FALSE; char msg[MAX_LEN_MSG]; struct player *pplayer = &game.players[pinfo->playerno]; + bool govnation_changed = FALSE; sz_strlcpy(pplayer->name, pinfo->name); + if (pplayer->nation != pinfo->nation + || pplayer->government != pinfo->government) { + govnation_changed = TRUE; + } pplayer->nation=pinfo->nation; pplayer->is_male=pinfo->is_male; pplayer->team = pinfo->team; @@ -1325,6 +1430,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; @@ -1392,6 +1505,11 @@ append_output_window(msg); } } + + if (govnation_changed) { + player_update_global_effects(pplayer); + update_all_effects(); + } if (pplayer == game.player_ptr && (pplayer->revolution < 1 || pplayer->revolution > 5) && @@ -1853,6 +1971,7 @@ game.government_when_anarchy = packet->government_when_anarchy; game.default_government = packet->default_government; + game.num_unit_flags = packet->num_unit_flags; game.num_unit_types = packet->num_unit_types; game.num_impr_types = packet->num_impr_types; game.num_tech_types = packet->num_tech_types; @@ -1918,6 +2037,7 @@ u->paratroopers_range = p->paratroopers_range; u->paratroopers_mr_req = p->paratroopers_mr_req; u->paratroopers_mr_sub = p->paratroopers_mr_sub; + u->effect = p->effect; /* pointer assignment */ u->helptext = p->helptext; /* pointer assignment */ @@ -2089,7 +2209,7 @@ b->effect[inx].aff_unit, (b->effect[inx].aff_unit == UCL_LAST) ? "All" : - unit_class_name(b->effect[inx].aff_unit)); + unit_class_names[b->effect[inx].aff_unit]); ptr = strchr(ptr, '\0'); my_snprintf(ptr, sizeof(buf)-(ptr-buf), " aff_terr=%d/%s", b->effect[inx].aff_terr, @@ -2195,6 +2315,7 @@ gov->ruler_titles = fc_calloc(gov->num_ruler_titles, sizeof(struct ruler_title)); + gov->effect = p->effect; /* pointer assignment */ gov->helptext = p->helptext; /* pointer assignment */ tilespec_setup_government(p->id); @@ -2330,6 +2451,7 @@ pl->leaders[i].is_male = p->leader_sex[i]; } pl->city_style = p->city_style; + pl->effect = p->effect; /* pointer assignment */ if (p->class[0] != '\0') { pl->class = mystrdup(p->class); diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/aicore/pf_tools.c freeciv-patched/common/aicore/pf_tools.c --- freeciv-cvs/common/aicore/pf_tools.c 2003-07-24 17:06:33.000000000 +0100 +++ freeciv-patched/common/aicore/pf_tools.c 2003-07-24 17:09:40.000000000 +0100 @@ -322,8 +322,7 @@ parameter->get_zoc = NULL; } - if (unit_flag(punit, F_TRIREME) - && base_trireme_loss_pct(unit_owner(punit)) > 0) { + if (base_ship_loss_pct(punit, NULL) > 0) { parameter->turn_mode = TM_WORST_TIME; parameter->is_pos_dangerous = trireme_is_pos_dangerous; } else { @@ -363,8 +362,7 @@ parameter->get_zoc = NULL; - if (unit_flag(punit, F_TRIREME) - && base_trireme_loss_pct(unit_owner(punit)) > 0) { + if (base_ship_loss_pct(punit, NULL) > 0) { parameter->is_pos_dangerous = trireme_is_pos_dangerous; } else { parameter->is_pos_dangerous = NULL; diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/capstr.c freeciv-patched/common/capstr.c --- freeciv-cvs/common/capstr.c 2003-07-24 17:06:34.000000000 +0100 +++ freeciv-patched/common/capstr.c 2003-07-24 17:09:39.000000000 +0100 @@ -79,7 +79,7 @@ "+impr_req +waste +fastfocus +continent +small_dipl " \ "+no_nation_selected +diplomacy +no_extra_tiles " \ "+diplomacy2 +citizens_style +root_tech auth " \ - "+nat_ulimit retake +goto_pack borders" + "+nat_ulimit retake +goto_pack borders impr_gen" /* "+1.14.0" is protocol for 1.14.0 release. * diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/city.c freeciv-patched/common/city.c --- freeciv-cvs/common/city.c 2003-07-24 17:06:38.000000000 +0100 +++ freeciv-patched/common/city.c 2003-07-24 17:09:42.000000000 +0100 @@ -41,8 +41,7 @@ static void citizen_happy_size(struct city *pcity); static void citizen_happy_luxury(struct city *pcity); static void citizen_happy_units(struct city *pcity, int unhap); -static void citizen_happy_buildings(struct city *pcity); -static void citizen_happy_wonders(struct city *pcity); +static void citizen_happy_buildings(struct city *pcity, int wonder); static void unhappy_city_check(struct city *pcity); static void set_pollution(struct city *pcity); static void set_food_trade_shields(struct city *pcity); @@ -220,10 +219,10 @@ && city_affected_by_wonder(pcity, B_MICHELANGELO)) return TRUE; break; - case B_CITY: +/*case B_CITY: if (city_affected_by_wonder(pcity, B_WALL)) return TRUE; - break; + break;*/ case B_HYDRO: case B_POWER: case B_NUCLEAR: @@ -284,12 +283,12 @@ int build=pcity->shield_stock; if (pcity->is_building_unit) { - total=unit_value(pcity->currently_building); + total = unit_value_adjusted(pcity->currently_building, pcity); if (build>=total) return 0; cost=(total-build)*2+(total-build)*(total-build)/20; } else { - total=improvement_value(pcity->currently_building); + total = improvement_value_adjusted(pcity->currently_building, pcity); if (build>=total) return 0; cost=(total-build)*2; @@ -340,6 +339,21 @@ } /************************************************************************** + Is the given improvement a space ship component? +**************************************************************************/ +static bool is_space_part(Impr_Type_id id) +{ + struct impr_effect *eff; + + for (eff = get_improvement_type(id)->effect; + eff && eff->type != EFT_LAST; eff++) { + if (eff->type == EFT_SPACE_PART) + return TRUE; + } + return FALSE; +} + +/************************************************************************** Will this city ever be able to build this improvement? Doesn't check for building prereqs **************************************************************************/ @@ -349,6 +363,10 @@ if (!could_player_eventually_build_improvement(city_owner(pcity),id)) return FALSE; + if (!(pcity->buildflags & CITY_ENABLE_SPACE) + && is_space_part(id)) + return FALSE; + if (city_got_building(pcity, id)) return FALSE; @@ -413,6 +431,11 @@ return FALSE; } + if (unit_type_flag(id, F_NUCLEAR) + && !(pcity->buildflags & CITY_ENABLE_NUKE)) { + return FALSE; + } + /* You can't build naval units inland. */ if (!is_ocean_near_tile(pcity->x, pcity->y) && is_water_unit(id)) { return FALSE; @@ -483,9 +506,7 @@ return 0; if (is_wonder(i)) return 0; - if (improvement_types[i].upkeep == 1 && - city_affected_by_wonder(pcity, B_ASMITHS)) - return 0; + if (improvement_types[i].upkeep <= pcity->upkeep_free) return 0; if (government_has_flag(get_gov_pcity(pcity), G_CONVERT_TITHES_TO_MONEY) && (i == B_TEMPLE || i == B_COLOSSEUM || i == B_CATHEDRAL)) { return 0; @@ -495,7 +516,8 @@ } /************************************************************************** - Caller to pass asmiths = city_affected_by_wonder(pcity, B_ASMITHS) + Caller to pass asmiths = TRUE if we should honour the upkeep_free field + in pcity (set by improvements such as A.Smith's) **************************************************************************/ static int improvement_upkeep_asmiths(struct city *pcity, Impr_Type_id i, bool asmiths) @@ -504,7 +526,7 @@ return 0; if (is_wonder(i)) return 0; - if (asmiths && improvement_types[i].upkeep == 1) + if (asmiths && improvement_types[i].upkeep <= pcity->upkeep_free) return 0; if (government_has_flag(get_gov_pcity(pcity), G_CONVERT_TITHES_TO_MONEY) && (i == B_TEMPLE || i == B_COLOSSEUM || i == B_CATHEDRAL)) { @@ -580,11 +602,14 @@ } if (contains_special(spec_t, S_RAILROAD)) s+=(s*terrain_control.rail_shield_bonus)/100; - if (city_affected_by_wonder(pcity, B_RICHARDS)) - s++; - if (is_ocean(tile_t) && city_got_building(pcity, B_OFFSHORE)) { - s++; + + /* Add effects from generalised improvements */ + s += pcity->tile_bonuses[x][y].shields; + if (s > 0) { + s += pcity->tile_add[x][y].shields; } + s = s * pcity->tile_pct[x][y].shields / 100; + /* government shield bonus & penalty */ if (s > 0) s += (is_celebrating ? g->celeb_shield_bonus : g->shield_bonus); @@ -674,6 +699,15 @@ if (contains_special(spec_t, S_ROAD)) { t += (get_tile_type(tile_t))->road_trade_incr; } + + /* Add effects from generalised improvements */ + t += pcity->tile_bonuses[x][y].trade; + if (t > 0) { + t += pcity->tile_add[x][y].trade; + } + if (t < 0) t = 0; /* Don't want tiles producing negative trade */ + t = t * pcity->tile_pct[x][y].trade / 100; + if (t > 0) { int before_penalty = (is_celebrating ? g->celeb_trade_before_penalty : g->trade_before_penalty); @@ -686,11 +720,6 @@ if (t > 0) t += (is_celebrating ? g->celeb_trade_bonus : g->trade_bonus); - if(city_affected_by_wonder(pcity, B_COLLOSSUS)) - t++; - if(contains_special(spec_t, S_ROAD) && city_got_building(pcity, B_SUPERHIGHWAYS)) - t+=(t*terrain_control.road_superhighway_trade_bonus)/100; - /* government trade penalty -- SKi */ if (before_penalty > 0 && t > before_penalty) t--; @@ -785,17 +814,14 @@ if (contains_special(spec_t, S_IRRIGATION) || city_auto_water) { f += type->irrigation_food_incr; - if ((contains_special(spec_t, S_FARMLAND) || - (city_auto_water && - player_knows_techs_with_flag(city_owner(pcity), TF_FARMLAND))) && - city_got_building(pcity, B_SUPERMARKET)) { - f += (f * terrain_control.farmland_supermarket_food_bonus) / 100; - } } - if (is_ocean(tile_t) && city_got_building(pcity, B_HARBOUR)) { - f++; + /* Add effects from generalised improvements */ + f += pcity->tile_bonuses[x][y].food; + if (f > 0) { + f += pcity->tile_add[x][y].food; } + f = f * pcity->tile_pct[x][y].food / 100; if (contains_special(spec_t, S_RAILROAD)) f+=(f*terrain_control.rail_food_bonus)/100; @@ -1004,11 +1030,10 @@ *************************************************************************/ int city_gold_surplus(struct city *pcity) { - bool asmiths = city_affected_by_wonder(pcity, B_ASMITHS); int cost = 0; built_impr_iterate(pcity, i) { - cost += improvement_upkeep_asmiths(pcity, i, asmiths); + cost += improvement_upkeep_asmiths(pcity, i, TRUE); } built_impr_iterate_end; unit_list_iterate(pcity->units_supported, punit) { @@ -1355,8 +1380,13 @@ int city_shield_surplus = pcity->shield_surplus; int city_shield_stock = include_shield_stock ? city_change_production_penalty(pcity, id, id_is_unit, FALSE) : 0; - int improvement_cost = id_is_unit ? - get_unit_type(id)->build_cost : get_improvement_type(id)->build_cost; + int improvement_cost; + + if (id_is_unit) { + improvement_cost = unit_value_adjusted(id, pcity); + } else { + improvement_cost = improvement_value_adjusted(id, pcity); + } if (include_shield_stock && (city_shield_stock >= improvement_cost)) { return 1; @@ -1520,23 +1550,7 @@ **************************************************************************/ int get_city_shield_bonus(struct city *pcity) { - int shield_bonus = 100; - - if (city_got_building(pcity, B_FACTORY)) { - shield_bonus += 50; - if (city_got_building(pcity, B_MFG)) { - shield_bonus += 50; - } - - if (city_affected_by_wonder(pcity, B_HOOVER) || - city_got_building(pcity, B_POWER) || - city_got_building(pcity, B_HYDRO) || - city_got_building(pcity, B_NUCLEAR)) { - shield_bonus = 100 + (3 * (shield_bonus - 100)) / 2; - } - } - - return shield_bonus; + return pcity->shield_bonus; } /************************************************************************** @@ -1545,19 +1559,7 @@ **************************************************************************/ int get_city_tax_bonus(struct city *pcity) { - int tax_bonus = 100; - - if (city_got_building(pcity, B_MARKETPLACE)) { - tax_bonus += 50; - if (city_got_building(pcity, B_BANK)) { - tax_bonus += 50; - if (city_got_building(pcity, B_STOCK)) { - tax_bonus += 50; - } - } - } - - return tax_bonus; + return pcity->tax_bonus; } /************************************************************************** @@ -1565,25 +1567,7 @@ **************************************************************************/ static int get_city_tithes_bonus(struct city *pcity) { - int tithes_bonus = 0; - - if (!government_has_flag - (get_gov_pcity(pcity), G_CONVERT_TITHES_TO_MONEY)) { - return 0; - } - - if (city_got_building(pcity, B_TEMPLE)) - tithes_bonus += get_temple_power(pcity); - if (city_got_building(pcity, B_COLOSSEUM)) - tithes_bonus += get_colosseum_power(pcity); - if (city_got_effect(pcity, B_CATHEDRAL)) - tithes_bonus += get_cathedral_power(pcity); - if (city_affected_by_wonder(pcity, B_BACH)) - tithes_bonus += 2; - if (city_affected_by_wonder(pcity, B_CURE)) - tithes_bonus += 1; - - return tithes_bonus; + return pcity->tithes_bonus; } /************************************************************************** @@ -1591,28 +1575,7 @@ **************************************************************************/ int get_city_science_bonus(struct city *pcity) { - int science_bonus = 100; - - if (city_got_building(pcity, B_LIBRARY)) { - science_bonus += 50; - if (city_got_building(pcity, B_UNIVERSITY)) { - science_bonus += 50; - } - if (city_got_effect(pcity, B_RESEARCH)) { - science_bonus += 50; - } - } - if (city_affected_by_wonder(pcity, B_COPERNICUS)) { - science_bonus += 50; - } - if (city_affected_by_wonder(pcity, B_ISAAC)) { - science_bonus += 100; - } - if (government_has_flag(get_gov_pcity(pcity), G_REDUCED_RESEARCH)) { - science_bonus /= 2; - } - - return science_bonus; + return pcity->science_bonus; } /************************************************************************** @@ -1740,11 +1703,6 @@ **************************************************************************/ static void add_buildings_effect(struct city *pcity) { - /* this is the place to set them */ - pcity->tax_bonus = get_city_tax_bonus(pcity); - pcity->science_bonus = get_city_science_bonus(pcity); - pcity->shield_bonus = get_city_shield_bonus(pcity); - pcity->shield_prod = (pcity->shield_prod * pcity->shield_bonus) / 100; pcity->luxury_total = (pcity->luxury_total * pcity->tax_bonus) / 100; pcity->tax_total = (pcity->tax_total * pcity->tax_bonus) / 100; @@ -1753,6 +1711,386 @@ } /************************************************************************** + Sets the given city's shield/food/trade per-tile bonuses to defaults +**************************************************************************/ +void city_init_tile_mods(struct city *pcity) +{ + struct tile_mod *tilept; + + city_map_iterate(x, y) { + tilept = &pcity->tile_bonuses[x][y]; + tilept->trade = tilept->shields = tilept->food = 0; + + tilept = &pcity->tile_add[x][y]; + tilept->trade = tilept->shields = tilept->food = 0; + + tilept = &pcity->tile_pct[x][y]; + tilept->trade = tilept->shields = tilept->food = 100; + } city_map_iterate_end; +} + +static int handle_tile_bonuses( + struct impr_effect *imeff, + struct tile_mod add[CITY_MAP_SIZE][CITY_MAP_SIZE], + struct tile_mod bonus[CITY_MAP_SIZE][CITY_MAP_SIZE], + struct tile_mod pct[CITY_MAP_SIZE][CITY_MAP_SIZE], + enum tile_special_type tile_spec[CITY_MAP_SIZE][CITY_MAP_SIZE], + enum tile_terrain_type tile_terr[CITY_MAP_SIZE][CITY_MAP_SIZE]) +{ + int amount; + + amount = imeff->amount; + + switch(imeff->type) { + case EFT_TRADE_ADD_TILE: + case EFT_TRADE_INC_TILE: + case EFT_TRADE_PER_TILE: + case EFT_PROD_ADD_TILE: + case EFT_PROD_INC_TILE: + case EFT_PROD_PER_TILE: + case EFT_FOOD_ADD_TILE: + case EFT_FOOD_INC_TILE: + case EFT_FOOD_PER_TILE: + city_map_iterate(x, y) { + if (is_unit_terrain_affected(imeff, NULL, tile_terr[x][y], + tile_spec[x][y])) { + switch(imeff->type) { + case EFT_TRADE_ADD_TILE: + add[x][y].trade += amount; break; + case EFT_TRADE_INC_TILE: + bonus[x][y].trade += amount; break; + case EFT_TRADE_PER_TILE: + pct[x][y].trade = pct[x][y].trade * amount / 100; break; + case EFT_PROD_ADD_TILE: + add[x][y].shields += amount; break; + case EFT_PROD_INC_TILE: + bonus[x][y].shields += amount; break; + case EFT_PROD_PER_TILE: + pct[x][y].shields = pct[x][y].shields * amount / 100; break; + case EFT_FOOD_ADD_TILE: + add[x][y].food += amount; break; + case EFT_FOOD_INC_TILE: + bonus[x][y].food += amount; break; + case EFT_FOOD_PER_TILE: + pct[x][y].food = pct[x][y].food * amount / 100; break; + default: + break; + } + } + } city_map_iterate_end; + return TRUE; + default: + return FALSE; + } +} + +/************************************************************************** + Sets the maximum size that the city can grow (-1 = no limit) and, if + applicable, the improvement needed to increase this limit +**************************************************************************/ +static void set_city_size_limit(struct city *pcity, int size_unlimit) +{ + int impr, unlimit_impr = B_LAST, size_limit = -1; + struct impr_effect *imeff; + + /* May be better to run this loop after reading rulesets, and store the + * unlimit effects, rather than running this every time - check profiles */ + for (impr = 0; impr < game.num_impr_types; impr++) { + for (imeff = improvement_types[impr].effect; + imeff && imeff->type != EFT_LAST; imeff++) { + if (imeff->type == EFT_SIZE_UNLIMIT && imeff->amount > size_unlimit + && (size_limit == -1 || size_limit >= imeff->amount)) { + size_limit = imeff->amount; + unlimit_impr = impr; + } + } + } + + pcity->size_limit = size_limit; + pcity->unlimit_impr = unlimit_impr; +} + +static void update_bonus(int bonus[4], int amount, struct eff_iter *iter, + bool add_bonus) +{ + int bonusind = 0; + + if (iter->imeff->outside) { + switch(iter->range) { + case EFR_ISLAND: + bonusind = 1; + break; + case EFR_PLAYER: + bonusind = 2; + break; + case EFR_WORLD: + bonusind = 3; + break; + default: + break; + } + } + if (add_bonus) { + bonus[bonusind] += amount; + } else { + bonus[bonusind] = bonus[bonusind] * amount / 100; + } +} + +/************************************************************************** + Updates the various modifiers and bonuses due to city improvements + (should be called whenever city effects change - i.e. by + update_all_effects and friends) +**************************************************************************/ +void update_city_bonuses(struct city *pcity) +{ + int tax_bonus[4] = { 100, 100, 100, 100 }; + int science_bonus[4] = { 100, 100, 100, 100 }; + int shield_bonus = 100, luxury_bonus = 100; /* City bonuses */ + int tax_pct[4] = { 100, 100, 100, 100 }; + int science_pct[4] = { 100, 100, 100, 100 }; + int shield_pct = 100, luxury_pct = 100; /* City bonus multipliers */ + int poppoladj = 100, prodpoladj = 100, /* Pollution adjustments */ + poladj = 100; + int poppolpct = 100, prodpolpct = 100, /* Pollution multipliers */ + polpct = 100; + int makecontent = 0, makehappy = 0, /* Happiness modifiers */ + forcecontent = 0, forcehappy = 0; + int defensepct[UCL_LAST + 1]; /* Defense multipliers */ + int attackpct[UCL_LAST + 1]; /* Attack multipliers */ + int defensefppct[UCL_LAST + 1]; /* Firepower multipliers */ + int attackfppct[UCL_LAST + 1]; + int impr_cost_adj; /* Cost modifiers */ + int unit_cost_adj[UCL_LAST + 1]; + int impr_cost_pct; /* Cost multipliers */ + int unit_cost_pct[UCL_LAST + 1]; + int makecontentpct = 100, forcecontentpct = 100; + int makecontentmil = 0, makecontentmilper = 0; + int corruptadj = 0, corruptpct = 100; + int size_unlimit = -1, upkeep_free = 0; + enum city_flags buildflags = 0; + int growth_food = 0, nuke_proof = 0; + + enum tile_special_type spec = map_get_special(pcity->x, pcity->y); + enum tile_terrain_type terr = map_get_terrain(pcity->x, pcity->y); + enum tile_special_type tile_spec[CITY_MAP_SIZE][CITY_MAP_SIZE]; + enum tile_terrain_type tile_terr[CITY_MAP_SIZE][CITY_MAP_SIZE]; + + int amount, i; + struct player *pplayer; + struct government *gov; + + pplayer = city_owner(pcity); + gov = get_gov_pcity(pcity); + + impr_cost_pct = impr_cost_adj = 100; + for (i = 0; i <= UCL_LAST; i++) { + defensepct[i] = attackpct[i] = defensefppct[i] = attackfppct[i] = 100; + unit_cost_pct[i] = unit_cost_adj[i] = 100; + } + + city_init_tile_mods(pcity); + pcity->airlift = FALSE; + + city_map_iterate(x, y) { + struct tile_type *type; + int map_x, map_y, is_real, city_auto_water; + + is_real = city_map_to_map(&map_x, &map_y, pcity, x, y); + if (is_real) { + tile_spec[x][y] = map_get_special(map_x, map_y); + tile_terr[x][y] = map_get_terrain(map_x, map_y); + } else { + tile_spec[x][y] = S_ALL; + tile_terr[x][y] = T_UNKNOWN; + } + + type = get_tile_type(tile_terr[x][y]); + city_auto_water = (is_city_center(x, y) + && tile_terr[x][y] == type->irrigation_result + && terrain_control.may_irrigate); + /* City centre counts as farmland if we have the necessary tech */ + if (city_auto_water + && player_knows_techs_with_flag(city_owner(pcity), TF_FARMLAND)) { + tile_spec[x][y] |= S_FARMLAND; + } + } city_map_iterate_end; + + /* Ignore terrain in the iteration, as it's the terrain of the city + center - we need to consider all city tiles too */ + city_effects_iterate_full(iter, pcity, NULL, T_UNKNOWN, S_ALL) { + if (!handle_tile_bonuses(iter.imeff, pcity->tile_add, + pcity->tile_bonuses, pcity->tile_pct, + tile_spec, tile_terr) + && is_unit_terrain_affected(iter.imeff, NULL, terr, spec)) { + amount = iter.imeff->amount; + switch(iter.imeff->type) { + case EFT_UNIT_DEFEND: + defensepct[iter.imeff->aff_unit] = defensepct[iter.imeff->aff_unit] + * amount / 100; + break; + case EFT_UNIT_ATTACK: + attackpct[iter.imeff->aff_unit] = attackpct[iter.imeff->aff_unit] + * amount / 100; + break; + case EFT_UNIT_ATTACK_FIREPOWER: + attackfppct[iter.imeff->aff_unit] = attackfppct[iter.imeff->aff_unit] + * amount / 100; + break; + case EFT_UNIT_DEFEND_FIREPOWER: + defensefppct[iter.imeff->aff_unit] = defensefppct[iter.imeff->aff_unit] + * amount / 100; + break; + case EFT_UNIT_COST: + unit_cost_adj[iter.imeff->aff_unit] += amount; + break; + case EFT_BUILDING_COST: + impr_cost_adj += amount; + break; + case EFT_UNIT_COST_PCT: + unit_cost_pct[iter.imeff->aff_unit] = + unit_cost_pct[iter.imeff->aff_unit] * amount / 100; + break; + case EFT_BUILDING_COST_PCT: + impr_cost_pct = impr_cost_pct * amount / 100; + break; + case EFT_TAX_BONUS: + update_bonus(tax_bonus, amount, &iter, TRUE); + break; + case EFT_TAX_PCT: + update_bonus(tax_pct, amount, &iter, FALSE); + break; + case EFT_LUXURY_BONUS: + luxury_bonus += amount; break; + case EFT_LUXURY_PCT: + luxury_pct = luxury_pct * amount / 100; break; + case EFT_SCIENCE_BONUS: + update_bonus(science_bonus, amount, &iter, TRUE); + break; + case EFT_SCIENCE_PCT: + update_bonus(science_pct, amount, &iter, FALSE); + break; + case EFT_PROD_BONUS: + shield_bonus += amount; break; + case EFT_PROD_PCT: + shield_pct = shield_pct * amount / 100; break; + case EFT_POLLU_ADJ: + poladj += amount; break; + case EFT_POLLU_PCT: + polpct = polpct * amount / 100; break; + case EFT_POLLU_POP_ADJ: + poppoladj += amount; break; + case EFT_POLLU_POP_PCT: + poppolpct = poppolpct * amount / 100; break; + case EFT_POLLU_PROD_ADJ: + prodpoladj += amount; break; + case EFT_POLLU_PROD_PCT: + prodpolpct = prodpolpct * amount / 100; break; + case EFT_MAKE_CONTENT_MIL: + makecontentmil += amount; break; + case EFT_MAKE_CONTENT_MIL_PER: + makecontentmilper += amount; break; + case EFT_MAKE_CONTENT: + makecontent += amount; break; + case EFT_MAKE_CONTENT_PCT: + makecontentpct = makecontentpct * amount / 100; break; + case EFT_MAKE_HAPPY: + makehappy += amount; break; + case EFT_FORCE_CONTENT: + forcecontent += amount; break; + case EFT_FORCE_CONTENT_PCT: + forcecontentpct = forcecontentpct * amount / 100; break; + case EFT_FORCE_HAPPY: + forcehappy += amount; break; + case EFT_CORRUPT_ADJ: + corruptadj += amount; break; + case EFT_CORRUPT_PCT: + corruptpct = corruptpct * amount / 100; break; + case EFT_UPKEEP_FREE: + upkeep_free = MAX(upkeep_free,amount); break; + case EFT_SIZE_UNLIMIT: + size_unlimit = MAX(size_unlimit,amount); break; + case EFT_GROWTH_FOOD: + growth_food = MAX(growth_food,amount); break; + case EFT_ENABLE_SPACE: + buildflags |= CITY_ENABLE_SPACE; break; + case EFT_ENABLE_NUKE: + buildflags |= CITY_ENABLE_NUKE; break; + case EFT_NUKE_PROOF: + nuke_proof = MAX(nuke_proof,amount); break; + case EFT_AIRLIFT: + pcity->airlift = TRUE; break; + default: + break; + } + } + } city_effects_iterate_end; + + pcity->corruptadj = corruptadj; + pcity->corruptpct = corruptpct; + pcity->buildflags = buildflags; + set_city_size_limit(pcity, size_unlimit); + pcity->nuke_proof = nuke_proof; + pcity->growth_food = growth_food; + pcity->upkeep_free = upkeep_free; + + /* Apply the multipliers to the bonuses (such that the bonuses are all + summed first, and only then are the multipliers applied) */ + for (i = 0; i < 4; ++i) { + tax_bonus[i] = tax_bonus[i] * tax_pct[i] / 100; + science_bonus[i] = science_bonus[i] * science_pct[i] / 100; + } + shield_bonus = shield_bonus * shield_pct / 100; + luxury_bonus = luxury_bonus * luxury_pct / 100; + makecontent = makecontent * makecontentpct / 100; + forcecontent = forcecontent * forcecontentpct / 100; + poladj = poladj * polpct / 100; + poppoladj = poppoladj * poppolpct / 100; + prodpoladj = prodpoladj * prodpolpct / 100; + + /* Tithes are generated from "content-making" buildings - note that + we're choosing to ignore the makecontentpct modifiers here */ + if (government_has_flag(gov, G_CONVERT_TITHES_TO_MONEY)) { + pcity->tithes_bonus = pcity->makecontent + pcity->forcecontent; + } else { + pcity->tithes_bonus = 0; + } + + if (government_has_flag(gov, G_REDUCED_RESEARCH)) { + science_bonus[0] /= 2; + } + + pcity->tax_bonus = tax_bonus[0]; + pcity->science_bonus = science_bonus[0]; + /* FIXME: no handling of Island-range yet */ + pplayer->tax_bonus = tax_bonus[2]; + pplayer->science_bonus = science_bonus[2]; + game.tax_bonus = tax_bonus[3]; + game.science_bonus = science_bonus[3]; + + pcity->shield_bonus = shield_bonus; + pcity->shield_prod = pcity->shield_prod * shield_bonus / 100; + pcity->makecontent = makecontent; + pcity->makehappy = makehappy; + pcity->forcecontent = forcecontent; + pcity->forcehappy = forcehappy; + pcity->makecontentmil = makecontentmil; + pcity->makecontentmilper = makecontentmilper; + pcity->poladj = poladj; + pcity->poppoladj = poppoladj; + pcity->prodpoladj = prodpoladj; + + pcity->impr_cost_pct = impr_cost_pct * impr_cost_adj / 100; + for (i = 0; i <= UCL_LAST; i++) { + pcity->defensepct[i] = defensepct[i]; + pcity->attackpct[i] = attackpct[i]; + pcity->attackfppct[i] = attackfppct[i]; + pcity->defensefppct[i] = defensefppct[i]; + pcity->unit_cost_pct[i] = unit_cost_pct[i] * unit_cost_adj[i] / 100; + } +} + +/************************************************************************** ... **************************************************************************/ static void happy_copy(struct city *pcity, int i) @@ -1861,94 +2199,55 @@ } /************************************************************************** -... -**************************************************************************/ -static void citizen_happy_buildings(struct city *pcity) -{ - struct government *g = get_gov_pcity(pcity); - int faces = 0; - happy_copy(pcity, 1); - - if (city_got_building(pcity, B_TEMPLE)) { - faces += get_temple_power(pcity); + Apply the effect on our citizens' happiness of normal buildings + (wonder=FALSE) or Wonders (wonder=TRUE); the bonuses used are updated + by update_city_bonuses() +**************************************************************************/ +static void citizen_happy_buildings(struct city *pcity, int wonder) +{ + int hi; /* hi = "Happy Index" */ + int confaces; /* Number of people to make content */ + int hapfaces; /* Number of people to make happy */ + + if (wonder) { + hi = 4; + confaces = pcity->forcecontent; + hapfaces = pcity->forcehappy; + } else { + hi = 2; + confaces = pcity->makecontent; + hapfaces = pcity->makehappy; } - if (city_got_building(pcity, B_COURTHOUSE) && g->corruption_level == 0) { - faces++; + happy_copy(pcity, hi - 1); + + /* hapfaces can make content people happy */ + while (hapfaces > 0 && pcity->ppl_content[hi] > 0) { + pcity->ppl_content[hi]--; + pcity->ppl_happy[hi]++; + hapfaces--; } - if (city_got_building(pcity, B_COLOSSEUM)) - faces += get_colosseum_power(pcity); - if (city_got_effect(pcity, B_CATHEDRAL)) - faces += get_cathedral_power(pcity); - /* make people content (but not happy): + /* If any hapfaces left, have them act as confaces */ + confaces += hapfaces; + + /* confaces make people content (but not happy): get rid of angry first, then make unhappy content. */ - while (faces > 0 && pcity->ppl_angry[2] > 0) { - pcity->ppl_angry[2]--; - pcity->ppl_unhappy[2]++; - faces--; - } - while (faces > 0 && pcity->ppl_unhappy[2] > 0) { - pcity->ppl_unhappy[2]--; - pcity->ppl_content[2]++; - faces--; - } -/* no longer hijacking ppl_content[0]; seems no longer to be helpful -- Syela */ - /* TV doesn't make people happy just content... - - while (faces && pcity->ppl_content[2]) { - pcity->ppl_content[2]--; - pcity->ppl_happy[2]++; - faces--; - } - - */ -} - -/************************************************************************** -... -**************************************************************************/ -static void citizen_happy_wonders(struct city *pcity) -{ - int bonus = 0; - - happy_copy(pcity, 3); - - if (city_affected_by_wonder(pcity, B_HANGING)) { - bonus += 1; - if (city_got_building(pcity, B_HANGING)) - bonus += 2; - while (bonus > 0 && pcity->ppl_content[4] > 0) { - pcity->ppl_content[4]--; - pcity->ppl_happy[4]++; - bonus--; - /* well i'm not sure what to do with the rest, - will let it make unhappy content */ - } - } - if (city_affected_by_wonder(pcity, B_BACH)) - bonus += 2; - if (city_affected_by_wonder(pcity, B_CURE)) - bonus += 1; - /* get rid of angry first, then make unhappy content */ - while (bonus > 0 && pcity->ppl_angry[4] > 0) { - pcity->ppl_angry[4]--; - pcity->ppl_unhappy[4]++; - bonus--; - } - while (bonus > 0 && pcity->ppl_unhappy[4] > 0) { - pcity->ppl_unhappy[4]--; - pcity->ppl_content[4]++; - bonus--; - } - if (city_affected_by_wonder(pcity, B_SHAKESPEARE)) { - pcity->ppl_content[4] += pcity->ppl_unhappy[4] + pcity->ppl_angry[4]; - pcity->ppl_unhappy[4] = 0; - pcity->ppl_angry[4] = 0; - } - if (government_has_flag(get_gov_pcity(pcity), G_NO_UNHAPPY_CITIZENS)) { - pcity->ppl_content[4] += pcity->ppl_unhappy[4] + pcity->ppl_angry[4]; - pcity->ppl_unhappy[4] = 0; - pcity->ppl_angry[4] = 0; + while (confaces > 0 && pcity->ppl_angry[hi] > 0) { + pcity->ppl_angry[hi]--; + pcity->ppl_unhappy[hi]++; + confaces--; + } + while (confaces > 0 && pcity->ppl_unhappy[hi] > 0) { + pcity->ppl_unhappy[hi]--; + pcity->ppl_content[hi]++; + confaces--; + } + + if (wonder + && government_has_flag(get_gov_pcity(pcity), G_NO_UNHAPPY_CITIZENS)) { + pcity->ppl_content[hi] += pcity->ppl_unhappy[hi] + pcity->ppl_angry[hi]; + pcity->ppl_unhappy[hi] = 0; + pcity->ppl_angry[hi] = 0; } } @@ -1965,30 +2264,26 @@ } } - - /************************************************************************** -... + Sets the city's pollution, using the modifiers set by + update_city_bonuses **************************************************************************/ static void set_pollution(struct city *pcity) { + int pol, poppol, prodpol, mod; struct player *pplayer = city_owner(pcity); - pcity->pollution = pcity->shield_prod; - if (city_got_building(pcity, B_RECYCLING)) - pcity->pollution /= 3; - else if (city_got_building(pcity, B_HYDRO) || - city_affected_by_wonder(pcity, B_HOOVER) || - city_got_building(pcity, B_NUCLEAR)) - pcity->pollution /= 2; - - if (!city_got_building(pcity, B_MASS)) { - pcity->pollution += (pcity->size * - num_known_tech_with_flag - (pplayer, TF_POPULATION_POLLUTION_INC)) / 4; - } + mod = player_knows_techs_with_flag(pplayer, TF_POPULATION_POLLUTION_INC); + prodpol = pcity->shield_prod; + poppol = pcity->size * mod / 4; + + prodpol = prodpol * pcity->prodpoladj / 100; + poppol = poppol * pcity->poppoladj / 100; + + pol = prodpol + poppol; + pol = pol * pcity->poladj / 100; - pcity->pollution = MAX(0, pcity->pollution - 20); + pcity->pollution = MAX(0, pol - 20); } /************************************************************************** @@ -2036,7 +2331,7 @@ } /************************************************************************** -... + Evaluate the cost of supporting units, martial law and aggressive units **************************************************************************/ static void city_support(struct city *pcity, void (*send_unit_info) (struct player *pplayer, @@ -2044,18 +2339,14 @@ { struct government *g = get_gov_pcity(pcity); - bool have_police = city_got_effect(pcity, B_POLICE); - int variant = improvement_variant(B_WOMENS); - int free_happy = citygov_free_happy(pcity, g); int free_shield = citygov_free_shield(pcity, g); int free_food = citygov_free_food(pcity, g); int free_gold = citygov_free_gold(pcity, g); - if (variant == 0 && have_police) { - /* ?? This does the right thing for normal Republic and Democ -- dwp */ - free_happy += g->unit_happy_cost_factor; - } + /* Add total exemption from unhappiness from aggressive + * units, makecontentmil (e.g. from Police Station) */ + free_happy += pcity->makecontentmil; happy_copy(pcity, 2); @@ -2127,9 +2418,10 @@ happy_cost = 0; } } - if (happy_cost > 0 && variant == 1 && have_police) { - happy_cost--; - } + + /* Per-unit modifier (effect of, e.g., Civ2's Police Station) */ + if (happy_cost <= pcity->makecontentmilper) happy_cost=0; + else happy_cost -= pcity->makecontentmilper; /* subtract values found above from city's resources -- SKi */ if (happy_cost > 0) { @@ -2189,9 +2481,16 @@ add_buildings_effect(pcity); /* marketplace, library wonders.. */ set_pollution(pcity); citizen_happy_luxury(pcity); /* with our new found luxuries */ - citizen_happy_buildings(pcity); /* temple cathedral colosseum */ - city_support(pcity, send_unit_info); /* manage settlers, and units */ - citizen_happy_wonders(pcity); /* happy wonders & fundamentalism */ + + /* Happy buildings - e.g. temple cathedral colosseum */ + citizen_happy_buildings(pcity, FALSE); + + /* manage settlers, and units */ + city_support(pcity, send_unit_info); + + /* Happy Wonders (& Fundamentalism) */ + citizen_happy_buildings(pcity, TRUE); + unhappy_city_check(pcity); if (refresh_trade_route_cities && pcity->tile_trade != prev_tile_trade) { @@ -2238,7 +2537,7 @@ struct government *g = get_gov_pcity(pcity); struct city *capital; int dist; - int val, trade_penalty; + int val, trade_penalty, corruptadj; assert(game.notradesize < game.fulltradesize); if (pcity->size <= game.notradesize) { @@ -2268,8 +2567,10 @@ dist * g->corruption_distance_factor + g->extra_corruption_distance; val = trade * dist / g->corruption_modifier; - if (city_got_building(pcity, B_COURTHOUSE) || - city_got_building(pcity, B_PALACE)) val /= 2; + /* Add modifiers from generalised improvements */ + corruptadj = pcity->corruptadj * pcity->corruptpct / 100; + val = val * corruptadj / 100; + val *= g->corruption_level; val /= 100; val = CLIP(trade_penalty, val, trade); @@ -2373,36 +2674,107 @@ **************************************************************************/ 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); + } } /************************************************************************** Removes an improvement (and its effects) from a city, and updates the global arrays if the improvement has effects and/or an equiv_range that extend outside of the city. -**************************************************************************/ -void city_remove_improvement(struct city *pcity,Impr_Type_id impr) -{ - struct player *pplayer = city_owner(pcity); + If "is_destroyed" is TRUE, then the improvement has been destroyed; any + effects with "survives" set are to be preserved. + N.B. Must call update_all_effects() to resolve dependencies. +**************************************************************************/ +void city_remove_improvement(struct city *pcity,Impr_Type_id impr, + bool is_destroyed) +{ + struct ceff_vector *ceffs[2]; + struct geff_vector *geffs[4]; + bool survives = FALSE; + 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; + } + + /* Are there actually any destruction-surviving effects? */ + if (is_destroyed) { + struct impr_effect *imeff; + for (imeff = improvement_types[impr].effect; + imeff && imeff->type != EFT_LAST; imeff++) { + if (imeff->survives) { + survives = TRUE; + break; + } + } + } - 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; + } + } + } + + if (survives) { + /* Add the effects to the player's "destroyed effects" list */ + struct eff_city *eff; + struct player *pplayer = city_owner(pcity); + + eff = append_ceff(&pplayer->destroyed_effects); + eff->impr = impr; + eff->active = 0; + freelog(LOG_DEBUG, "Effects of %s moved to player's destroyed effects list", + get_improvement_name(impr)); + game.destroyed_owner[impr] = pplayer; + } else { + /* 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 +2882,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 +2918,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-24 17:06:38.000000000 +0100 +++ freeciv-patched/common/city.h 2003-07-24 17:09:42.000000000 +0100 @@ -52,6 +52,12 @@ CITYO_DISBAND, CITYO_NEW_EINSTEIN, CITYO_NEW_TAXMAN }; +/* Whether this city can build certain "special" units or improvements */ +enum city_flags { + CITY_ENABLE_SPACE = 1 << 0, + CITY_ENABLE_NUKE = 1 << 1 +}; + /* first four bits are for auto-attack: */ #define CITYOPT_AUTOATTACK_BITS 0xF @@ -204,6 +210,10 @@ * all units coming to kill us. */ }; +struct tile_mod { + int trade, shields, food; +}; + struct city { int id; int owner; @@ -233,6 +243,32 @@ int shield_prod, shield_surplus, shield_waste; int trade_prod, corruption, tile_trade; int shield_bonus, tax_bonus, science_bonus; /* more CPU savings! */ + int tithes_bonus; + + /* per tile production bonuses from city improvements */ + struct tile_mod tile_bonuses[CITY_MAP_SIZE][CITY_MAP_SIZE]; + struct tile_mod tile_add[CITY_MAP_SIZE][CITY_MAP_SIZE]; + struct tile_mod tile_pct[CITY_MAP_SIZE][CITY_MAP_SIZE]; + + /* combat multipliers for each type of attacker */ + int defensepct[UCL_LAST + 1]; + int attackpct[UCL_LAST + 1]; + int defensefppct[UCL_LAST + 1]; + int attackfppct[UCL_LAST + 1]; + + /* corruption modifiers from city improvements */ + int corruptadj, corruptpct; + + /* happiness modifiers from city improvements */ + int makecontent, makehappy, forcecontent, forcehappy; + int makecontentmil, makecontentmilper; + + /* pollution modifiers from city improvements */ + int poppoladj, prodpoladj, poladj; + + /* cost modifiers */ + int impr_cost_pct; + int unit_cost_pct[UCL_LAST + 1]; /* the totals */ int luxury_total, tax_total, science_total; @@ -248,6 +284,7 @@ int currently_building; Impr_Status improvements[B_LAST]; + struct ceff_vector effects; struct worklist worklist; @@ -275,6 +312,13 @@ int original; /* original owner */ int city_options; /* bitfield; positions as enum city_options */ + enum city_flags buildflags; /* whether we can build spaceship/nukes */ + int size_limit; /* max. population with current improvements */ + Impr_Type_id unlimit_impr; /* the improvement that increases size_limit */ + int nuke_proof; /* nuclear attacks fail within this range */ + int growth_food; /* pct. size of foodbox on growth/shrink */ + int upkeep_free; /* improvements with this upkeep are free */ + /* server variable. indicates if the city map is synced with the client. */ bool synced; @@ -447,9 +491,12 @@ int city_granary_size(int city_size); void city_add_improvement(struct city *pcity,Impr_Type_id impr); -void city_remove_improvement(struct city *pcity,Impr_Type_id impr); +void city_remove_improvement(struct city *pcity, Impr_Type_id impr, + bool is_destroyed); /* city update functions */ +void update_city_bonuses(struct city *pcity); +void city_init_tile_mods(struct city *pcity); void generic_city_refresh(struct city *pcity, bool refresh_trade_route_cities, void (*send_unit_info) (struct player * pplayer, diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/combat.c freeciv-patched/common/combat.c --- freeciv-cvs/common/combat.c 2003-07-24 17:06:34.000000000 +0100 +++ freeciv-patched/common/combat.c 2003-07-24 17:09:41.000000000 +0100 @@ -223,12 +223,104 @@ return accum_prob; } +struct impr_eff_unit { + int combatpct; + int firepowerpct; +}; + +static void get_impr_eff_unit(Unit_Type_id att_type, + Unit_Type_id def_type, int x, int y, + struct player *def_player, + struct player *att_player, + bool get_attack, + struct impr_eff_unit *effunit) +{ + int combatpct = 100, firepowerpct = 100; + struct eff_iter iter; + struct map_position mappos; + struct city *pcity = map_get_city(x, y); + int i; + + mappos.x = x; + mappos.y = y; + + if (get_attack) { + eff_iterator_unit_init(&iter, att_type, def_type, NULL, &mappos); + } else { + eff_iterator_unit_init(&iter, def_type, att_type, def_player, &mappos); + } + + /* Ignore effects at City-range and greater if we're in a city, + * as we already have these effects cached; if the attacking unit + * "ignores walls" then we ignore these effects completely. */ + if (unit_type_flag(att_type, F_IGWALL) || pcity) { + for (i = EFR_CITY; i < EFR_LAST; ++i) { + iter.effs[i] = NULL; + } + } + + if (get_attack) { + while (eff_iterator_next(&iter)) { + switch (iter.imeff->type) { + case EFT_UNIT_ATTACK: + combatpct = combatpct * iter.imeff->amount / 100; + break; + case EFT_UNIT_ATTACK_FIREPOWER: + firepowerpct = firepowerpct * iter.imeff->amount / 100; + break; + default: + break; + } + } + } else { + while (eff_iterator_next(&iter)) { + switch (iter.imeff->type) { + case EFT_UNIT_DEFEND: + combatpct = combatpct * iter.imeff->amount / 100; + break; + case EFT_UNIT_DEFEND_FIREPOWER: + firepowerpct = firepowerpct * iter.imeff->amount / 100; + break; + default: + break; + } + } + } + + /* Apply cached city effects */ + if (pcity && !unit_type_flag(att_type, F_IGWALL)) { + struct unit_classes aff_unit; + + if (get_attack) { + get_unittype_classes(def_type, &aff_unit); + for (i = 0; i <= UCL_LAST; ++i) { + if (i == UCL_LAST || is_unit_class_in_set(i, &aff_unit)) { + combatpct = combatpct * pcity->attackpct[i] / 100; + firepowerpct = firepowerpct * pcity->attackfppct[i] / 100; + } + } + } else { + get_unittype_classes(att_type, &aff_unit); + for (i = 0; i <= UCL_LAST; ++i) { + if (i == UCL_LAST || is_unit_class_in_set(i, &aff_unit)) { + combatpct = combatpct * pcity->defensepct[i] / 100; + firepowerpct = firepowerpct * pcity->defensefppct[i] / 100; + } + } + } + } + effunit->combatpct = combatpct; + effunit->firepowerpct = firepowerpct; +} + /************************************************************************** A unit's effective firepower depend on the situation. **************************************************************************/ void get_modified_firepower(struct unit *attacker, struct unit *defender, int *att_fp, int *def_fp) { + struct impr_eff_unit att_effunit, def_effunit; + *att_fp = unit_type(attacker)->firepower; *def_fp = unit_type(defender)->firepower; @@ -238,15 +330,29 @@ *att_fp *= 2; *def_fp = 1; } - - /* - * When attacked by fighters, helicopters have their firepower - * reduced to 1. - */ - if (is_heli_unit(defender) && unit_flag(attacker, F_FIGHTER)) { - *def_fp = 1; - } + /* Effects from generalised improvements */ + get_impr_eff_unit(attacker->type, defender->type, defender->x, defender->y, + unit_owner(defender), unit_owner(attacker), + TRUE, &att_effunit); + get_impr_eff_unit(attacker->type, defender->type, defender->x, defender->y, + unit_owner(defender), unit_owner(attacker), + FALSE, &def_effunit); + + freelog(LOG_DEBUG, + "%s attacking %s: impr-gen firepower multiplier %d", + get_unit_type(attacker->type)->name, + get_unit_type(defender->type)->name, + att_effunit.firepowerpct); + freelog(LOG_DEBUG, + "%s defending against %s: impr-gen firepower multiplier %d", + get_unit_type(defender->type)->name, + get_unit_type(attacker->type)->name, + def_effunit.firepowerpct); + + *def_fp = *def_fp * def_effunit.firepowerpct / 100; + *att_fp = *att_fp * att_effunit.firepowerpct / 100; + /* In land bombardment both units have their firepower reduced to 1 */ if (is_sailing_unit(attacker) && !is_ocean(map_get_terrain(defender->x, defender->y)) @@ -295,20 +401,6 @@ } /************************************************************************** - a wrapper function that returns whether or not the unit is on a citysquare - with citywalls -**************************************************************************/ -bool unit_behind_walls(struct unit *punit) -{ - struct city *pcity; - - if((pcity=map_get_city(punit->x, punit->y))) - return city_got_citywalls(pcity); - - return FALSE; -} - -/************************************************************************** a wrapper function returns 1 if the unit is on a square with fortress **************************************************************************/ bool unit_on_fortress(struct unit *punit) @@ -317,48 +409,20 @@ } /************************************************************************** - a wrapper function returns 1 if the unit is on a square with coastal defense -**************************************************************************/ -bool unit_behind_coastal(struct unit *punit) -{ - struct city *pcity; - if((pcity=map_get_city(punit->x, punit->y))) - return city_got_building(pcity, B_COASTAL); - return FALSE; -} - -/************************************************************************** - a wrapper function returns 1 if the unit is on a square with sam site -**************************************************************************/ -bool unit_behind_sam(struct unit *punit) -{ - struct city *pcity; - if((pcity=map_get_city(punit->x, punit->y))) - return city_got_building(pcity, B_SAM); - return FALSE; -} - -/************************************************************************** - a wrapper function returns 1 if the unit is on a square with sdi defense -**************************************************************************/ -bool unit_behind_sdi(struct unit *punit) -{ - struct city *pcity; - if((pcity=map_get_city(punit->x, punit->y))) - return city_got_building(pcity, B_SDI); - return FALSE; -} - -/************************************************************************** a wrapper function returns 1 if there is a sdi-defense close to the square **************************************************************************/ struct city *sdi_defense_close(struct player *owner, int x, int y) { - square_iterate(x, y, 2, x1, y1) { - struct city *pcity = map_get_city(x1, y1); - if (pcity && (!pplayers_allied(city_owner(pcity), owner)) - && city_got_building(pcity, B_SDI)) return pcity; - } square_iterate_end; + players_iterate(pplayer) { + if (pplayer != owner) { + city_list_iterate(pplayer->cities, pcity) { + if (pcity->nuke_proof > 0 + && map_distance(pcity->x, pcity->y, x, y) <= pcity->nuke_proof) { + return pcity; + } + } city_list_iterate_end; + } + } players_iterate_end; return NULL; } @@ -422,13 +486,25 @@ } /*************************************************************************** - return the modified attack power of a unit. Currently they aren't any - modifications... + return the modified attack power of a unit. ***************************************************************************/ int get_total_attack_power(struct unit *attacker, struct unit *defender) { + struct impr_eff_unit effunit; int attackpower = get_attack_power(attacker); + /* Effects from generalised improvements */ + get_impr_eff_unit(attacker->type, defender->type, attacker->x, attacker->y, + unit_owner(defender), unit_owner(attacker), + TRUE, &effunit); + + freelog(LOG_DEBUG, + "%s attacking %s: impr-gen multiplier %d", + get_unit_type(attacker->type)->name, + get_unit_type(defender->type)->name, + effunit.combatpct); + attackpower = attackpower * effunit.combatpct / 100; + return attackpower; } @@ -444,45 +520,24 @@ **************************************************************************/ static int defence_multiplication(Unit_Type_id att_type, Unit_Type_id def_type, int x, int y, - int defensepower, bool fortified) + int defensepower, bool fortified, + struct player *att_player, + struct player *def_player) { struct city *pcity = map_get_city(x, y); if (unit_type_exists(att_type)) { - if (unit_type_flag(def_type, F_PIKEMEN) - && unit_type_flag(att_type, F_HORSE)) { - defensepower *= 2; - } + /* Effects from generalised improvements */ + struct impr_eff_unit effunit; - if (unit_type_flag(def_type, F_AEGIS) && - (is_air_unittype(att_type) || is_heli_unittype(att_type))) { - defensepower *= 5; - } - - if (is_air_unittype(att_type) && pcity) { - if (city_got_building(pcity, B_SAM)) { - defensepower *= 2; - } - if (city_got_building(pcity, B_SDI) - && unit_type_flag(att_type, F_MISSILE)) { - defensepower *= 2; - } - } else if (is_water_unit(att_type) && pcity) { - if (city_got_building(pcity, B_COASTAL)) { - defensepower *= 2; - } - } - if (!unit_type_flag(att_type, F_IGWALL) - && (is_ground_unittype(att_type) || is_heli_unittype(att_type) - || (improvement_variant(B_CITY) == 1 - && is_water_unit(att_type))) && pcity - && city_got_citywalls(pcity)) { - defensepower *= 3; - } + get_impr_eff_unit(att_type, def_type, x, y, def_player, att_player, + FALSE, &effunit); - if (unit_type_flag(att_type, F_FIGHTER) && is_heli_unittype(def_type)) { - defensepower /= 2; - } + freelog(LOG_DEBUG, + "%s defending against %s: impr-gen multiplier %d", + get_unit_type(def_type)->name, + get_unit_type(att_type)->name, effunit.combatpct); + defensepower = defensepower * effunit.combatpct / 100; } if (map_has_special(x, y, S_FORTRESS) && !pcity) { @@ -523,8 +578,10 @@ defensepower = (defensepower * 3) / 2; } + /* FIXME: we don't know the player, so can't consider Player-range + * Unit_Defend improvement effects... */ return defence_multiplication(att_type, def_type, x, y, defensepower, - fortified); + fortified, NULL, NULL); } /*************************************************************************** @@ -537,7 +594,8 @@ return defence_multiplication(attacker->type, defender->type, defender->x, defender->y, get_defense_power(defender), - defender->activity == ACTIVITY_FORTIFIED); + defender->activity == ACTIVITY_FORTIFIED, + unit_owner(attacker), unit_owner(defender)); } /************************************************************************** diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/combat.h freeciv-patched/common/combat.h --- freeciv-cvs/common/combat.h 2003-07-24 17:06:34.000000000 +0100 +++ freeciv-patched/common/combat.h 2003-07-24 17:09:40.000000000 +0100 @@ -38,11 +38,7 @@ double unit_win_chance(struct unit *attacker, struct unit *defender); bool unit_really_ignores_citywalls(struct unit *punit); -bool unit_behind_walls(struct unit *punit); bool unit_on_fortress(struct unit *punit); -bool unit_behind_coastal(struct unit *punit); -bool unit_behind_sam(struct unit *punit); -bool unit_behind_sdi(struct unit *punit); struct city *sdi_defense_close(struct player *owner, int x, int y); int get_attack_power(struct unit *punit); diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/game.c freeciv-patched/common/game.c --- freeciv-cvs/common/game.c 2003-07-24 17:06:38.000000000 +0100 +++ freeciv-patched/common/game.c 2003-07-24 17:09:42.000000000 +0100 @@ -169,10 +169,21 @@ **************************************************************************/ void game_remove_city(struct city *pcity) { + int i; + + /* Update list of destroyed wonders properly */ + for (i = 0; i < B_LAST; i++) { + if (game.global_wonders[i] == pcity->id) { + game.global_wonders[i] = -1; + } + } + freelog(LOG_DEBUG, "game_remove_city %d", pcity->id); 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 +261,12 @@ game.nbarbarians = 0; game.occupychance= GAME_DEFAULT_OCCUPYCHANCE; + geff_vector_init(&game.effects); + ceff_vector_init(&game.destroyed_effects); + for (i = 0; i < B_LAST; i++) { + game.destroyed_owner[i] = NULL; + } + game.heating = 0; game.cooling = 0; sz_strlcpy(game.save_name, GAME_DEFAULT_SAVE_NAME); @@ -266,6 +283,7 @@ sz_strlcpy(game.rulesetdir, GAME_DEFAULT_RULESETDIR); + game.num_unit_flags = F_LAST; game.num_unit_types = 0; game.num_impr_types = 0; game.num_tech_types = 0; @@ -325,6 +343,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 +453,8 @@ pplayer->attribute_block.data = NULL; } + geff_vector_free(&pplayer->effects); + ceff_vector_free(&pplayer->destroyed_effects); if (pplayer->island_improv) { free(pplayer->island_improv); pplayer->island_improv = NULL; @@ -574,3 +596,142 @@ #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())); + deactivate_effects(&game.destroyed_effects); + + 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; + deactivate_effects(&pplayer->destroyed_effects); + } 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; + + /* Update all effects of this player's destroyed Wonders */ + update_city_effects(&pplayer->destroyed_effects, NULL, pplayer, + &cond_eff, &changed); + } players_iterate_end; + + /* Update all effects of destroyed Wonders that belonged to + * now-dead players */ + update_city_effects(&game.destroyed_effects, NULL, NULL, + &cond_eff, &changed); + } while (changed && cond_eff); + + players_iterate(pplayer) { + city_list_iterate(pplayer->cities, pcity) { + update_city_bonuses(pcity); + } city_list_iterate_end; + } players_iterate_end; +} diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/game.h freeciv-patched/common/game.h --- freeciv-cvs/common/game.h 2003-07-24 17:06:38.000000000 +0100 +++ freeciv-patched/common/game.h 2003-07-24 17:09:42.000000000 +0100 @@ -106,9 +106,14 @@ struct conn_list game_connections; /* involved in game; send map etc */ int global_advances[A_LAST]; /* a counter */ int global_wonders[B_LAST]; /* contains city id's */ - /* global_wonders[] may also be (-1), or the id of a city - which no longer exists, if the wonder has been destroyed */ + /* global_wonders[] may also be (-1) if the wonder has been destroyed */ + struct player *destroyed_owner[B_LAST]; /* the player that owns each + * destroyed Wonder */ 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 and + * player destruction */ int heating; /* Number of polluted squares. */ int globalwarming; /* Total damage done. (counts towards a warming event.) */ @@ -143,6 +148,7 @@ bool fogofwar_old; /* as the fog_of_war bit get changed by setting the server we need to remember the old setting */ + int num_unit_flags; int num_unit_types; int num_impr_types; int num_tech_types; /* including A_NONE */ @@ -162,6 +168,9 @@ int borders; /* distance of border from city; 0=disabled. */ + /* Bonuses that affect all cities */ + int science_bonus, tax_bonus; + char rulesetdir[MAX_LEN_NAME]; int firepower_factor; /* See README.rulesets */ struct { @@ -254,6 +263,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/government.c freeciv-patched/common/government.c --- freeciv-cvs/common/government.c 2003-07-24 17:06:34.000000000 +0100 +++ freeciv-patched/common/government.c 2003-07-24 17:09:42.000000000 +0100 @@ -63,7 +63,7 @@ struct ai_gov_tech_hint ai_gov_tech_hints[MAX_NUM_TECH_LIST]; static const char *flag_names[] = { - "Build_Veteran_Diplomats", "Revolution_When_Unhappy", "Has_Senate", + "(unused)", "Revolution_When_Unhappy", "Has_Senate", "Unbribable", "Inspires_Partisans", "Rapture_City_Growth", "Fanatic_Troops", "No_Unhappy_Citizens", "Convert_Tithes_To_Money", "Reduced_Research" @@ -252,10 +252,15 @@ * ever get it, then we can't change to the gov type even if we have * a wonder that would otherwise allow it. */ return FALSE; + } else if (get_invention(pplayer, req) == TECH_KNOWN) { + return TRUE; } else { - return (get_invention(pplayer, req) == TECH_KNOWN - || player_owns_active_govchange_wonder(pplayer)); + /* Do we have the Any_Government effect? */ + impr_effects_iterate(iter, B_LAST, NULL, pplayer) { + if (iter.imeff->type == EFT_ANY_GOVERNMENT) return TRUE; + } impr_effects_iterate_end; } + return FALSE; } /*************************************************************** @@ -297,6 +302,9 @@ ***************************************************************/ static void government_free(struct government *gov) { + free(gov->effect); + gov->effect = NULL; + free(gov->ruler_titles); gov->ruler_titles = NULL; diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/government.h freeciv-patched/common/government.h --- freeciv-cvs/common/government.h 2003-07-24 17:06:31.000000000 +0100 +++ freeciv-patched/common/government.h 2003-07-24 17:09:41.000000000 +0100 @@ -18,6 +18,7 @@ struct city; struct player; struct Sprite; /* opaque; client-gui specific */ +struct impr_effect; #define G_MAGIC (127) /* magic constant, used as flag value */ @@ -25,7 +26,8 @@ #define G_CITY_SIZE_FREE G_MAGIC enum government_flag_id { - G_BUILD_VETERAN_DIPLOMAT=0, /* and Spies (in general: all F_DIPLOMAT) */ + G_BUILD_VETERAN_DIPLOMAT=0, /* obsolete - use Unit_Veteran effect in + * units.ruleset instead */ G_REVOLUTION_WHEN_UNHAPPY, G_HAS_SENATE, /* not implemented */ G_UNBRIBABLE, @@ -152,6 +154,8 @@ int hints; struct Sprite *sprite; + + struct impr_effect *effect; /* list; .type==EFT_LAST terminated */ char *helptext; }; diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/improvement.c freeciv-patched/common/improvement.c --- freeciv-cvs/common/improvement.c 2003-07-24 17:06:38.000000000 +0100 +++ freeciv-patched/common/improvement.c 2003-07-24 17:09:42.000000000 +0100 @@ -19,6 +19,7 @@ #include #include "game.h" +#include "government.h" #include "log.h" #include "map.h" #include "mem.h" @@ -28,6 +29,18 @@ #include "improvement.h" +int B_CITY, B_PALACE; + +/* 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! */ @@ -93,21 +106,21 @@ "No_Sink_Deep", "Nuke_Proof", "Pollu_Adj", - "Pollu_Adj_Pop", - "Pollu_Adj_Prod", - "Pollu_Set", - "Pollu_Set_Pop", - "Pollu_Set_Prod", + "Pollu_Pop_Adj", + "Pollu_Prod_Adj", + "Pollu_Pct", + "Pollu_Pop_Pct", + "Pollu_Prod_Pct", "Prod_Add_Tile", "Prod_Bonus", "Prod_Inc_Tile", "Prod_Per_Tile", "Prod_To_Gold", - "Reduce_Corrupt", - "Reduce_Waste", + "Corrupt_Adj", + "Corrupt_Pct", "Reveal_Cities", "Reveal_Map", - "Revolt_Dist", + "Revolt_Dist_Adj", "Science_Bonus", "Science_Pct", "Size_Unlimit", @@ -134,7 +147,22 @@ "Upgrade_One_Leap", "Upgrade_All_Step", "Upgrade_All_Leap", - "Upkeep_Free" + "Upkeep_Free", + "Food_Pct", + "Prod_Pct", + "Revolt_Dist_Pct", + "Trade_Pct", + "Make_Content_Mil_Per", + "Force_Content", + "Force_Content_Pct", + "Force_Happy", + "Unit_Attack", + "Unit_Attack_Firepower", + "Unit_Defend_Firepower", + "Unit_Cost", + "Building_Cost", + "Unit_Cost_Pct", + "Building_Cost_Pct" }; /************************************************************************** @@ -318,6 +346,15 @@ } /************************************************************************** + Returns the effective value of the improvement, after applying any + cost modifiers. +**************************************************************************/ +int improvement_value_adjusted(Impr_Type_id id, struct city *pcity) +{ + return improvement_value(id) * pcity->impr_cost_pct / 100; +} + +/************************************************************************** ... **************************************************************************/ bool is_wonder(Impr_Type_id id) @@ -493,9 +530,6 @@ /* This if for a spaceship component is asked */ while ((type = peffect->type) != EFT_LAST) { if (type == EFT_SPACE_PART) { - /* TODO: remove this */ - if (game.global_wonders[B_APOLLO] == 0) - return FALSE; if (p->spaceship.state >= SSHIP_LAUNCHED) return FALSE; if (peffect->amount == 1 && p->spaceship.structurals >= NUM_SS_STRUCTURALS) @@ -567,14 +601,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 +673,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 +700,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 +779,681 @@ #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 *