diff -Nur -Xfreecivdiff.ignore freeciv-cvs/ai/advdomestic.c freeciv-patched/ai/advdomestic.c --- freeciv-cvs/ai/advdomestic.c 2003-06-30 12:18:27.000000000 +0100 +++ freeciv-patched/ai/advdomestic.c 2003-06-30 12:17: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-06-30 12:18:27.000000000 +0100 +++ freeciv-patched/ai/aicity.c 2003-06-30 12:16:42.000000000 +0100 @@ -307,7 +307,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-06-30 12:18:26.000000000 +0100 +++ freeciv-patched/ai/aiunit.c 2003-06-30 12:17:55.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-06-30 12:18:24.000000000 +0100 +++ freeciv-patched/client/citydlg_common.c 2003-06-30 12:18:17.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-06-30 12:18:24.000000000 +0100 +++ freeciv-patched/client/cityrepdata.c 2003-06-30 12:18:17.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-06-30 12:18:25.000000000 +0100 +++ freeciv-patched/client/climisc.c 2003-06-30 12:18:17.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-06-30 12:18:27.000000000 +0100 +++ freeciv-patched/client/gui-gtk/citydlg.c 2003-06-30 12:18:17.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-06-30 12:18:27.000000000 +0100 +++ freeciv-patched/client/gui-gtk/gotodlg.c 2003-06-30 12:16:18.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-06-30 12:18:27.000000000 +0100 +++ freeciv-patched/client/gui-gtk/happiness.c 2003-06-30 12:16:03.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-06-30 12:18:27.000000000 +0100 +++ freeciv-patched/client/gui-gtk/repodlgs.c 2003-06-30 12:16:42.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-06-30 12:18:27.000000000 +0100 +++ freeciv-patched/client/gui-gtk-2.0/citydlg.c 2003-06-30 12:18:17.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-06-30 12:18:27.000000000 +0100 +++ freeciv-patched/client/gui-gtk-2.0/gotodlg.c 2003-06-30 12:16:19.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-06-30 12:18:27.000000000 +0100 +++ freeciv-patched/client/gui-gtk-2.0/happiness.c 2003-06-30 12:16:03.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-06-30 12:18:27.000000000 +0100 +++ freeciv-patched/client/gui-gtk-2.0/repodlgs.c 2003-06-30 12:16:42.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-06-30 12:18:27.000000000 +0100 +++ freeciv-patched/client/gui-mui/dialogs.c 2003-06-30 12:16:42.000000000 +0100 @@ -1854,7 +1854,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-06-30 12:18:27.000000000 +0100 +++ freeciv-patched/client/gui-mui/gotodlg.c 2003-06-30 12:16:19.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-06-30 12:18:27.000000000 +0100 +++ freeciv-patched/client/gui-mui/mapclass.c 2003-06-30 12:16:42.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-06-30 12:18:27.000000000 +0100 +++ freeciv-patched/client/gui-mui/repodlgs.c 2003-06-30 12:16:42.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-06-30 12:18:27.000000000 +0100 +++ freeciv-patched/client/gui-sdl/repodlgs.c 2003-06-30 12:16:42.000000000 +0100 @@ -191,7 +191,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), @@ -450,7 +450,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); /* ----------- */ @@ -841,7 +841,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-06-30 12:18:27.000000000 +0100 +++ freeciv-patched/client/gui-win32/citydlg.c 2003-06-30 12:16:42.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-06-30 12:18:27.000000000 +0100 +++ freeciv-patched/client/gui-win32/gotodlg.c 2003-06-30 12:16:19.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-06-30 12:18:27.000000000 +0100 +++ freeciv-patched/client/gui-win32/repodlgs.c 2003-06-30 12:16:42.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-06-30 12:18:27.000000000 +0100 +++ freeciv-patched/client/gui-xaw/citydlg.c 2003-06-30 12:16:42.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); } } @@ -2037,7 +2037,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-06-30 12:18:27.000000000 +0100 +++ freeciv-patched/client/gui-xaw/gotodlg.c 2003-06-30 12:16:19.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-06-30 12:18:27.000000000 +0100 +++ freeciv-patched/client/gui-xaw/repodlgs.c 2003-06-30 12:16:42.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-06-30 12:18:26.000000000 +0100 +++ freeciv-patched/client/helpdata.c 2003-06-30 12:17:33.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-06-30 12:18:28.000000000 +0100 +++ freeciv-patched/client/packhand.c 2003-06-30 12:18:03.000000000 +0100 @@ -75,6 +75,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; + /************************************************************************** ... **************************************************************************/ @@ -109,6 +119,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); if (packet->you_can_login) { @@ -244,7 +257,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; @@ -258,15 +271,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) { @@ -377,6 +408,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; @@ -403,6 +437,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; @@ -418,6 +455,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 { @@ -457,8 +500,6 @@ } handle_city_packet_common(pcity, city_is_new, popup, packet->diplomat_investigate); - - try_update_effects(need_effect_update); } /************************************************************************** @@ -593,6 +634,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, @@ -638,11 +682,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 { @@ -655,8 +705,6 @@ if (update_descriptions && tile_visible_mapcanvas(pcity->x,pcity->y)) { update_city_descriptions(); } - - try_update_effects(need_effect_update); } /************************************************************************** @@ -664,6 +712,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() @@ -702,6 +753,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 @@ -720,6 +774,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; @@ -1113,6 +1170,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; @@ -1135,29 +1229,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 */ @@ -1291,9 +1391,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; @@ -1307,6 +1412,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; @@ -1374,6 +1487,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) && @@ -1822,6 +1940,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; @@ -1885,6 +2004,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 */ @@ -2056,7 +2176,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, @@ -2162,6 +2282,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); @@ -2297,6 +2418,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-06-30 12:18:26.000000000 +0100 +++ freeciv-patched/common/aicore/pf_tools.c 2003-06-30 12:17:19.000000000 +0100 @@ -286,8 +286,7 @@ parameter->zoc_used = (unit_type(punit)->move_type == LAND_MOVING && !unit_flag(punit, F_IGZOC)); - 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 { @@ -325,8 +324,7 @@ parameter->zoc_used = FALSE; - 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-06-30 12:18:26.000000000 +0100 +++ freeciv-patched/common/capstr.c 2003-06-30 12:16:49.000000000 +0100 @@ -78,7 +78,7 @@ "city_struct_minor_cleanup obsolete_last class_legend " \ "+impr_req +waste +fastfocus +continent +small_dipl " \ "+no_nation_selected +diplomacy +no_extra_tiles " \ - "+diplomacy2 +citizens_style +root_tech" + "+diplomacy2 +citizens_style +root_tech 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-06-30 12:18:29.000000000 +0100 +++ freeciv-patched/common/city.c 2003-06-30 12:18:17.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); @@ -223,10 +222,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: @@ -287,12 +286,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; @@ -343,6 +342,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 **************************************************************************/ @@ -352,6 +366,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; @@ -416,6 +434,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; @@ -486,9 +509,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; @@ -498,7 +519,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) @@ -507,7 +529,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)) { @@ -583,11 +605,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); @@ -677,6 +702,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); @@ -689,11 +723,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--; @@ -788,17 +817,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; @@ -910,11 +936,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; return pcity->tax_total-cost; @@ -1257,8 +1282,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; @@ -1422,23 +1452,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; } /************************************************************************** @@ -1447,19 +1461,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; } /************************************************************************** @@ -1467,25 +1469,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; } /************************************************************************** @@ -1493,28 +1477,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; } /************************************************************************** @@ -1642,11 +1605,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; @@ -1655,6 +1613,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) @@ -1763,94 +2101,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; } } @@ -1867,30 +2166,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); } /************************************************************************** @@ -1938,7 +2233,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, @@ -1946,18 +2241,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); @@ -2029,9 +2320,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) { @@ -2091,9 +2383,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) { @@ -2140,7 +2439,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) { @@ -2170,8 +2469,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); @@ -2275,36 +2576,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; + } + } + } + } } /************************************************************************** @@ -2412,6 +2784,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; @@ -2445,5 +2820,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-06-30 12:18:29.000000000 +0100 +++ freeciv-patched/common/city.h 2003-06-30 12:18:17.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; @@ -432,9 +476,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-06-30 12:18:26.000000000 +0100 +++ freeciv-patched/common/combat.c 2003-06-30 12:17:55.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-06-30 12:18:26.000000000 +0100 +++ freeciv-patched/common/combat.h 2003-06-30 12:16:57.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-06-30 12:18:29.000000000 +0100 +++ freeciv-patched/common/game.c 2003-06-30 12:18:03.000000000 +0100 @@ -619,10 +619,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; @@ -699,6 +710,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); @@ -715,6 +732,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; @@ -774,6 +792,8 @@ ***************************************************************/ void game_free(void) { + geff_vector_free(&game.effects); + ceff_vector_free(&game.destroyed_effects); game_remove_all_players(); map_free(); idex_free(); @@ -882,6 +902,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; @@ -1023,3 +1045,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-06-30 12:18:29.000000000 +0100 +++ freeciv-patched/common/game.h 2003-06-30 12:18:10.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 */ @@ -160,6 +166,9 @@ int watchtower_vision; int allowed_city_names; + /* Bonuses that affect all cities */ + int science_bonus, tax_bonus; + char rulesetdir[MAX_LEN_NAME]; int firepower_factor; /* See README.rulesets */ struct { @@ -253,6 +262,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-06-30 12:18:26.000000000 +0100 +++ freeciv-patched/common/government.c 2003-06-30 12:18:03.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" @@ -251,10 +251,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; } /*************************************************************** @@ -290,6 +295,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-06-30 12:18:25.000000000 +0100 +++ freeciv-patched/common/government.h 2003-06-30 12:17:55.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-06-30 12:18:29.000000000 +0100 +++ freeciv-patched/common/improvement.c 2003-06-30 12:18:17.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 *ceffs[], + struct geff_vector *geffs[], + Impr_Type_id impr, struct city *pcity) +{ + struct impr_effect *ie; + int j, i, cont; + bool effects[EFR_LAST]; + struct player *plr; + + assert(pcity && impr >= 0 && impr < game.num_impr_types); + + for (i = 0; i < EFR_LAST; i++) { + effects[i] = FALSE; + } + + if ((ie = improvement_types[impr].effect)) { + for (; ie->type < EFT_LAST; ie++) { + effects[ie->range] = TRUE; + } + } + + plr = city_owner(pcity); + cont = map_get_continent(pcity->x, pcity->y); + + i = 0; + for (j = 0; j < EFR_LAST; j++) { + if (effects[j]) { + switch(j) { + case EFR_ISLAND: + geffs[i++] = get_eff_island(cont, plr); + break; + case EFR_PLAYER: + geffs[i++] = get_eff_player(plr); + break; + case EFR_WORLD: + geffs[i++] = get_eff_world(); + break; + default: + break; + } + } + } + geffs[i++] = NULL; + + ceffs[0] = get_eff_city(pcity); + ceffs[1] = NULL; +} + +/************************************************************************** + Updates the relevant global effect structures, to bring them into + line with the current activity status of their parent city structure, + which should be owned by the passed pcity and pplayer (either can be + NULL for destroyed effects). +**************************************************************************/ +void update_global_effect(struct city *pcity, struct player *pplayer, + struct eff_city *effect) +{ + struct geff_vector *effs[EFR_LAST]; + int i, j; + + for (i = 0; i < EFR_LAST; i++) { + effs[i] = NULL; + } + + if (pplayer) { + effs[EFR_PLAYER] = get_eff_player(pplayer); + if (pcity) { + int cont = map_get_continent(pcity->x, pcity->y); + effs[EFR_ISLAND] = get_eff_island(cont, pplayer); + } + } + effs[EFR_WORLD] = get_eff_world(); + + for (j = 0; j < EFR_LAST; j++) { + if (effs[j]) { + for (i = 0; i < geff_vector_size(effs[j]); i++) { + struct eff_global *eff = geff_vector_get(effs[j], i); + + if ((!pcity || eff->cityid == pcity->id) + && (!pplayer || eff->plr == pplayer) + && eff->eff.impr == effect->impr) { + eff->eff.active = effect->active; + break; + } + } + } + } +} + +/************************************************************************** +... +**************************************************************************/ +struct eff_city *append_ceff(struct ceff_vector *x) +{ + int i, n; + struct eff_city *eff; + + /* Try for an unused vector instance if possible. */ + n = ceff_vector_size(x); + for (i = 0; i < n; i++) { + eff = ceff_vector_get(x, i); + if (eff->impr == B_LAST) { + return eff; + } + } + + /* That didn't work, so add a new instance to the vector. */ + ceff_vector_reserve(x, n + 1); + return ceff_vector_get(x, n); +} + +/************************************************************************** +... +**************************************************************************/ +struct eff_global *append_geff(struct geff_vector *x) +{ + int i, n; + struct eff_global *eff; + + /* Try for an unused vector instance if possible. */ + n = geff_vector_size(x); + for (i = 0; i < n; i++) { + eff = geff_vector_get(x, i); + if (eff->eff.impr == B_LAST) { + return eff; + } + } + + /* That didn't work, so add a new instance to the vector. */ + geff_vector_reserve(x, n + 1); + return geff_vector_get(x, n); +} + +/************************************************************************** + Fills in the efflist pointer array with the relevant ceff_vectors for + the given improvement, in the given city, on the given island, and the + given player. B_LAST, NULL, -1 and NULL respectively cause those ranges + to be skipped (i.e. returned as NULL). +**************************************************************************/ +void get_all_effect_vectors(struct ceff_vector *effs[EFR_LAST], + Impr_Type_id impr, struct city *pcity, + int cont, struct player *plr) +{ + int i; + + for (i = 0; i < EFR_LAST; i++) { + effs[i] = NULL; + } + + if (pcity) { + effs[EFR_CITY] = get_eff_city(pcity); + if (impr != B_LAST) { + effs[EFR_LOCAL] = effs[EFR_CITY]; + } + } + + if (plr) { + effs[EFR_PLAYER] = get_geff_parent(get_eff_player(plr)); + if (cont != -1) { + effs[EFR_ISLAND] = get_geff_parent(get_eff_island(cont, plr)); + } + } + + effs[EFR_WORLD] = get_geff_parent(get_eff_world()); +} + +/************************************************************************** + Returns TRUE if the given building (if not B_LAST), city or player + are being affected by the given effect; the player should be the owner of + the city (and will be filled in if city is specified and player is NULL) +**************************************************************************/ +bool is_under_effect(Impr_Type_id impr, struct city *pcity, + struct player *pplayer, enum effect_type cond_eff) +{ + if (!pplayer && pcity) { + pplayer = city_owner(pcity); + } + + impr_effects_iterate(iter, impr, pcity, pplayer) { + if (iter.imeff->type == cond_eff) { + return TRUE; + } + } impr_effects_iterate_end; + + return FALSE; +} + +/************************************************************************** + Returns TRUE if the given effect (in the given city, owned by the given + player) is activated by all of the necessary prerequisites; + *cond_eff is set to TRUE if the effect could not be activated because it + depends on another effect; + pcity and/or pplayer can be NULL for destroyed effects. + Note: the cond_bldg field, and the various aff_xxx fields, are not + checked, as one effect may affect several cities, terrains, etc. + Use the is_city_affected and is_unit_terrain_affected functions to + determine if a so-called "active" effect really is. +**************************************************************************/ +bool is_effect_activated(Impr_Type_id impr, struct city *pcity, + struct player *pplayer, struct impr_effect *imeff, + bool *cond_eff) +{ + if (imeff->cond_gov != game.government_count && pplayer + && pplayer->government != imeff->cond_gov) { + freelog(LOG_DEBUG, "Effect requires a government"); + return FALSE; + } + if (pplayer && get_invention(pplayer, imeff->cond_adv) != TECH_KNOWN) { + freelog(LOG_DEBUG, "Effect requires a future tech"); + return FALSE; + } + if (imeff->cond_eff != EFT_LAST + && !is_under_effect(impr, pcity, pplayer, imeff->cond_eff)) { + freelog(LOG_DEBUG, "Effect requires another effect"); + if (cond_eff) { + *cond_eff = TRUE; + } + return FALSE; + } + return TRUE; +} + +/************************************************************************** + Sets an effect iterator to the start of the effect lists - common + function used by other effect iterator initialisation functions +**************************************************************************/ +static void eff_iterator_start(struct eff_iter *iter) +{ + iter->imeff = NULL; + iter->range = 0; + iter->index = 0; + iter->bitmask = 1; +} + +/************************************************************************** + Initialises an effect iterator structure (a pointer to which should be + passed in as the first argument). The iterator will return all improvement + effects which affect the designated improvement in the given city owned + by the given player (if impr is B_LAST, and/or pcity/pplayer is NULL, + the respective ranges are skipped) +**************************************************************************/ +void eff_iterator_impr_init(struct eff_iter *iter, Impr_Type_id impr, + struct city *pcity, struct player *pplayer) +{ + int cont = -1; + + assert(iter != NULL); + + if (pcity) + cont = map_get_continent(pcity->x, pcity->y); + + get_all_effect_vectors(iter->effs, impr, pcity, cont, pplayer); + + iter->utype = U_LAST; + iter->impr = impr; + iter->pcity = pcity; + iter->pplayer = pplayer; + iter->aff_unit.move_type = LAST_MOVING; + + if (pcity) { + iter->aff_terr = map_get_terrain(pcity->x, pcity->y); + iter->aff_spec = map_get_special(pcity->x, pcity->y); + } else { + iter->aff_terr = T_UNKNOWN; + iter->aff_spec = S_ALL; + } + + eff_iterator_start(iter); +} + +/************************************************************************** + Similar to the above function, this initialises an effect iterator for + considering effects that influence a unit that is owned by the given + player and is at the given map coordinates (if pplayer and/or mp are NULL, + the unit is considered unowned and/or unpositioned) +**************************************************************************/ +void eff_iterator_unit_init(struct eff_iter *iter, Unit_Type_id utype, + Unit_Type_id aff_utype, + struct player *pplayer, struct map_position *mp) +{ + int cont = -1; + struct city *pcity = NULL; + + assert(iter != NULL); + + if (mp) { + pcity = map_get_city(mp->x, mp->y); + cont = map_get_continent(mp->x, mp->y); + } + + get_all_effect_vectors(iter->effs, B_LAST, pcity, cont, pplayer); + + iter->utype = utype; + iter->impr = B_LAST; + iter->pcity = pcity; + iter->pplayer = pplayer; + get_unittype_classes(aff_utype, &iter->aff_unit); + + if (mp) { + iter->aff_terr = map_get_terrain(mp->x, mp->y); + iter->aff_spec = map_get_special(mp->x, mp->y); + } else { + iter->aff_terr = T_UNKNOWN; + iter->aff_spec = S_ALL; + } + + eff_iterator_start(iter); +} + +struct impr_effect *get_base_effect(Impr_Type_id impr, struct player *pplayer) +{ + switch(impr) { + case B_NATION: + return get_nation_by_plr(pplayer)->effect; + case B_GOV: + return get_gov_pplayer(pplayer)->effect; + default: + break; + } + return improvement_types[impr].effect; +} + +const char *get_effect_cause_name(Impr_Type_id impr, struct player *pplayer) +{ + switch(impr) { + case B_NATION: + return pplayer ? get_nation_name(pplayer->nation) : "unknown nation"; + case B_GOV: + return pplayer ? get_government_name(pplayer->government) + : "unknown government"; + default: + break; + } + return get_improvement_name(impr); +} + +/************************************************************************** + Given an initialised effect iterator, returns the next active effect. + iter->imeff also points to this effect (the data is in the ruleset, so + should NOT be freed when you're done with it). NULL is returned when no + more effects can be found. Does _not_ consider the various aff_xxx fields. + + N.B. Don't go adding/removing effects while an iteration is in progress! +**************************************************************************/ +static struct impr_effect *eff_iterator_next_internal(struct eff_iter *iter) +{ + struct ceff_vector *ceff; + struct eff_city *effect; + int efflen; + + assert(iter != NULL); + + /* Return immediately if the iterator is exhausted */ + if (!iter->imeff && iter->range != 0) + return NULL; + + /* Loop over all valid ranges */ + for (; iter->range < EFR_LAST; iter->range++) { + ceff = iter->effs[iter->range]; + /* We don't currently pre-activate unittype effects, so must check + * each effect for activation */ + if (iter->range == EFR_LOCAL && iter->utype != U_LAST) { + if (iter->imeff) { + iter->imeff++; + } else { + iter->imeff = get_unit_type(iter->utype)->effect; + } + for (; iter->imeff && iter->imeff->type != EFT_LAST; iter->imeff++) { + if (is_effect_activated(iter->impr, iter->pcity, iter->pplayer, + iter->imeff, NULL) + && is_city_affected(iter->imeff, iter->pcity)) { + return iter->imeff; + } + } + iter->imeff = NULL; + } else if (ceff) { + efflen = ceff_vector_size(ceff); + for (; iter->index < efflen; iter->index++) { + effect = ceff_vector_get(ceff, iter->index); + if (effect->impr != B_LAST) { + if (iter->imeff) { + /* Make sure we don't return the same effect again */ + iter->imeff++; iter->bitmask <<= 1; + } else { + iter->imeff = get_base_effect(effect->impr, iter->pplayer); + iter->bitmask = 1; + } + for (; iter->imeff && iter->imeff->type != EFT_LAST; + iter->imeff++, iter->bitmask <<= 1) { + if (iter->imeff->range == iter->range) { + freelog(LOG_DEBUG, "Effect iteration: effect %s at range %s", + effect_type_name(iter->imeff->type), + effect_range_name(iter->range)); + } + + if (effect->active & iter->bitmask + && iter->imeff->range == iter->range + && (iter->imeff->range != EFR_LOCAL + || effect->impr == iter->impr) + && is_city_affected(iter->imeff, iter->pcity) + && is_effect_outside(iter->imeff, iter->pcity)) { + return iter->imeff; + } + } + /* Force reinitialisation of imeff next time round */ + iter->imeff = NULL; + } + } + } + /* Make sure we restart at zero for the next range out */ + iter->index = 0; + } + /* We didn't match any more effects, so signal that we're done */ + iter->imeff = NULL; + return NULL; +} + +/************************************************************************** + Returns the next active effect that satisfies the aff_xxx fields set for + the city/unit etc. when the iterator was initialised. +**************************************************************************/ +struct impr_effect *eff_iterator_next(struct eff_iter *iter) +{ + struct impr_effect *imeff; + + do { + imeff = eff_iterator_next_internal(iter); + if (imeff && is_unit_terrain_affected(imeff, &iter->aff_unit, + iter->aff_terr, iter->aff_spec)) { + return imeff; + } + } while(imeff); + return NULL; +} + +/************************************************************************** + Returns the next active effect that satisfies the various aff_xxx fields + passed (use NULL, T_UNKNOWN and/or S_ALL respectively if you want + any value - except "None" - to match) +**************************************************************************/ +struct impr_effect *eff_iterator_next_full(struct eff_iter *iter, + struct unit_classes *aff_unit, + enum tile_terrain_type aff_terr, + enum tile_special_type aff_spec) +{ + struct impr_effect *imeff; + + do { + imeff = eff_iterator_next_internal(iter); + if (imeff && is_unit_terrain_affected(imeff, aff_unit, aff_terr, + aff_spec)) { + return imeff; + } + } while(imeff); + return NULL; +} + +/************************************************************************** + Returns the ID of the improvement that is conferring the last effect + returned from the iterator, or B_LAST if no building is doing so +**************************************************************************/ +Impr_Type_id eff_iterator_get_improvement(struct eff_iter *iter) +{ + struct ceff_vector *ceff; + struct eff_city *effect; + + assert(iter != NULL); + + if (!iter->imeff) + return B_LAST; + + ceff = iter->effs[iter->range]; + effect = ceff_vector_get(ceff, iter->index); + + return effect->impr; +} + +/************************************************************************** + Returns TRUE if the given effect should affect the given city (i.e. it + has the building required for the effect to be active) +**************************************************************************/ +bool is_city_affected(struct impr_effect *imeff, struct city *pcity) +{ + return (imeff->cond_bldg == B_LAST + || (pcity && city_got_building(pcity, imeff->cond_bldg))); +} + +/************************************************************************** + Returns TRUE if the given effect is in a city, or if it's in the field + and its "outside" flag is set, or if "outside" is ignored for this effect +**************************************************************************/ +bool is_effect_outside(struct impr_effect *imeff, struct city *pcity) +{ + if (!pcity && !imeff->outside) { + switch(imeff->type) { + case EFT_UNIT_DEFEND: + case EFT_UNIT_ATTACK: + case EFT_UNIT_DEFEND_FIREPOWER: + case EFT_UNIT_ATTACK_FIREPOWER: + case EFT_UNIT_RECOVER: + case EFT_UNIT_VET_COMBAT: + case EFT_UPGRADE_ONE_STEP: + case EFT_UPGRADE_ONE_LEAP: + case EFT_UPGRADE_ALL_STEP: + case EFT_UPGRADE_ALL_LEAP: + return FALSE; + default: + break; + } + } + return TRUE; +} + +/************************************************************************** + Returns TRUE if the given effect should affect the given + unit/terrain/special, by checking the relevant aff_xxx fields + (Pass in NULL, T_UNKNOWN, and S_ALL respectively if you don't care + about that field; these values will match everything except "None".) +**************************************************************************/ +int is_unit_terrain_affected(struct impr_effect *imeff, + struct unit_classes *aff_unit, + enum tile_terrain_type aff_terr, + enum tile_special_type aff_spec) +{ + return is_unit_class_in_set(imeff->aff_unit, aff_unit) + && (((aff_terr == T_UNKNOWN && imeff->aff_terr != T_LAST) + || imeff->aff_terr == T_UNKNOWN || imeff->aff_terr == aff_terr) + || imeff->aff_spec & aff_spec); +} + +/************************************************************************** + Returns the index of the improvement which provides the given effect + (constrained to the given unit class and player, if not 0 and + NULL respectively) or B_LAST if no suitable improvement could be found. +**************************************************************************/ +Impr_Type_id get_improvement_for_effect(struct player *pplayer, + enum effect_type id, + struct unit_classes *aff_unit) +{ + int impr; + struct impr_effect *eff; + + for (impr = 0; impr < game.num_impr_types; impr++) { + for (eff = improvement_types[impr].effect; + eff && eff->type != EFT_LAST; eff++) { + if (eff->type == id + && (!pplayer || can_player_build_improvement(pplayer, impr)) + && is_unit_terrain_affected(eff, aff_unit, T_UNKNOWN, S_ALL)) { + return impr; + } + } + } + return B_LAST; +} + +/************************************************************************** + Adds the effects of a previously-destroyed improvement to the given + players's destroyed effects vector (if player=NULL, then adds them to + the global game destroyed effects vector instead). +**************************************************************************/ +void add_destroyed_improvement(struct player *p, Impr_Type_id id) +{ + struct ceff_vector *desteff; + struct eff_city *ceff; + + game.destroyed_owner[id] = p; + freelog(LOG_DEBUG, "Adding destroyed improvement %s to %s", + improvement_types[id].name, p ? p->name : "global list"); + + desteff = (p ? &p->destroyed_effects : &game.destroyed_effects); + ceff = append_ceff(desteff); + ceff->impr = id; + ceff->active = 0; + add_global_effects(id, p); +} + +static void get_global_effects(Impr_Type_id impr, struct player *pplayer, + struct geff_vector *globeff[]) +{ + struct impr_effect *imeff; + + globeff[0] = globeff[1] = NULL; + for (imeff = get_base_effect(impr, pplayer); + imeff && imeff->type != EFT_LAST; ++imeff) { + if (imeff->range == EFR_WORLD) { + globeff[1] = get_eff_world(); + } else if (imeff->range == EFR_PLAYER) { + globeff[0] = get_eff_player(pplayer); + } + } +} + +void remove_global_effects(Impr_Type_id impr, struct player *pplayer) +{ + struct geff_vector *globeff[2]; + int i, effind; + + get_global_effects(impr, pplayer, globeff); + for (i = 0; i < 2; ++i) { + if (globeff[i]) { + for (effind = 0; effind < geff_vector_size(globeff[i]); ++effind) { + struct eff_global *eff = geff_vector_get(globeff[i], effind); + + if (eff->cityid == -1 && eff->eff.impr == impr && eff->plr == pplayer) { + eff->eff.impr = B_LAST; + } + } + } + } +} + +void add_global_effects(Impr_Type_id impr, struct player *pplayer) +{ + struct geff_vector *globeff[2]; + int i; + + get_global_effects(impr, pplayer, globeff); + for (i = 0; i < 2; ++i) { + if (globeff[i]) { + struct eff_global *geff; + + geff = append_geff(globeff[i]); + geff->eff.impr = impr; + geff->eff.active = 0; + geff->cityid = -1; + geff->plr = pplayer; + } + } +} diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/improvement.h freeciv-patched/common/improvement.h --- freeciv-cvs/common/improvement.h 2003-06-30 12:18:29.000000000 +0100 +++ freeciv-patched/common/improvement.h 2003-06-30 12:18:17.000000000 +0100 @@ -21,6 +21,7 @@ #include "unittype.h" /* Unit_Class_id, Unit_Type_id */ struct player; +struct map_position; /* Improvement types. @@ -44,9 +45,9 @@ #ifdef OLD_IMPR_TYPE_ENUM enum improvement_type_id { B_AIRPORT=0, B_AQUEDUCT, B_BANK, B_BARRACKS, B_BARRACKS2, B_BARRACKS3, - B_CATHEDRAL, B_CITY, B_COASTAL, B_COLOSSEUM, B_COURTHOUSE, B_FACTORY, + B_CATHEDRAL, B_orCITY, B_COASTAL, B_COLOSSEUM, B_COURTHOUSE, B_FACTORY, B_GRANARY, B_HARBOUR, B_HYDRO, B_LIBRARY, B_MARKETPLACE, B_MASS, B_MFG, - B_NUCLEAR, B_OFFSHORE, B_PALACE, B_POLICE, B_PORT, B_POWER, + B_NUCLEAR, B_OFFSHORE, B_orPALACE, B_POLICE, B_PORT, B_POWER, B_RECYCLING, B_RESEARCH, B_SAM, B_SDI, B_SEWER, B_SOLAR, B_SCOMP, B_SMODULE, B_SSTRUCTURAL, B_STOCK, B_SUPERHIGHWAYS, B_SUPERMARKET, B_TEMPLE, B_UNIVERSITY, @@ -60,6 +61,11 @@ }; #endif +/* Some code checks explicitly for City Walls or Palace - as a temporary + fix, set these to the indices of improvements which provide the + EFT_UNIT_DEFEND (aff_unit=Land) and EFT_CAPITAL_CITY effects */ +extern int B_CITY, B_PALACE; + /* B_LAST is a value which is guaranteed to be larger than all * actual Impr_Type_id values. It is used as a flag value; * it can also be used for fixed allocations to ensure ability @@ -77,11 +83,14 @@ IR_LAST /* keep this last */ }; +#define B_NATION (B_LAST + 1) +#define B_GOV (B_LAST + 2) + /* Range of effects (used in equiv_range and effect.range fields) * These must correspond to effect_range_names[] in improvement.c. */ enum effect_range { EFR_NONE, - EFR_BUILDING, + EFR_LOCAL, EFR_CITY, EFR_ISLAND, EFR_PLAYER, @@ -121,21 +130,21 @@ EFT_NO_SINK_DEEP, EFT_NUKE_PROOF, EFT_POLLU_ADJ, - EFT_POLLU_ADJ_POP, - EFT_POLLU_ADJ_PROD, - EFT_POLLU_SET, - EFT_POLLU_SET_POP, - EFT_POLLU_SET_PROD, + EFT_POLLU_POP_ADJ, + EFT_POLLU_PROD_ADJ, + EFT_POLLU_PCT, + EFT_POLLU_POP_PCT, + EFT_POLLU_PROD_PCT, EFT_PROD_ADD_TILE, EFT_PROD_BONUS, EFT_PROD_INC_TILE, EFT_PROD_PER_TILE, EFT_PROD_TO_GOLD, - EFT_REDUCE_CORRUPT, - EFT_REDUCE_WASTE, + EFT_CORRUPT_ADJ, + EFT_CORRUPT_PCT, EFT_REVEAL_CITIES, EFT_REVEAL_MAP, - EFT_REVOLT_DIST, + EFT_REVOLT_DIST_ADJ, EFT_SCIENCE_BONUS, EFT_SCIENCE_PCT, EFT_SIZE_UNLIMIT, @@ -163,6 +172,21 @@ EFT_UPGRADE_ALL_STEP, EFT_UPGRADE_ALL_LEAP, EFT_UPKEEP_FREE, + EFT_FOOD_PCT, + EFT_PROD_PCT, + EFT_REVOLT_DIST_PCT, + EFT_TRADE_PCT, + EFT_MAKE_CONTENT_MIL_PER, + EFT_FORCE_CONTENT, + EFT_FORCE_CONTENT_PCT, + EFT_FORCE_HAPPY, + EFT_UNIT_ATTACK, + EFT_UNIT_ATTACK_FIREPOWER, + EFT_UNIT_DEFEND_FIREPOWER, + EFT_UNIT_COST, + EFT_BUILDING_COST, + EFT_UNIT_COST_PCT, + EFT_BUILDING_COST_PCT, EFT_LAST /* keep this last */ }; @@ -172,6 +196,9 @@ enum effect_range range; int amount; int survives; /* 1 = effect survives wonder destruction */ + int outside; /* 1 = effect influences units outside + * of cities, not just those in the + * center tile */ Impr_Type_id cond_bldg; /* B_LAST = unconditional */ int cond_gov; /* game.government_count = unconditional */ Tech_Type_id cond_adv; /* A_NONE = unconditional; A_LAST = never */ @@ -181,10 +208,38 @@ enum tile_special_type aff_spec; /* S_* bit mask of specials affected */ }; +/* Status of a city's improvement effects + * (a bitfield: bit 0 set = first effect active, etc.) */ +typedef unsigned Eff_Status; + /* Maximum number of effects per improvement * (this should not be more than the number of bits in the Eff_Status type) */ #define MAX_EFFECTS 16 +/* Keeps track of which improvement effects are active in a city. */ +struct eff_city { + Impr_Type_id impr; /* The ID of the improvement that confers the effects; + * if B_LAST, then this instance is unused and ready + * to be freed (or replaced with a new improvement); + * if B_NATION or B_GOV, then the effects are not + * from an improvement at all, but are conferred by + * the nation or governement type */ + Eff_Status active; /* Which of the actual impr_effect effects are active */ +}; + +/* Copy of eff_city effect activity for effects with Player-, Island-, + * and World- ranges */ +struct eff_global { + struct eff_city eff; /* Should be updated whenever the corresponding + * structure in the improvement's home city is + * modified. N.B. Keep this first in the structure + * so that a (struct eff_city) cast works */ + int cityid; /* ID of the city that owns the improvment (if -1, + * then the effect has survived the city destruction, + * and should therefore be placed in the savefile) */ + struct player *plr; /* Player that owns whatever confers this effect */ +}; + /* Type of improvement. (Read from buildings.ruleset file.) */ struct impr_type { char name[MAX_LEN_NAME]; @@ -214,21 +269,144 @@ extern struct impr_type improvement_types[B_LAST]; +/* get 'struct ceff_vector' and related functions: */ +#define SPECVEC_TAG ceff +#define SPECVEC_TYPE struct eff_city +#include "specvec.h" + +/* get 'struct geff_vector' and related functions: */ +#define SPECVEC_TAG geff +#define SPECVEC_TYPE struct eff_global +#include "specvec.h" + /* impr range id/string converters */ enum impr_range impr_range_from_str(const char *str); const char *impr_range_name(enum impr_range id); +/* An iterator to look at the effects affecting a building/city/player. */ +struct eff_iter { + struct impr_effect *imeff; /* The next active effect */ + struct city *pcity; + struct player *pplayer; + Impr_Type_id impr; + struct ceff_vector *effs[EFR_LAST]; + enum effect_range range; + int index; + Eff_Status bitmask; + Unit_Type_id utype; + struct unit_classes aff_unit; + enum tile_terrain_type aff_terr; + enum tile_special_type aff_spec; +}; + /* improvement effect functions */ enum effect_range effect_range_from_str(const char *str); const char *effect_range_name(enum effect_range id); enum effect_type effect_type_from_str(const char *str); const char *effect_type_name(enum effect_type id); +void get_effect_vectors(struct ceff_vector *ceffs[], + struct geff_vector *geffs[], + Impr_Type_id impr, struct city *pcity); +void get_all_effect_vectors(struct ceff_vector *effs[EFR_LAST], + Impr_Type_id impr, struct city *pcity, + int cont, struct player *plr); +bool is_under_effect(Impr_Type_id impr, struct city *pcity, + struct player *pplayer, enum effect_type cond_eff); +bool is_effect_activated(Impr_Type_id impr, struct city *pcity, + struct player *pplayer, struct impr_effect *imeff, + bool *cond_eff); +struct ceff_vector *get_geff_parent(struct geff_vector *geff); +void update_global_effect(struct city *pcity, struct player *pplayer, + struct eff_city *effect); +struct eff_city *append_ceff(struct ceff_vector *x); +struct eff_global *append_geff(struct geff_vector *x); +void eff_iterator_impr_init(struct eff_iter *iter, Impr_Type_id impr, + struct city *pcity, struct player *pplayer); +void eff_iterator_unit_init(struct eff_iter *iter, Unit_Type_id utype, + Unit_Type_id aff_utype, + struct player *pplayer, struct map_position *mp); +struct impr_effect *eff_iterator_next(struct eff_iter *iter); +struct impr_effect *eff_iterator_next_full(struct eff_iter *iter, + struct unit_classes *aff_unit, + enum tile_terrain_type aff_terr, + enum tile_special_type aff_spec); +Impr_Type_id eff_iterator_get_improvement(struct eff_iter *iter); +int is_unit_terrain_affected(struct impr_effect *imeff, + struct unit_classes *aff_unit, + enum tile_terrain_type aff_terr, + enum tile_special_type aff_spec); +bool is_city_affected(struct impr_effect *imeff, struct city *pcity); +bool is_effect_outside(struct impr_effect *imeff, struct city *pcity); + +#define city_effects_iterate(EI_iter, pcity) \ + { \ + struct eff_iter EI_iter; \ + eff_iterator_impr_init(&EI_iter, B_LAST, pcity, city_owner(pcity)); \ + while (eff_iterator_next(&EI_iter)) { + +#define city_effects_iterate_end \ + } \ + } + +#define city_effects_iterate_full(EI_iter, pcity, uclass, terr, spec) \ + { \ + struct eff_iter EI_iter; \ + eff_iterator_impr_init(&EI_iter, B_LAST, pcity, city_owner(pcity)); \ + while (eff_iterator_next_full(&EI_iter, uclass, terr, spec)) { + +#define city_effects_iterate_full_end \ + } \ + } + +#define impr_effects_iterate(EI_iter, impr, pcity, pplayer) \ + { \ + struct eff_iter EI_iter; \ + eff_iterator_impr_init(&EI_iter, impr, pcity, pplayer); \ + while (eff_iterator_next(&EI_iter)) { + +#define impr_effects_iterate_end \ + } \ + } + +#define impr_effects_iterate_full(EI_iter, impr, pcity, pplayer, uclass, terr, spec) \ + { \ + struct eff_iter EI_iter; \ + eff_iterator_impr_init(&EI_iter, impr, pcity, pplayer); \ + while (eff_iterator_next_full(&EI_iter, uclass, terr, spec)) { + +#define impr_effects_iterate_full_end \ + } \ + } + +#define unit_effects_iterate(EI_iter, punit, aff_unit) \ + { \ + struct eff_iter EI_iter; \ + struct map_position EI_mappos; \ + EI_mappos.x = (punit)->x; EI_mappos.y = (punit)->y; \ + eff_iterator_unit_init(&iter, (punit)->type, (aff_unit)->type, unit_owner(punit), &EI_mappos); \ + while (eff_iterator_next(&EI_iter)) { + +#define unit_effects_iterate_end \ + } \ + } + +#define unittype_effects_iterate(EI_iter, utype, aff_utype, pplayer, mappos) \ + { \ + struct eff_iter EI_iter; \ + eff_iterator_unit_init(&iter, utype, aff_utype, pplayer, mappos); \ + while (eff_iterator_next(&EI_iter)) { + +#define unittype_effects_iterate_end \ + } \ + } + /* improvement functions */ void improvements_free(void); struct impr_type *get_improvement_type(Impr_Type_id id); bool improvement_exists(Impr_Type_id id); int improvement_value(Impr_Type_id id); +int improvement_value_adjusted(Impr_Type_id id, struct city *pcity); bool is_wonder(Impr_Type_id id); const char *get_improvement_name(Impr_Type_id id); @@ -242,6 +420,11 @@ bool is_wonder_useful(Impr_Type_id id); Impr_Type_id find_improvement_by_name(const char *s); void improvement_status_init(Impr_Status * improvements, size_t elements); +Impr_Type_id get_improvement_for_effect(struct player *pplayer, + enum effect_type id, + struct unit_classes *aff_unit); +struct impr_effect *get_base_effect(Impr_Type_id impr, struct player *pplayer); +const char *get_effect_cause_name(Impr_Type_id impr, struct player *pplayer); /* player related improvement and unit functions */ bool could_player_eventually_build_improvement(struct player *p, @@ -249,7 +432,15 @@ bool can_player_build_improvement(struct player *p, Impr_Type_id id); /* city related improvement functions */ +void add_destroyed_improvement(struct player *p, Impr_Type_id id); void mark_improvement(struct city *pcity,Impr_Type_id id,Impr_Status status); +struct geff_vector *get_eff_world(void); +struct geff_vector *get_eff_player(struct player *plr); +struct geff_vector *get_eff_island(int cont, struct player *plr); +struct ceff_vector *get_eff_city(struct city *pcity); +void remove_global_effects(Impr_Type_id impr, struct player *pplayer); +void add_global_effects(Impr_Type_id impr, struct player *pplayer); + void allot_island_improvs(void); void improvements_update_obsolete(void); void improvements_update_redundant(struct player *pplayer, struct city *pcity, diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/map.c freeciv-patched/common/map.c --- freeciv-cvs/common/map.c 2003-06-30 12:18:29.000000000 +0100 +++ freeciv-patched/common/map.c 2003-06-30 12:15:13.000000000 +0100 @@ -193,6 +193,7 @@ map.separatepoles = MAP_DEFAULT_SEPARATE_POLES; map.tiles = NULL; map.num_continents = 0; + map.old_num_continents = -1; map.num_start_positions = 0; map.fixed_start_positions = FALSE; map.have_specials = FALSE; diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/map.h freeciv-patched/common/map.h --- freeciv-cvs/common/map.h 2003-06-30 12:18:29.000000000 +0100 +++ freeciv-patched/common/map.h 2003-06-30 12:15:13.000000000 +0100 @@ -173,6 +173,7 @@ bool have_huts; bool have_rivers_overlay; /* only applies if !have_specials */ int num_continents; + int old_num_continents; struct tile *tiles; /* Only used by server. */ diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/nation.c freeciv-patched/common/nation.c --- freeciv-cvs/common/nation.c 2003-06-30 12:18:25.000000000 +0100 +++ freeciv-patched/common/nation.c 2003-06-30 12:18:03.000000000 +0100 @@ -208,6 +208,9 @@ int i; struct nation_type *p = get_nation_by_idx(nation); + free(p->effect); + p->effect = NULL; + for (i = 0; i < p->leader_count; i++) { free(p->leaders[i].name); } diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/nation.h freeciv-patched/common/nation.h --- freeciv-cvs/common/nation.h 2003-06-30 12:18:25.000000000 +0100 +++ freeciv-patched/common/nation.h 2003-06-30 12:18:03.000000000 +0100 @@ -36,6 +36,7 @@ struct Sprite; /* opaque; client-gui specific */ struct player; +struct impr_effect; enum advisor_type {ADV_ISLAND, ADV_MILITARY, ADV_TRADE, ADV_SCIENCE, ADV_FOREIGN, ADV_ATTITUDE, ADV_DOMESTIC, ADV_LAST}; @@ -98,6 +99,8 @@ /* civilized was never implemented, but will be eventually. -- Syela */ int advisors[ADV_LAST]; /* never implemented either. -- Syela */ + struct impr_effect *effect; /* list; .type==EFT_LAST terminated */ + /* Following basically disabled -- Syela */ /* Note the client doesn't use/have these. */ struct { diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/packets.c freeciv-patched/common/packets.c --- freeciv-cvs/common/packets.c 2003-06-30 12:18:26.000000000 +0100 +++ freeciv-patched/common/packets.c 2003-06-30 12:18:10.000000000 +0100 @@ -178,6 +178,13 @@ return result; } +static void dio_put_effects(struct data_out *dout, + struct connection *pc, + const struct impr_effect *effect); +static void dio_get_effects(struct data_in *din, + struct connection *pc, + struct impr_effect **peff); + /************************************************************************** presult indicates if there is more packets in the cache. We return result instead of just testing if the returning package is NULL as we sometimes @@ -1053,7 +1060,11 @@ for (i = 0; i < A_LAST /*game.num_tech_types */ ; i++) dio_put_uint8(&dout, pinfo->global_advances[i]); for (i = 0; i < B_LAST /*game.num_impr_types */ ; i++) - dio_put_uint16(&dout, pinfo->global_wonders[i]); + dio_put_sint16(&dout, pinfo->global_wonders[i]); + if (has_capability("impr_gen", pc->capability)) { + for (i = 0; i < B_LAST; i++) + dio_put_sint16(&dout, pinfo->destroyed_owner[i]); + } dio_put_uint8(&dout, pinfo->techpenalty); dio_put_uint8(&dout, pinfo->foodbox); @@ -1101,7 +1112,14 @@ for (i = 0; i < A_LAST /*game.num_tech_types */ ; i++) dio_get_uint8(&din, &pinfo->global_advances[i]); for (i = 0; i < B_LAST /*game.num_impr_types */ ; i++) - dio_get_uint16(&din, &pinfo->global_wonders[i]); + dio_get_sint16(&din, &pinfo->global_wonders[i]); + if (has_capability("impr_gen", pc->capability)) { + for (i = 0; i < B_LAST; i++) + dio_get_sint16(&din, &pinfo->destroyed_owner[i]); + } else { + for(i = 0; i < B_LAST; i++) + pinfo->destroyed_owner[i] = -1; + } dio_get_uint8(&din, &pinfo->techpenalty); dio_get_uint8(&din, &pinfo->foodbox); @@ -1362,6 +1380,11 @@ dio_put_uint32(&dout, req->turn_founded); + if (has_capability("impr_gen", pc->capability)) { + dio_put_uint8(&dout, req->buildflags); + dio_put_uint8(&dout, req->upkeep_free); + } + for (data = 0; data < NUM_TRADEROUTES; data++) { if (req->trade[data] != 0) { dio_put_uint16(&dout, req->trade[data]); @@ -1453,6 +1476,11 @@ dio_get_uint32(&din, &packet->turn_founded); + if (has_capability("impr_gen", pc->capability)) { + dio_get_uint8(&din, &packet->buildflags); + dio_get_uint8(&din, &packet->upkeep_free); + } + for (data = 0; data < NUM_TRADEROUTES; data++) { if (dio_input_remaining(&din) < 3) break; @@ -1859,6 +1887,9 @@ dio_put_uint8(&dout, packet->default_government); dio_put_uint8(&dout, packet->government_when_anarchy); + if (has_capability("impr_gen", pc->capability)) { + dio_put_uint8(&dout, packet->num_unit_flags); + } dio_put_uint8(&dout, packet->num_unit_types); dio_put_uint8(&dout, packet->num_impr_types); dio_put_uint8(&dout, packet->num_tech_types); @@ -1905,6 +1936,11 @@ dio_get_uint8(&din, &packet->default_government); dio_get_uint8(&din, &packet->government_when_anarchy); + if (has_capability("impr_gen", pc->capability)) { + dio_get_uint8(&din, &packet->num_unit_flags); + } else { + packet->num_unit_flags = F_LAST; + } dio_get_uint8(&din, &packet->num_unit_types); dio_get_uint8(&din, &packet->num_impr_types); dio_get_uint8(&din, &packet->num_tech_types); @@ -1970,6 +2006,10 @@ } dio_put_uint8(&dout, packet->pop_cost); + if (has_capability("impr_gen", pc->capability)) { + dio_put_effects(&dout, pc, packet->effect); + } + /* This must be last, so client can determine length: */ if(packet->helptext) { dio_put_string(&dout, packet->helptext); @@ -2030,6 +2070,13 @@ } dio_get_uint8(&din, &packet->pop_cost); + if (has_capability("impr_gen", pc->capability)) { + dio_get_effects(&din, pc, &packet->effect); + } else { + packet->effect = fc_malloc(sizeof(struct impr_effect)); + packet->effect[0].type = EFT_LAST; + } + len = dio_input_remaining(&din); if (len > 0) { packet->helptext = fc_malloc(len); @@ -2115,11 +2162,44 @@ /************************************************************************** ... **************************************************************************/ +static void dio_put_effects(struct data_out *dout, + struct connection *pc, + const struct impr_effect *effect) +{ + const struct impr_effect *eff; + int count; + + assert(effect != NULL); + + for (count = 0, eff = effect; eff->type != EFT_LAST; count++, eff++) { + /* nothing */ + } + + dio_put_uint8(dout, count); + for (eff = effect; eff->type != EFT_LAST; eff++) { + dio_put_uint8(dout, eff->type); + dio_put_uint8(dout, eff->range); + dio_put_sint16(dout, eff->amount); + dio_put_uint8(dout, eff->survives); + if (has_capability("impr_gen", pc->capability)) { + dio_put_uint8(dout, eff->outside); + } + dio_put_uint8(dout, eff->cond_bldg); + dio_put_uint8(dout, eff->cond_gov); + dio_put_uint8(dout, eff->cond_adv); + dio_put_uint8(dout, eff->cond_eff); + dio_put_uint8(dout, eff->aff_unit); + dio_put_uint8(dout, eff->aff_terr); + dio_put_uint16(dout, eff->aff_spec); + } +} + +/************************************************************************** +... +**************************************************************************/ int send_packet_ruleset_building(struct connection *pc, const struct packet_ruleset_building *packet) { - struct impr_effect *eff; - int count; SEND_PACKET_START(PACKET_RULESET_BUILDING); dio_put_uint8(&dout, packet->id); @@ -2143,24 +2223,8 @@ dio_put_uint16(&dout, packet->build_cost); dio_put_uint8(&dout, packet->upkeep); dio_put_uint8(&dout, packet->sabotage); - for (count = 0, eff = packet->effect; eff->type != EFT_LAST; - count++, eff++) { - /* nothing */ - } - dio_put_uint8(&dout, count); - for (eff = packet->effect; eff->type != EFT_LAST; eff++) { - dio_put_uint8(&dout, eff->type); - dio_put_uint8(&dout, eff->range); - dio_put_sint16(&dout, eff->amount); - dio_put_uint8(&dout, eff->survives); - dio_put_uint8(&dout, eff->cond_bldg); - dio_put_uint8(&dout, eff->cond_gov); - dio_put_uint8(&dout, eff->cond_adv); - dio_put_uint8(&dout, eff->cond_eff); - dio_put_uint8(&dout, eff->aff_unit); - dio_put_uint8(&dout, eff->aff_terr); - dio_put_uint16(&dout, eff->aff_spec); - } + dio_put_effects(&dout, pc, packet->effect); + dio_put_uint8(&dout, packet->variant); /* FIXME: remove when gen-impr obsoletes */ dio_put_string(&dout, packet->name); @@ -2183,10 +2247,42 @@ /************************************************************************** ... **************************************************************************/ +static void dio_get_effects(struct data_in *din, + struct connection *pc, + struct impr_effect **peff) +{ + int count, inx; + struct impr_effect *effect; + + dio_get_uint8(din, &count); + effect = fc_malloc((count + 1) * sizeof(struct impr_effect)); + for (inx = 0; inx < count; inx++) { + dio_get_uint8(din, (int *)&(effect[inx].type)); + dio_get_uint8(din, (int *)&(effect[inx].range)); + dio_get_sint16(din, &(effect[inx].amount)); + dio_get_uint8(din, &(effect[inx].survives)); + if (has_capability("impr_gen", pc->capability)) { + dio_get_uint8(din, &(effect[inx].outside)); + } + dio_get_uint8(din, &(effect[inx].cond_bldg)); + dio_get_uint8(din, &(effect[inx].cond_gov)); + dio_get_uint8(din, &(effect[inx].cond_adv)); + dio_get_uint8(din, (int *)&(effect[inx].cond_eff)); + dio_get_uint8(din, (int *)&(effect[inx].aff_unit)); + dio_get_uint8(din, (int *)&(effect[inx].aff_terr)); + dio_get_uint16(din, (int *)&(effect[inx].aff_spec)); + } + effect[count].type = EFT_LAST; + *peff = effect; +} + +/************************************************************************** +... +**************************************************************************/ struct packet_ruleset_building * receive_packet_ruleset_building(struct connection *pc) { - int len, inx, count; + int len; RECEIVE_PACKET_START(packet_ruleset_building, packet); dio_get_uint8(&din, &packet->id); @@ -2206,22 +2302,8 @@ dio_get_uint16(&din, &packet->build_cost); dio_get_uint8(&din, &packet->upkeep); dio_get_uint8(&din, &packet->sabotage); - dio_get_uint8(&din, &count); - packet->effect = fc_malloc((count + 1) * sizeof(struct impr_effect)); - for (inx = 0; inx < count; inx++) { - dio_get_uint8(&din, (int *)&(packet->effect[inx].type)); - dio_get_uint8(&din, (int *)&(packet->effect[inx].range)); - dio_get_sint16(&din, &(packet->effect[inx].amount)); - dio_get_uint8(&din, &(packet->effect[inx].survives)); - dio_get_uint8(&din, &(packet->effect[inx].cond_bldg)); - dio_get_uint8(&din, &(packet->effect[inx].cond_gov)); - dio_get_uint8(&din, &(packet->effect[inx].cond_adv)); - dio_get_uint8(&din, (int *)&(packet->effect[inx].cond_eff)); - dio_get_uint8(&din, (int *)&(packet->effect[inx].aff_unit)); - dio_get_uint8(&din, (int *)&(packet->effect[inx].aff_terr)); - dio_get_uint16(&din, (int *)&(packet->effect[inx].aff_spec)); - } - packet->effect[count].type = EFT_LAST; + dio_get_effects(&din, pc, &packet->effect); + dio_get_uint8(&din, &packet->variant); /* FIXME: remove when gen-impr obsoletes */ dio_get_string(&din, packet->name, sizeof(packet->name)); @@ -2503,6 +2585,10 @@ dio_put_string(&dout, packet->graphic_str); dio_put_string(&dout, packet->graphic_alt); + if (has_capability("impr_gen", pc->capability)) { + dio_put_effects(&dout, pc, packet->effect); + } + /* This must be last, so client can determine length: */ if(packet->helptext) { dio_put_string(&dout, packet->helptext); @@ -2596,6 +2682,13 @@ dio_get_string(&din, packet->graphic_str, sizeof(packet->graphic_str)); dio_get_string(&din, packet->graphic_alt, sizeof(packet->graphic_alt)); + if (has_capability("impr_gen", pc->capability)) { + dio_get_effects(&din, pc, &packet->effect); + } else { + packet->effect = fc_malloc(sizeof(struct impr_effect)); + packet->effect[0].type = EFT_LAST; + } + len = dio_input_remaining(&din); if (len > 0) { packet->helptext = fc_malloc(len); @@ -2646,6 +2739,9 @@ } dio_put_uint8(&dout, packet->city_style); dio_put_tech_list(&dout, packet->init_techs); + if (has_capability("impr_gen", pc->capability)) { + dio_put_effects(&dout, pc, packet->effect); + } if (has_capability("class_legend", pc->capability)) { dio_put_string(&dout, packet->class); dio_put_string(&dout, packet->legend); @@ -2683,6 +2779,9 @@ dio_get_uint8(&din, &packet->city_style); dio_get_tech_list(&din, packet->init_techs); + if (has_capability("impr_gen", pc->capability)) { + dio_get_effects(&din, pc, &packet->effect); + } if (has_capability("class_legend", pc->capability)) { dio_get_string(&din, packet->class, sizeof(packet->class)); dio_get_string(&din, packet->legend, sizeof(packet->legend)); diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/packets.h freeciv-patched/common/packets.h --- freeciv-cvs/common/packets.h 2003-06-30 12:18:26.000000000 +0100 +++ freeciv-patched/common/packets.h 2003-06-30 12:18:03.000000000 +0100 @@ -368,6 +368,8 @@ bool diplomat_investigate; int city_options; int turn_founded; + + int buildflags, upkeep_free; }; @@ -566,6 +568,7 @@ int sewer_size; int add_to_size_limit; int notradesize, fulltradesize; + int num_unit_flags; int num_unit_types; int num_impr_types; int num_tech_types; @@ -623,6 +626,7 @@ int paratroopers_range; /* max range of paratroopers, F_PARATROOPERS */ int paratroopers_mr_req; int paratroopers_mr_sub; + struct impr_effect *effect; /* Following is a pointer to malloced memory; on the server, it points to putype->helptext, malloced earlier; on the client, @@ -775,6 +779,7 @@ char graphic_str[MAX_LEN_NAME]; char graphic_alt[MAX_LEN_NAME]; + struct impr_effect *effect; char *helptext; /* same as for packet_ruleset_unit, above */ }; @@ -798,6 +803,7 @@ bool leader_sex[MAX_NUM_LEADERS]; int city_style; int init_techs[MAX_NUM_TECH_LIST]; + struct impr_effect *effect; char class[MAX_LEN_NAME]; char legend[MAX_LEN_MSG]; }; @@ -854,6 +860,7 @@ int diplcost,freecost,conquercost; int global_advances[A_LAST]; int global_wonders[B_LAST]; + int destroyed_owner[B_LAST]; int foodbox; int techpenalty; bool spacerace; diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/player.c freeciv-patched/common/player.c --- freeciv-cvs/common/player.c 2003-06-30 12:18:29.000000000 +0100 +++ freeciv-patched/common/player.c 2003-06-30 12:18:03.000000000 +0100 @@ -25,6 +25,7 @@ #include "government.h" #include "idex.h" #include "improvement.h" +#include "log.h" #include "map.h" #include "mem.h" #include "rand.h" @@ -66,7 +67,7 @@ { return (TEST_BIT(pplayer->embassy, pplayer2->player_no) || (pplayer == pplayer2) - || (player_owns_active_wonder(pplayer, B_MARCO) + || (is_under_effect(B_LAST, NULL, pplayer, EFT_HAVE_EMBASSIES) && !is_barbarian(pplayer2))); } @@ -130,14 +131,22 @@ /* Initialise list of improvements with Player-wide equiv_range */ improvement_status_init(plr->improvements, ARRAY_SIZE(plr->improvements)); + /* Initialise vector of effects with player range. */ + geff_vector_init(&plr->effects); + /* Initialise player's list of destroyed effects */ + ceff_vector_init(&plr->destroyed_effects); /* Initialise list of improvements with Island-wide equiv_range */ plr->island_improv = NULL; + plr->island_effects = NULL; if (map.num_continents > 0) { plr->island_improv = fc_malloc((map.num_continents + 1) * game.num_impr_types * sizeof(Impr_Status)); + plr->island_effects = fc_calloc(map.num_continents + 1, + sizeof(struct geff_vector)); for (i = 1; i <= map.num_continents; i++) { + geff_vector_init(&plr->island_effects[i]); improvement_status_init(&plr->island_improv[i * game.num_impr_types], game.num_impr_types); } @@ -314,18 +323,6 @@ } /************************************************************************** - ... -**************************************************************************/ -bool player_owns_active_govchange_wonder(struct player *pplayer) -{ - return ( player_owns_active_wonder(pplayer, B_LIBERTY) || - ( (improvement_variant(B_PYRAMIDS)==1) && - player_owns_active_wonder(pplayer, B_PYRAMIDS) ) || - ( (improvement_variant(B_UNITED)==1) && - player_owns_active_wonder(pplayer, B_UNITED) ) ); -} - -/************************************************************************** Returns the number of techs the player has researched which has this flag. Needs to be optimized later (e.g. int tech_flags[TF_LAST] in struct player) @@ -439,7 +436,7 @@ struct city *find_palace(struct player *pplayer) { city_list_iterate(pplayer->cities, pcity) - if (city_got_building(pcity, B_PALACE)) + if (city_got_building(pcity, B_PALACE)) return pcity; city_list_iterate_end; return NULL; @@ -610,3 +607,38 @@ { return TEST_BIT(me->gives_shared_vision, them->player_no); } + +void player_update_global_effects(struct player *pplayer) +{ + struct ceff_vector *ceff; + struct eff_city *eff; + int effind; + + freelog(LOG_DEBUG, "player_update_global_effects: " + "player %s nation %s gov %s", pplayer->name, + get_nation_name(pplayer->nation), + get_government_name(pplayer->government)); + ceff = &pplayer->destroyed_effects; + + /* First, remove any existing effects */ + for (effind = 0; effind < ceff_vector_size(ceff); ++effind) { + eff = ceff_vector_get(ceff, effind); + + if (eff->impr == B_NATION || eff->impr == B_GOV) { + remove_global_effects(eff->impr, pplayer); + eff->impr = B_LAST; + } + } + + ceff = &pplayer->destroyed_effects; + + eff = append_ceff(ceff); + eff->impr = B_NATION; + eff->active = 0; + add_global_effects(eff->impr, pplayer); + + eff = append_ceff(ceff); + eff->impr = B_GOV; + eff->active = 0; + add_global_effects(eff->impr, pplayer); +} diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/player.h freeciv-patched/common/player.h --- freeciv-cvs/common/player.h 2003-06-30 12:18:29.000000000 +0100 +++ freeciv-patched/common/player.h 2003-06-30 12:18:10.000000000 +0100 @@ -205,6 +205,13 @@ Impr_Status improvements[B_LAST]; /* improvements with equiv_range==Player */ Impr_Status *island_improv; /* improvements with equiv_range==Island, dimensioned to [map.num_continents][game.num_impr_types] */ + struct geff_vector effects; /* effects with range==Player */ + struct geff_vector *island_effects; /* effects with range==Island */ + struct ceff_vector destroyed_effects;/* list of effects that have survived + * building destruction */ + + /* Bonuses that affect all cities */ + int science_bonus, tax_bonus; struct { int length; @@ -233,7 +240,6 @@ bool player_in_city_radius(struct player *pplayer, int x, int y); bool player_owns_active_wonder(struct player *pplayer, Impr_Type_id id); -bool player_owns_active_govchange_wonder(struct player *pplayer); bool player_knows_improvement_tech(struct player *pplayer, Impr_Type_id id); bool player_knows_techs_with_flag(struct player *pplayer, @@ -269,6 +275,7 @@ bool is_barbarian(const struct player *pplayer); bool gives_shared_vision(struct player *me, struct player *them); +void player_update_global_effects(struct player *pplayer); #define players_iterate(PI_player) \ { \ diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/tech.c freeciv-patched/common/tech.c --- freeciv-cvs/common/tech.c 2003-06-30 12:18:29.000000000 +0100 +++ freeciv-patched/common/tech.c 2003-06-30 12:18:10.000000000 +0100 @@ -22,6 +22,7 @@ #include "fcintl.h" #include "game.h" +#include "log.h" #include "player.h" #include "shared.h" /* ARRAY_SIZE */ #include "support.h" @@ -66,7 +67,7 @@ if (value == TECH_KNOWN) { game.global_advances[tech]++; - improvements_update_obsolete(); + update_all_effects(); } } @@ -327,18 +328,33 @@ } /************************************************************************** - Returns number of turns to advance (assuming current state of - civilization). + Returns the total number of bulbs the civilization produces every turn **************************************************************************/ -int tech_turns_to_advance(struct player *pplayer) +int total_science_output(struct player *pplayer) { - /* The number of bulbs the civilization produces every turn. */ int current_output = 0; city_list_iterate(pplayer->cities, pcity) { current_output += pcity->science_total; } city_list_iterate_end; + freelog(LOG_DEBUG, "Total science output: %d", current_output); + current_output = current_output * pplayer->science_bonus / 100; + current_output = current_output * game.science_bonus / 100; + freelog(LOG_DEBUG, "Player bonus %d; game bonus %d; total %d", + pplayer->science_bonus, game.science_bonus, current_output); + + return current_output; +} + +/************************************************************************** + Returns number of turns to advance (assuming current state of + civilization). +**************************************************************************/ +int tech_turns_to_advance(struct player *pplayer) +{ + int current_output = total_science_output(pplayer); + if (current_output <= 0) { return FC_INFINITY; } diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/tech.h freeciv-patched/common/tech.h --- freeciv-cvs/common/tech.h 2003-06-30 12:18:24.000000000 +0100 +++ freeciv-patched/common/tech.h 2003-06-30 12:18:10.000000000 +0100 @@ -119,6 +119,7 @@ enum tech_flag_id tech_flag_from_str(const char *s); Tech_Type_id find_tech_by_flag(int index, enum tech_flag_id flag); +int total_science_output(struct player *pplayer); int tech_turns_to_advance(struct player *pplayer); int total_bulbs_required(struct player *pplayer); diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/unit.c freeciv-patched/common/unit.c --- freeciv-cvs/common/unit.c 2003-06-30 12:18:28.000000000 +0100 +++ freeciv-patched/common/unit.c 2003-06-30 12:18:17.000000000 +0100 @@ -38,9 +38,6 @@ This function calculates the move rate of the unit taking into account the penalty for reduced hitpoints (affects sea and land units only) and the effects of wonders for sea units. - -FIXME: Use generalised improvements code instead of hardcoded -wonder effects --RK ***************************************************************/ int unit_move_rate(struct unit *punit) { @@ -49,20 +46,13 @@ switch (unit_type(punit)->move_type) { case LAND_MOVING: move_rate = (move_rate * punit->hp) / unit_type(punit)->hp; + move_rate += punit->move_modifier; break; case SEA_MOVING: move_rate = (move_rate * punit->hp) / unit_type(punit)->hp; + move_rate += punit->move_modifier; - if (player_owns_active_wonder(unit_owner(punit), B_LIGHTHOUSE)) { - move_rate += SINGLE_MOVE; - } - - if (player_owns_active_wonder(unit_owner(punit), B_MAGELLAN)) { - move_rate += (improvement_variant(B_MAGELLAN) == 1) - ? SINGLE_MOVE : 2 * SINGLE_MOVE; - } - if (player_knows_techs_with_flag(unit_owner(punit), TF_BOAT_FAST)) { move_rate += SINGLE_MOVE; } @@ -74,6 +64,7 @@ case HELI_MOVING: case AIR_MOVING: + move_rate += punit->move_modifier; break; default: @@ -210,15 +201,19 @@ **************************************************************************/ bool unit_can_help_build_wonder(struct unit *punit, struct city *pcity) { + int imp_value; + if (!is_tiles_adjacent(punit->x, punit->y, pcity->x, pcity->y) && !same_pos(punit->x, punit->y, pcity->x, pcity->y)) return FALSE; + imp_value = improvement_value_adjusted(pcity->currently_building, pcity); + return unit_flag(punit, F_HELP_WONDER) && punit->owner == pcity->owner && !pcity->is_building_unit && is_wonder(pcity->currently_building) - && pcity->shield_stock < improvement_value(pcity->currently_building); + && pcity->shield_stock < imp_value; } @@ -411,6 +406,14 @@ /************************************************************************** ... **************************************************************************/ +void get_unit_classes(struct unit *punit, struct unit_classes *classes) +{ + get_unittype_classes(punit->type, classes); +} + +/************************************************************************** +... +**************************************************************************/ bool kills_citizen_after_attack(struct unit *punit) { return TEST_BIT(game.killcitizen, (int) (unit_type(punit)->move_type) - 1); @@ -476,14 +479,8 @@ return AB_TOO_BIG; if (pcity->owner != punit->owner) return AB_NOT_OWNER; - if (improvement_exists(B_AQUEDUCT) - && !city_got_building(pcity, B_AQUEDUCT) - && new_pop > game.aqueduct_size) + if (pcity->size_limit >= 0 && new_pop > pcity->size_limit) return AB_NO_AQUEDUCT; - if (improvement_exists(B_SEWER) - && !city_got_building(pcity, B_SEWER) - && new_pop > game.sewer_size) - return AB_NO_SEWER; return AB_ADD_OK; } @@ -1329,9 +1326,9 @@ } /************************************************************************** - Like base_trireme_loss_pct but take the position into account. + Like base_ship_loss_pct but take the position into account. **************************************************************************/ -int trireme_loss_pct(struct player *pplayer, int x, int y) +int ship_loss_pct(struct unit *punit, int x, int y) { /* * If we are in a city or next to land, we have no chance of losing @@ -1339,28 +1336,46 @@ * we'd need to confirm that we can exist/move at the (x, y) * location we are given. */ - if (map_get_terrain(x, y) != T_OCEAN || is_coastline(x, y)) { + if (!unit_flag(punit, F_TRIREME) || map_get_terrain(x, y) != T_OCEAN + || is_coastline(x, y)) { return 0; } else { - return base_trireme_loss_pct(pplayer); + struct map_position mp; + mp.x = x; + mp.y = y; + return base_ship_loss_pct(punit, &mp); } } /************************************************************************** Triremes have a varying loss percentage. based on tech. Seafaring - reduces this to 25%, Navigation to 12.5%. The Lighthouse wonder - reduces this to 0. + reduces this to 25%, Navigation to 12.5%. The No_Sink_Deep improvement + effect (e.g. Lighthouse wonder) reduces this to 0. (Units that are not + Triremes always have a loss percentage of 0.) + mp should point to a map position, or be NULL if this is indeterminate. **************************************************************************/ -int base_trireme_loss_pct(struct player *pplayer) +int base_ship_loss_pct(struct unit *punit, struct map_position *mp) { - if (player_owns_active_wonder(pplayer, B_LIGHTHOUSE)) { + struct player *pplayer = unit_owner(punit); + + if (!unit_flag(punit, F_TRIREME)) { return 0; - } else if (player_knows_techs_with_flag(pplayer, TF_REDUCE_TRIREME_LOSS2)) { - return 12; - } else if (player_knows_techs_with_flag(pplayer, TF_REDUCE_TRIREME_LOSS1)) { - return 25; } else { - return 50; + struct eff_iter iter; + int losspct = 50; + if (player_knows_techs_with_flag(pplayer, TF_REDUCE_TRIREME_LOSS1)) { + losspct = 25; + } + if (player_knows_techs_with_flag(pplayer, TF_REDUCE_TRIREME_LOSS2)) { + losspct = 12; + } + eff_iterator_unit_init(&iter, punit->type, punit->type, pplayer, mp); + while (eff_iterator_next(&iter)) { + if (iter.imeff->type == EFT_NO_SINK_DEEP) { + return 0; + } + } + return losspct; } } @@ -1438,7 +1453,10 @@ punit->foul = FALSE; punit->fuel = unit_type(punit)->fuel; punit->hp = unit_type(punit)->hp; + punit->move_modifier = 0; + punit->hp_recover = 0; punit->moves_left = unit_move_rate(punit); + punit->moved = FALSE; punit->paradropped = FALSE; punit->connecting = FALSE; diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/unit.h freeciv-patched/common/unit.h --- freeciv-cvs/common/unit.h 2003-06-30 12:18:28.000000000 +0100 +++ freeciv-patched/common/unit.h 2003-06-30 12:17:55.000000000 +0100 @@ -126,6 +126,8 @@ int goto_dest_x, goto_dest_y; int activity_count; enum tile_special_type activity_target; + int hp_recover; /* hp to recover each turn (if -1, full recover) */ + int move_modifier; /* moves bonus from Lighthouse, Magellan etc. */ enum unit_focus_status focus_status; int ord_map, ord_city; /* ord_map and ord_city are the order index of this unit in tile.units @@ -216,6 +218,7 @@ bool is_air_unit(struct unit *punit); bool is_heli_unit(struct unit *punit); bool is_ground_unit(struct unit *punit); +void get_unit_classes(struct unit *punit, struct unit_classes *classes); bool can_unit_add_to_city (struct unit *punit); bool can_unit_build_city (struct unit *punit); bool can_unit_add_or_build_city (struct unit *punit); @@ -243,8 +246,8 @@ struct unit *is_non_attack_unit_tile(struct tile *ptile, struct player *pplayer); -int trireme_loss_pct(struct player *pplayer, int x, int y); -int base_trireme_loss_pct(struct player *pplayer); +int ship_loss_pct(struct unit *punit, int x, int y); +int base_ship_loss_pct(struct unit *punit, struct map_position *mp); bool is_my_zoc(struct player *unit_owner, int x0, int y0); bool unit_being_aggressive(struct unit *punit); diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/unittype.c freeciv-patched/common/unittype.c --- freeciv-cvs/common/unittype.c 2003-06-30 12:18:28.000000000 +0100 +++ freeciv-patched/common/unittype.c 2003-06-30 12:18:17.000000000 +0100 @@ -39,10 +39,10 @@ static const char *move_type_names[] = { "Land", "Sea", "Heli", "Air" }; -static const char *flag_names[] = { +static char flag_names[F_MAX][MAX_LEN_NAME] = { "TradeRoute" ,"HelpWonder", "Missile", "IgZOC", "NonMil", "IgTer", - "Carrier", "OneAttack", "Pikemen", "Horse", "IgWall", "FieldUnit", - "AEGIS", "Fighter", "Marines", "Partial_Invis", "Settlers", "Diplomat", + "Carrier", "OneAttack", "(unused)", "(unused)", "IgWall", "FieldUnit", + "(unused)", "Fighter", "Marines", "Partial_Invis", "Settlers", "Diplomat", "Trireme", "Nuclear", "Spy", "Transform", "Paratroopers", "Airbase", "Cities", "IgTired", "Missile_Carrier", "No_Land_Attack", "AddToCity", "Fanatic" @@ -54,14 +54,6 @@ "BarbarianBuild", "BarbarianBuildTech", "BarbarianLeader", "BarbarianSea", "BarbarianSeaTech" }; -static const char *unit_class_names[] = { - "Air", - "Helicopter", - "Land", - "Missile", - "Nuclear", - "Sea" -}; /************************************************************************** Returns 1 if the unit_type "exists" in this game, 0 otherwise. @@ -130,6 +122,39 @@ /************************************************************************** ... **************************************************************************/ +void get_unittype_classes(Unit_Type_id id, struct unit_classes *classes) +{ + struct unit_type *ut = get_unit_type(id); + + classes->move_type = ut->move_type; + classes->flags = ut->flags; +} + +/************************************************************************** +... +**************************************************************************/ +bool is_unit_class_in_set(Unit_Class_id id, struct unit_classes *classes) +{ + assert (id >= 0 && id <= UCL_LAST); + + if (id == UCL_LAST || !classes || classes->move_type == LAST_MOVING) { + return TRUE; + } else if (id < F_MAX) { + return BV_ISSET(classes->flags, id); + } else { + if (id - F_MAX == AIR_MOVING + && (BV_ISSET(classes->flags, F_NUCLEAR) + || BV_ISSET(classes->flags, F_MISSILE))) { + return FALSE; + } else { + return (classes->move_type == (id - F_MAX)); + } + } +} + +/************************************************************************** +... +**************************************************************************/ int utype_shield_cost(struct unit_type *ut, struct government *g) { if (government_has_flag(g, G_FANATIC_TROOPS) && @@ -168,7 +193,7 @@ **************************************************************************/ bool unit_type_flag(Unit_Type_id id, int flag) { - assert(flag>=0 && flag=0 && flagunit_cost_pct[i] / 100; + } + } + return val; +} + +/************************************************************************** ... **************************************************************************/ int unit_pop_value(Unit_Type_id id) @@ -235,18 +279,6 @@ } /************************************************************************** -... -**************************************************************************/ -const char *unit_class_name(Unit_Class_id id) -{ - if ((id >= 0) && (id < UCL_LAST)) { - return unit_class_names[id]; - } else { - return ""; - } -} - -/************************************************************************** Return a string with all the names of units with this flag Return NULL if no unit with this flag exists. The string must be free'd @@ -298,15 +330,22 @@ /************************************************************************** ... **************************************************************************/ -int can_upgrade_unittype(struct player *pplayer, Unit_Type_id id) +int can_upgrade_unittype(struct player *pplayer, Unit_Type_id id, bool leap) { Unit_Type_id best_upgrade = -1; if (!can_player_build_unit_direct(pplayer, id)) return -1; - while (unit_type_exists(id = unit_types[id].obsoleted_by)) + + if (leap) { + while (unit_type_exists(id = unit_types[id].obsoleted_by)) + if (can_player_build_unit_direct(pplayer, id)) + best_upgrade = id; + } else { + id = unit_types[id].obsoleted_by; if (can_player_build_unit_direct(pplayer, id)) best_upgrade = id; + } return best_upgrade; } @@ -359,39 +398,30 @@ } /************************************************************************** - Convert Unit_Class_id names to enum; case insensitive; - returns UCL_LAST if can't match. -**************************************************************************/ -Unit_Class_id unit_class_from_str(const char *s) -{ - Unit_Class_id i; - - assert(ARRAY_SIZE(unit_class_names) == UCL_LAST); - - for (i = 0; i < UCL_LAST; i++) { - if (mystrcasecmp(unit_class_names[i], s)==0) { - return i; - } - } - return UCL_LAST; -} - -/************************************************************************** Convert flag names to enum; case insensitive; - returns F_LAST if can't match. + returns F_MAX if can't match. **************************************************************************/ enum unit_flag_id unit_flag_from_str(const char *s) { enum unit_flag_id i; - assert(ARRAY_SIZE(flag_names) == F_LAST); + assert(ARRAY_SIZE(flag_names) == F_MAX); - for(i=0; i= F_LAST && id < game.num_unit_flags && id < F_MAX); + sz_strlcpy(flag_names[id], s); } /************************************************************************** @@ -414,8 +444,8 @@ /************************************************************************** Whether player can build given unit somewhere, -ignoring whether unit is obsolete and assuming the -player has a coastal city. +ignoring whether unit is obsolete, any special requirements (e.g. for +nuclear units) and assuming the player has a coastal city. **************************************************************************/ bool can_player_build_unit_direct(struct player *p, Unit_Type_id id) { @@ -424,8 +454,6 @@ if (!unit_type_exists(id)) return FALSE; - if (unit_type_flag(id, F_NUCLEAR) && game.global_wonders[B_MANHATTEN] == 0) - return FALSE; if (unit_type_flag(id, F_FANATIC) && !government_has_flag(get_gov_pplayer(p), G_FANATIC_TROOPS)) return FALSE; @@ -610,6 +638,9 @@ { struct unit_type *p = get_unit_type(id); + free(p->effect); + p->effect = NULL; + free(p->helptext); p->helptext = NULL; } diff -Nur -Xfreecivdiff.ignore freeciv-cvs/common/unittype.h freeciv-patched/common/unittype.h --- freeciv-cvs/common/unittype.h 2003-06-30 12:18:28.000000000 +0100 +++ freeciv-patched/common/unittype.h 2003-06-30 12:18:17.000000000 +0100 @@ -20,6 +20,7 @@ struct government; struct Sprite; /* opaque; client-gui specific */ struct unit; +struct impr_effect; typedef int Unit_Type_id; /* @@ -52,23 +53,10 @@ */ enum unit_move_type { - LAND_MOVING = 1, SEA_MOVING, HELI_MOVING, AIR_MOVING + LAND_MOVING = 1, SEA_MOVING, HELI_MOVING, AIR_MOVING, LAST_MOVING }; -/* Classes for unit types. - * (These must correspond to unit_class_names[] in unit.c.) - */ -enum unit_class_id { - UCL_AIR, - UCL_HELICOPTER, - UCL_LAND, - UCL_MISSILE, - UCL_NUCLEAR, - UCL_SEA, - UCL_LAST /* keep this last */ -}; - -typedef enum unit_class_id Unit_Class_id; +typedef int Unit_Class_id; /* Unit "special effects" flags: Note this is now an enumerated type, and not power-of-two integers @@ -148,6 +136,8 @@ }; #define L_MAX 64 +#define UCL_LAST (F_MAX + LAST_MOVING) + BV_DEFINE(bv_flags, F_MAX); BV_DEFINE(bv_roles, L_MAX); struct unit_type { @@ -187,9 +177,15 @@ int paratroopers_mr_req; int paratroopers_mr_sub; + struct impr_effect *effect; /* list; .type==EFT_LAST terminated */ + char *helptext; }; +struct unit_classes { + enum unit_move_type move_type; + bv_flags flags; +}; extern struct unit_type unit_types[U_LAST]; @@ -205,12 +201,14 @@ bool is_air_unittype(Unit_Type_id id); bool is_heli_unittype(Unit_Type_id id); bool is_ground_unittype(Unit_Type_id id); +void get_unittype_classes(Unit_Type_id id, struct unit_classes *classes); +bool is_unit_class_in_set(Unit_Class_id id, struct unit_classes *classes); int unit_value(Unit_Type_id id); +int unit_value_adjusted(Unit_Type_id id, struct city *pcity); int unit_pop_value(Unit_Type_id id); const char *unit_name(Unit_Type_id id); -const char *unit_class_name(Unit_Class_id id); const char *get_unit_name(Unit_Type_id id); const char *get_units_with_flag_string(int flag); @@ -220,15 +218,15 @@ int utype_happy_cost(struct unit_type *ut, struct government *g); int utype_gold_cost(struct unit_type *ut, struct government *g); -int can_upgrade_unittype(struct player *pplayer, Unit_Type_id id); +int can_upgrade_unittype(struct player *pplayer, Unit_Type_id id, bool leap); int unit_upgrade_price(const struct player * const pplayer, const Unit_Type_id from, const Unit_Type_id to); Unit_Type_id find_unit_type_by_name(const char *s); enum unit_move_type unit_move_type_from_str(const char *s); -Unit_Class_id unit_class_from_str(const char *s); enum unit_flag_id unit_flag_from_str(const char *s); +void unit_flag_set_name(enum unit_flag_id id, const char *s); enum unit_role_id unit_role_from_str(const char *s); bool can_player_build_unit_direct(struct player *p, Unit_Type_id id); diff -Nur -Xfreecivdiff.ignore freeciv-cvs/data/civ1/buildings.ruleset freeciv-patched/data/civ1/buildings.ruleset --- freeciv-cvs/data/civ1/buildings.ruleset 2003-06-30 12:18:27.000000000 +0100 +++ freeciv-patched/data/civ1/buildings.ruleset 2003-06-30 12:18:10.000000000 +0100 @@ -77,12 +77,9 @@ } sound = "b_aqueduct" sound_alt = "b_generic" -; FIXME: use this help text when gen-impr implemented... -; /* (ignore for gettext until fixed) -; helptext = _("\ -; Allows a city to grow larger than size 10.\ -; ") -; */ +helptext = _("\ +Allows a city to grow larger than size 10.\ +") [building_bank] name = _("Bank") @@ -126,17 +123,9 @@ upkeep = 0 sabotage = 100 effect = - { "type", "range", "aff_unit" - "Unit_Veteran", "City", "Air" - "Unit_Repair", "City", "Air" - "Unit_Veteran", "City", "Helicopter" - "Unit_Repair", "City", "Helicopter" - "Unit_Veteran", "City", "Land" - "Unit_Repair", "City", "Land" - "Unit_Veteran", "City", "Missile" - "Unit_Repair", "City", "Missile" - "Unit_Veteran", "City", "Sea" - "Unit_Repair", "City", "Sea" + { "type", "range" + "Unit_Veteran", "City" + "Unit_Repair", "City" } variant = 1 ; FIXME: remove when gen-impr obsoletes sound = "b_barracks_i" @@ -164,17 +153,9 @@ upkeep = 1 sabotage = 100 effect = - { "type", "range", "aff_unit" - "Unit_Veteran", "City", "Air" - "Unit_Repair", "City", "Air" - "Unit_Veteran", "City", "Helicopter" - "Unit_Repair", "City", "Helicopter" - "Unit_Veteran", "City", "Land" - "Unit_Repair", "City", "Land" - "Unit_Veteran", "City", "Missile" - "Unit_Repair", "City", "Missile" - "Unit_Veteran", "City", "Sea" - "Unit_Repair", "City", "Sea" + { "type", "range" + "Unit_Veteran", "City" + "Unit_Repair", "City" } sound = "b_barracks_ii" sound_alt = "b_generic" @@ -201,17 +182,9 @@ upkeep = 2 sabotage = 100 effect = - { "type", "range", "aff_unit" - "Unit_Veteran", "City", "Air" - "Unit_Repair", "City", "Air" - "Unit_Veteran", "City", "Helicopter" - "Unit_Repair", "City", "Helicopter" - "Unit_Veteran", "City", "Land" - "Unit_Repair", "City", "Land" - "Unit_Veteran", "City", "Missile" - "Unit_Repair", "City", "Missile" - "Unit_Veteran", "City", "Sea" - "Unit_Repair", "City", "Sea" + { "type", "range" + "Unit_Veteran", "City" + "Unit_Repair", "City" } sound = "b_barracks_iii" sound_alt = "b_generic" @@ -264,7 +237,7 @@ sabotage = 100 effect = { "type", "range", "amount", "aff_unit" - "Unit_Defend", "City", 300, "Helicopter" + "Unit_Defend", "City", 300, "Heli" "Unit_Defend", "City", 300, "Land" "Unit_Defend", "City", 300, "Sea" "Unit_No_Lose_Pop", "City", 0, "Land" @@ -345,9 +318,9 @@ sabotage = 100 effect = { "type", "range", "amount", "cond_gov" - "Reduce_Corrupt", "City", 50 + "Corrupt_Adj", "City", -50 "Make_Content", "City", 1, "Democracy" - "Revolt_Dist", "City", 50 + "Revolt_Dist_Adj", "City", -50 } sound = "b_courthouse" sound_alt = "b_generic" @@ -454,8 +427,8 @@ effect = { "type", "range", "amount", "cond_bldg" "Prod_Bonus", "City", 50, "Factory" - "Pollu_Adj_Prod", "City", 50 - "Pollu_Adj_Prod", "City", -50, "Recycling Center" + "Pollu_Prod_Adj", "City", -50 + "Pollu_Prod_Adj", "City", 50, "Recycling Center" } sound = "b_hydro_plant" sound_alt = "b_generic" @@ -539,7 +512,7 @@ sabotage = 100 effect = { "type", "range", "amount" - "Pollu_Set_Pop", "City", 0 + "Pollu_Pop_Pct", "City", 0 } sound = "b_mass_transit" sound_alt = "b_generic" @@ -592,8 +565,8 @@ effect = { "type", "range", "amount", "cond_bldg" "Prod_Bonus", "City", 50, "Factory" - "Pollu_Adj_Prod", "City", 50 - "Pollu_Adj_Prod", "City", -50, "Recycling Center" + "Pollu_Prod_Adj", "City", -50 + "Pollu_Prod_Adj", "City", 50, "Recycling Center" } sound = "b_nuclear_plant" sound_alt = "b_generic" @@ -773,7 +746,7 @@ sabotage = 100 effect = { "type", "range", "amount" - "Pollu_Adj_Prod", "City", 34 + "Pollu_Prod_Adj", "City", -66 } sound = "b_recycling_center" sound_alt = "b_generic" @@ -907,7 +880,7 @@ effect = { "type", "range", "amount", "cond_bldg" "Prod_Bonus", "City", 50, "Factory" - "Pollu_Set_Prod", "City", 0 + "Pollu_Prod_Pct", "City", 0 "Slow_Global_Warm", "World", 10 } sound = "b_solar_plant" @@ -1273,7 +1246,7 @@ sabotage = 0 effect = { "type", "range", "amount" - "Make_Happy", "Player", 1 + "Force_Happy", "Player", 1 } sound = "w_cure_for_cancer" sound_alt = "w_generic" @@ -1405,8 +1378,8 @@ sabotage = 0 effect = { "type", "range", "amount" - "Make_Happy", "Player", 1 - "Make_Happy", "City", 2 + "Force_Happy", "Player", 1 + "Force_Happy", "City", 2 } sound = "w_hanging_gardens" sound_alt = "w_generic" @@ -1435,8 +1408,8 @@ effect = { "type", "range", "amount", "cond_bldg" "Prod_Bonus", "Island", 50, "Factory" - "Pollu_Adj_Prod", "Island", 50 - "Pollu_Adj_Prod", "Island", -50, "Recycling Center" + "Pollu_Prod_Adj", "Island", -50 + "Pollu_Prod_Adj", "Island", 50, "Recycling Center" } variant = 1 ; FIXME: remove when gen-impr obsoletes sound = "w_hoover_dam" @@ -1489,7 +1462,7 @@ sabotage = 0 effect = { "type", "range", "amount" - "Make_Content", "Island", 2 + "Force_Content", "Island", 2 } variant = 1 ; FIXME: remove when gen-impr obsoletes sound = "w_js_bachs_cathedral" @@ -1623,8 +1596,9 @@ } sound = "w_manhattan_project" sound_alt = "w_generic" -;helptext is set in client/helpdata.c:helptext_wonder() -;helptext = +helptext = _("\ +Allows all players with knowledge of Rocketry to build Nuclear units.\ +") [building_marco_polos_embassy] name = _("Marco Polo's Embassy") @@ -1769,7 +1743,7 @@ sabotage = 0 effect = { "type", "range", "amount" - "Make_Content", "City", 99 + "Force_Content", "City", 99 } sound = "w_shakespeares_theatre" sound_alt = "w_generic" @@ -1819,9 +1793,9 @@ upkeep = 0 sabotage = 0 effect = - { "type", "range", "amount", "aff_unit" + { "type", "range", "amount", "aff_unit", "outside" + "Unit_Vet_Combat", "Player", 100, "Land", 1 "Unit_Veteran", "Player", 0, "Land" - "Unit_Vet_Combat", "Player", 100, "Land" } sound = "w_sun_tzus_war_academy" sound_alt = "w_generic" @@ -1879,8 +1853,9 @@ upkeep = 0 sabotage = 0 effect = - { "type", "range", "amount" - "Make_Content_Mil", "Player", 1 + { "type", "range", "amount", "cond_gov" + "Make_Content_Mil_Per", "Player", 1, "Republic" + "Make_Content_Mil_Per", "Player", 1, "Democracy" } variant = 1 ; FIXME: remove when gen-impr obsoletes sound = "w_womens_suffrage" diff -Nur -Xfreecivdiff.ignore freeciv-cvs/data/civ1/governments.ruleset freeciv-patched/data/civ1/governments.ruleset --- freeciv-cvs/data/civ1/governments.ruleset 2003-06-30 12:18:25.000000000 +0100 +++ freeciv-patched/data/civ1/governments.ruleset 2003-06-30 12:17:55.000000000 +0100 @@ -349,7 +349,7 @@ tech_req = "Communism" graphic = "gov.communism" graphic_alt = "-" -flags = "Build_Veteran_Diplomats" +flags = "-" hints = "Favors_Growth" subgoal = "Monarchy" diff -Nur -Xfreecivdiff.ignore freeciv-cvs/data/civ1/units.ruleset freeciv-patched/data/civ1/units.ruleset --- freeciv-cvs/data/civ1/units.ruleset 2003-06-30 12:18:25.000000000 +0100 +++ freeciv-patched/data/civ1/units.ruleset 2003-06-30 12:17:55.000000000 +0100 @@ -14,6 +14,9 @@ options="1.9" +[userflags] +names = "Horse" + ; Below: The individual units, one per section. ; ; The number can be variable, up to 200. @@ -284,8 +287,15 @@ uk_shield = 1 uk_food = 0 uk_gold = 0 -flags = "Pikemen" +flags = "" roles = "DefendGood", "FirstBuild" +effect = + { "type", "amount", "aff_unit" + "Unit_Defend", 200, "Horse" + } +helptext = _("\ +Gets double defense against units specified as 'mounted'.\ +") [unit_musketeers] name = _("Musketeers") @@ -628,6 +638,9 @@ uk_gold = 0 flags = "Horse" roles = "AttackFast" +helptext = _("\ +Counts as 'mounted' against certain defenders.\ +") [unit_knights] name = _("Knights") @@ -685,6 +698,9 @@ uk_gold = 0 flags = "Horse" roles = "AttackFast", "BarbarianBuildTech", "BarbarianSeaTech" +helptext = _("\ +Counts as 'mounted' against certain defenders.\ +") [unit_civ2_cavalry] name = "Civ2-Cavalry" ; no i18n @@ -1215,8 +1231,17 @@ uk_shield = 1 uk_food = 0 uk_gold = 0 -flags = "AEGIS" +flags = "" roles = "" +effect = + { "type", "amount", "aff_unit" + "Unit_Defend", 500, "Air" + "Unit_Defend", 500, "Heli" + "Unit_Defend", 500, "Missile" + } +helptext = _("\ +Gets quintuple defence against missiles, aeroplanes, and helicopters.\ +") [unit_battleship] name = _("Battleship") @@ -1441,6 +1466,10 @@ uk_gold = 0 flags = "Diplomat", "IgZOC", "NonMil" roles = "Explorer" +effect = + { "type", "cond_gov" + "Unit_Veteran", "Communism" + } helptext = _("\ - A Diplomat can establish embassies with other civilizations\ by moving into another player's city.\ @@ -1487,6 +1516,10 @@ uk_gold = 0 flags = "Diplomat", "IgZOC", "NonMil", "Spy" roles = "" +effect = + { "type", "cond_gov" + "Unit_Veteran", "Communism" + } [unit_caravan] name = _("Caravan") diff -Nur -Xfreecivdiff.ignore freeciv-cvs/data/civ2/buildings.ruleset freeciv-patched/data/civ2/buildings.ruleset --- freeciv-cvs/data/civ2/buildings.ruleset 2003-06-30 12:18:27.000000000 +0100 +++ freeciv-patched/data/civ2/buildings.ruleset 2003-06-30 12:18:10.000000000 +0100 @@ -77,13 +77,10 @@ } sound = "b_aqueduct" sound_alt = "b_generic" -; FIXME: use this help text when gen-impr implemented... -; /* (ignore for gettext until fixed) -; helptext = _("\ -; Allows a city to grow larger than size 8. A Sewer System is also\ -; required for a city to grow larger than size 12.\ -; ") -; */ +helptext = _("\ +Allows a city to grow larger than size 8. A Sewer System is also\ + required for a city to grow larger than size 12.\ +") [building_bank] name = _("Bank") @@ -328,9 +325,9 @@ sabotage = 100 effect = { "type", "range", "amount", "cond_gov" - "Reduce_Corrupt", "City", 50 + "Corrupt_Adj", "City", -50 "Make_Content", "City", 1, "Democracy" - "Revolt_Dist", "City", 50 + "Revolt_Dist_Adj", "City", -50 } sound = "b_courthouse" sound_alt = "b_generic" @@ -437,8 +434,8 @@ effect = { "type", "range", "amount", "cond_bldg" "Prod_Bonus", "City", 50, "Factory" - "Pollu_Adj_Prod", "City", 50 - "Pollu_Adj_Prod", "City", -50, "Recycling Center" + "Pollu_Prod_Adj", "City", -50 + "Pollu_Prod_Adj", "City", 50, "Recycling Center" } sound = "b_hydro_plant" sound_alt = "b_generic" @@ -524,7 +521,7 @@ sabotage = 100 effect = { "type", "range", "amount" - "Pollu_Set_Pop", "City", 0 + "Pollu_Pop_Pct", "City", 0 } sound = "b_mass_transit" sound_alt = "b_generic" @@ -577,8 +574,8 @@ effect = { "type", "range", "amount", "cond_bldg" "Prod_Bonus", "City", 50, "Factory" - "Pollu_Adj_Prod", "City", 50 - "Pollu_Adj_Prod", "City", -50, "Recycling Center" + "Pollu_Prod_Adj", "City", -50 + "Pollu_Prod_Adj", "City", 50, "Recycling Center" } sound = "b_nuclear_plant" sound_alt = "b_generic" @@ -667,7 +664,7 @@ ;spec_gate = equiv_range = "City" ;equiv_dupl = -;equiv_repl = +equiv_repl = "Women's Suffrage" obsolete_by = "None" is_wonder = 0 build_cost = 60 @@ -676,14 +673,14 @@ effect = { "type", "range", "amount", "cond_gov" "Make_Content_Mil", "City", 1, "Republic" - "Make_Content_Mil", "City", 2, "Democracy" + "Make_Content_Mil", "City", 1, "Democracy" } sound = "b_police_station" sound_alt = "b_generic" helptext = _("\ Reduces the unhappiness caused by military units outside the city\ - by 2 under Democracy and 1 under Republic. This improvement has no\ - effect under other governments.\ + by one, under Democracy and Republic. This improvement has no effect\ + under other governments.\ ") [building_port_facility] @@ -761,7 +758,7 @@ sabotage = 100 effect = { "type", "range", "amount" - "Pollu_Adj_Prod", "City", 34 + "Pollu_Prod_Adj", "City", -66 } sound = "b_recycling_center" sound_alt = "b_generic" @@ -873,13 +870,10 @@ } sound = "b_sewer_system" sound_alt = "b_generic" -; FIXME: use this help text when gen-impr implemented... -; /* (ignore for gettext until fixed) -; helptext = _("\ -; Allows a city to grow larger than size 12. An Aqueduct is first\ -; required for a city to grow larger than size 8.\ -; ") -; */ +helptext = _("\ +Allows a city to grow larger than size 12. An Aqueduct is first\ + required for a city to grow larger than size 8.\ +") [building_solar_plant] name = _("Solar Plant") @@ -898,7 +892,7 @@ effect = { "type", "range", "amount", "cond_bldg" "Prod_Bonus", "City", 50, "Factory" - "Pollu_Set_Prod", "City", 0 + "Pollu_Prod_Pct", "City", 0 "Slow_Global_Warm", "World", 10 } sound = "b_solar_plant" @@ -1266,7 +1260,7 @@ sabotage = 0 effect = { "type", "range", "amount" - "Make_Happy", "Player", 1 + "Force_Happy", "Player", 1 } sound = "w_cure_for_cancer" sound_alt = "w_generic" @@ -1398,8 +1392,8 @@ sabotage = 0 effect = { "type", "range", "amount" - "Make_Happy", "Player", 1 - "Make_Happy", "City", 2 + "Force_Happy", "Player", 1 + "Force_Happy", "City", 2 } sound = "w_hanging_gardens" sound_alt = "w_generic" @@ -1428,8 +1422,8 @@ effect = { "type", "range", "amount", "cond_bldg" "Prod_Bonus", "Player", 50, "Factory" - "Pollu_Adj_Prod", "Player", 50 - "Pollu_Adj_Prod", "Player", -50, "Recycling Center" + "Pollu_Prod_Adj", "Player", -50 + "Pollu_Prod_Adj", "Player", 50, "Recycling Center" } sound = "w_hoover_dam" sound_alt = "w_generic" @@ -1479,7 +1473,7 @@ sabotage = 0 effect = { "type", "range", "amount" - "Make_Content", "Player", 2 + "Force_Content", "Player", 2 } sound = "w_js_bachs_cathedral" sound_alt = "w_generic" @@ -1608,8 +1602,9 @@ } sound = "w_manhattan_project" sound_alt = "w_generic" -;helptext is set in client/helpdata.c:helptext_wonder() -;helptext = +helptext = _("\ +Allows all players with knowledge of Rocketry to build Nuclear units.\ +") [building_marco_polos_embassy] name = _("Marco Polo's Embassy") @@ -1756,7 +1751,7 @@ sabotage = 0 effect = { "type", "range", "amount" - "Make_Content", "City", 99 + "Force_Content", "City", 99 } sound = "w_shakespeares_theatre" sound_alt = "w_generic" @@ -1806,9 +1801,9 @@ upkeep = 0 sabotage = 0 effect = - { "type", "range", "amount", "aff_unit" + { "type", "range", "amount", "aff_unit", "outside" + "Unit_Vet_Combat", "Player", 100, "Land", 1 "Unit_Veteran", "Player", 0, "Land" - "Unit_Vet_Combat", "Player", 100, "Land" } sound = "w_sun_tzus_war_academy" sound_alt = "w_generic" @@ -1871,15 +1866,14 @@ effect = { "type", "range", "amount", "cond_gov" "Make_Content_Mil", "Player", 1, "Republic" - "Make_Content_Mil", "Player", 2, "Democracy" + "Make_Content_Mil", "Player", 1, "Democracy" } sound = "w_womens_suffrage" sound_alt = "w_generic" helptext = _("\ Counts as a Police Station in every city. (That is, for each city,\ - reduces unhappiness for military units outside the city by 2 under\ - Democracy and 1 under Republic. This wonder has no effect under\ - other governments.)\ + reduces the unhappiness caused by military units outside the city\ + by one, under Democracy and Republic.)\ ") [building_capitalization] diff -Nur -Xfreecivdiff.ignore freeciv-cvs/data/civ2/governments.ruleset freeciv-patched/data/civ2/governments.ruleset --- freeciv-cvs/data/civ2/governments.ruleset 2003-06-30 12:18:25.000000000 +0100 +++ freeciv-patched/data/civ2/governments.ruleset 2003-06-30 12:17:55.000000000 +0100 @@ -351,7 +351,7 @@ tech_req = "Communism" graphic = "gov.communism" graphic_alt = "-" -flags = "Build_Veteran_Diplomats", "Inspires_Partisans" +flags = "Inspires_Partisans" hints = "Favors_Growth" subgoal = "Monarchy" diff -Nur -Xfreecivdiff.ignore freeciv-cvs/data/civ2/units.ruleset freeciv-patched/data/civ2/units.ruleset --- freeciv-cvs/data/civ2/units.ruleset 2003-06-30 12:18:25.000000000 +0100 +++ freeciv-patched/data/civ2/units.ruleset 2003-06-30 12:17:55.000000000 +0100 @@ -13,6 +13,9 @@ description="Civ2 unit_type data for Freeciv (approximate)" options="1.9" +[userflags] +names = "Horse" + ; Below: The individual units, one per section. ; ; The number can be variable, up to 200. @@ -288,8 +291,15 @@ uk_shield = 1 uk_food = 0 uk_gold = 0 -flags = "Pikemen" +flags = "" roles = "DefendGood", "FirstBuild" +effect = + { "type", "amount", "aff_unit" + "Unit_Defend", 200, "Horse" + } +helptext = _("\ +Gets double defense against units specified as 'mounted'.\ +") [unit_musketeers] name = _("Musketeers") @@ -570,6 +580,9 @@ uk_gold = 0 flags = "Horse" roles = "AttackFast", "Hut", "Barbarian" +helptext = _("\ +Counts as 'mounted' against certain defenders.\ +") [unit_chariot] name = _("Chariot") @@ -598,6 +611,9 @@ uk_gold = 0 flags = "Horse" roles = "AttackFast", "Hut" +helptext = _("\ +Counts as 'mounted' against certain defenders.\ +") [unit_elephants] name = _("Elephants") @@ -654,6 +670,9 @@ uk_gold = 0 flags = "Horse" roles = "AttackFast" +helptext = _("\ +Counts as 'mounted' against certain defenders.\ +") [unit_knights] name = _("Knights") @@ -683,6 +702,9 @@ flags = "Horse" roles = "AttackFast", "HutTech", "BarbarianTech", "BarbarianBuildTech", "BarbarianSeaTech" +helptext = _("\ +Counts as 'mounted' against certain defenders.\ +") [unit_dragoons] name = _("Dragoons") @@ -711,6 +733,9 @@ uk_gold = 0 flags = "Horse" roles = "AttackFast", "BarbarianBuildTech", "BarbarianSeaTech" +helptext = _("\ +Counts as 'mounted' against certain defenders.\ +") [unit_cavalry] name = _("Cavalry") @@ -963,11 +988,18 @@ uk_gold = 0 flags = "FieldUnit", "OneAttack" roles = "" +effect = + { "type", "amount", "aff_unit" + "Unit_Defend", 50, "Fighter" + "Unit_Defend_Firepower", 50, "Fighter" + } helptext = _("\ The Helicopter is a very powerful unit, as it can both fly and\ conquer cities. Care must be exercised, because Helicopters lose a\ small amount of health for every turn not spent in a city, unless\ you have the United Nations wonder.\ +Note that Helicopters suffer halved defense strength and firepower\ + when defending against Fighters.\ ") [unit_stealth_fighter] @@ -1259,8 +1291,17 @@ uk_shield = 1 uk_food = 0 uk_gold = 0 -flags = "AEGIS" +flags = "" roles = "" +effect = + { "type", "amount", "aff_unit" + "Unit_Defend", 500, "Air" + "Unit_Defend", 500, "Heli" + "Unit_Defend", 500, "Missile" + } +helptext = _("\ +Gets quintuple defence against missiles, aeroplanes, and helicopters.\ +") [unit_battleship] name = _("Battleship") @@ -1490,6 +1531,10 @@ uk_gold = 0 flags = "Diplomat", "IgZOC", "NonMil" roles = "" +effect = + { "type", "cond_gov" + "Unit_Veteran", "Communism" + } helptext = _("\ - A Diplomat can establish embassies with other civilizations\ by moving into another player's city.\ @@ -1536,6 +1581,10 @@ uk_gold = 0 flags = "Diplomat", "IgZOC", "NonMil", "Spy" roles = "" +effect = + { "type", "cond_gov" + "Unit_Veteran", "Communism" + } helptext = _("\ A Spy is a full time professional and as such is much more\ skilled in the arts of espionage than her Diplomat predecessor.\ diff -Nur -Xfreecivdiff.ignore freeciv-cvs/data/default/buildings.ruleset freeciv-patched/data/default/buildings.ruleset --- freeciv-cvs/data/default/buildings.ruleset 2003-06-30 12:18:27.000000000 +0100 +++ freeciv-patched/data/default/buildings.ruleset 2003-06-30 12:18:10.000000000 +0100 @@ -128,13 +128,10 @@ } sound = "b_aqueduct" sound_alt = "b_generic" -; FIXME: use this help text when gen-impr implemented... -; /* (ignore for gettext until fixed) -; helptext = _("\ -; Allows a city to grow larger than size 8. A Sewer System is also\ -; required for a city to grow larger than size 12.\ -; ") -; */ +helptext = _("\ +Allows a city to grow larger than size 8. A Sewer System is also\ + required for a city to grow larger than size 12.\ +") [building_bank] name = _("Bank") @@ -399,9 +396,9 @@ sabotage = 100 effect = { "type", "range", "amount", "cond_gov" - "Reduce_Corrupt", "City", 50 + "Corrupt_Adj", "City", -50 "Make_Content", "City", 1, "Democracy" - "Revolt_Dist", "City", 50 + "Revolt_Dist_Adj", "City", -50 } sound = "b_courthouse" sound_alt = "b_generic" @@ -517,8 +514,8 @@ { "type", "range", "amount", "cond_bldg" "Prod_Bonus", "City", 25, "Factory" "Prod_Bonus", "City", 25, "Mfg. Plant" - "Pollu_Adj_Prod", "City", 50 - "Pollu_Adj_Prod", "City", -50, "Recycling Center" + "Pollu_Prod_Adj", "City", -50 + "Pollu_Prod_Adj", "City", 50, "Recycling Center" } sound = "b_hydro_plant" sound_alt = "b_generic" @@ -613,7 +610,7 @@ sabotage = 100 effect = { "type", "range", "amount" - "Pollu_Set_Pop", "City", 0 + "Pollu_Pop_Pct", "City", 0 } sound = "b_mass_transit" sound_alt = "b_generic" @@ -671,8 +668,8 @@ { "type", "range", "amount", "cond_bldg" "Prod_Bonus", "City", 25, "Factory" "Prod_Bonus", "City", 25, "Mfg. Plant" - "Pollu_Adj_Prod", "City", 50 - "Pollu_Adj_Prod", "City", -50, "Recycling Center" + "Pollu_Prod_Adj", "City", -50 + "Pollu_Prod_Adj", "City", 50, "Recycling Center" } sound = "b_nuclear_plant" sound_alt = "b_generic" @@ -769,7 +766,7 @@ ;spec_gate = equiv_range = "City" ;equiv_dupl = -;equiv_repl = +equiv_repl = "Women's Suffrage" obsolete_by = "None" is_wonder = 0 build_cost = 60 @@ -787,9 +784,6 @@ by 2 under Democracy and 1 under Republic. This improvement has no\ effect under other governments.\ ") -; NOTE: -; For Civ2 this should reduce unhappiness by one for *each* unit -; outside a city that is causing at least one unhappiness. [building_port_facility] name = _("Port Facility") @@ -876,7 +870,7 @@ sabotage = 100 effect = { "type", "range", "amount" - "Pollu_Adj_Prod", "City", 34 + "Pollu_Prod_Adj", "City", -66 } sound = "b_recycling_center" sound_alt = "b_generic" @@ -996,13 +990,10 @@ } sound = "b_sewer_system" sound_alt = "b_generic" -; FIXME: use this help text when gen-impr implemented... -; /* (ignore for gettext until fixed) -; helptext = _("\ -; Allows a city to grow larger than size 12. An Aqueduct is first\ -; required for a city to grow larger than size 8.\ -; ") -; */ +helptext = _("\ +Allows a city to grow larger than size 12. An Aqueduct is first\ + required for a city to grow larger than size 8.\ +") [building_solar_plant] name = _("Solar Plant") @@ -1024,7 +1015,7 @@ { "type", "range", "amount", "cond_bldg" "Prod_Bonus", "City", 25, "Factory" "Prod_Bonus", "City", 25, "Mfg. Plant" - "Pollu_Set_Prod", "City", 0 + "Pollu_Prod_Pct", "City", 0 "Slow_Global_Warm", "World", 10 } sound = "b_solar_plant" @@ -1422,7 +1413,7 @@ sabotage = 0 effect = { "type", "range", "amount" - "Make_Content", "Player", 1 + "Force_Content", "Player", 1 } sound = "w_cure_for_cancer" sound_alt = "w_generic" @@ -1566,8 +1557,8 @@ sabotage = 0 effect = { "type", "range", "amount" - "Make_Happy", "Player", 1 - "Make_Happy", "City", 2 + "Force_Happy", "Player", 1 + "Force_Happy", "City", 2 } sound = "w_hanging_gardens" sound_alt = "w_generic" @@ -1599,8 +1590,8 @@ { "type", "range", "amount", "cond_bldg" "Prod_Bonus", "Player", 25, "Factory" "Prod_Bonus", "Player", 25, "Mfg. Plant" - "Pollu_Adj_Prod", "Player", 50 - "Pollu_Adj_Prod", "Player", -50, "Recycling Center" + "Pollu_Prod_Adj", "Player", -50 + "Pollu_Prod_Adj", "Player", 50, "Recycling Center" } sound = "w_hoover_dam" sound_alt = "w_generic" @@ -1654,7 +1645,7 @@ sabotage = 0 effect = { "type", "range", "amount" - "Make_Content", "Player", 2 + "Force_Content", "Player", 2 } sound = "w_js_bachs_cathedral" sound_alt = "w_generic" @@ -1793,8 +1784,9 @@ } sound = "w_manhattan_project" sound_alt = "w_generic" -;helptext is set in client/helpdata.c:helptext_wonder() -;helptext = +helptext = _("\ +Allows all players with knowledge of Rocketry to build Nuclear units.\ +") [building_marco_polos_embassy] name = _("Marco Polo's Embassy") @@ -1953,7 +1945,7 @@ sabotage = 0 effect = { "type", "range", "amount" - "Make_Content", "City", 99 + "Force_Content", "City", 99 } sound = "w_shakespeares_theatre" sound_alt = "w_generic" @@ -2007,9 +1999,9 @@ upkeep = 0 sabotage = 0 effect = - { "type", "range", "amount", "aff_unit" + { "type", "range", "amount", "aff_unit", "outside" + "Unit_Vet_Combat", "Player", 100, "Land", 1 "Unit_Veteran", "Player", 0, "Land" - "Unit_Vet_Combat", "Player", 100, "Land" } sound = "w_sun_tzus_war_academy" sound_alt = "w_generic" @@ -2036,12 +2028,12 @@ upkeep = 0 sabotage = 0 effect = - { "type", "range", "amount", "aff_unit" - "Unit_Recover", "Player", 2, "Air" - "Unit_Recover", "Player", 2, "Helicopter" - "Unit_Recover", "Player", 2, "Land" - "Unit_Recover", "Player", 2, "Missile" - "Unit_Recover", "Player", 2, "Sea" + { "type", "range", "amount", "aff_unit", "outside" + "Unit_Recover", "Player", 2, "Air", 1 + "Unit_Recover", "Player", 2, "Heli", 1 + "Unit_Recover", "Player", 2, "Land", 1 + "Unit_Recover", "Player", 2, "Missile", 1 + "Unit_Recover", "Player", 2, "Sea", 1 } sound = "w_united_nations" sound_alt = "w_generic" diff -Nur -Xfreecivdiff.ignore freeciv-cvs/data/default/governments.ruleset freeciv-patched/data/default/governments.ruleset --- freeciv-cvs/data/default/governments.ruleset 2003-06-30 12:18:25.000000000 +0100 +++ freeciv-patched/data/default/governments.ruleset 2003-06-30 12:17:55.000000000 +0100 @@ -352,7 +352,7 @@ tech_req = "Communism" graphic = "gov.communism" graphic_alt = "-" -flags = "Build_Veteran_Diplomats", "Inspires_Partisans" +flags = "Inspires_Partisans" hints = "Favors_Growth" subgoal = "Monarchy" diff -Nur -Xfreecivdiff.ignore freeciv-cvs/data/default/units.ruleset freeciv-patched/data/default/units.ruleset --- freeciv-cvs/data/default/units.ruleset 2003-06-30 12:18:25.000000000 +0100 +++ freeciv-patched/data/default/units.ruleset 2003-06-30 12:17:55.000000000 +0100 @@ -21,6 +21,9 @@ description="Default unit_type data for Freeciv" options="1.9" +[userflags] +names = "Horse" + ; Below: The individual units, one per section. ; ; The number can be variable, up to 200. @@ -76,11 +79,11 @@ ; "Carrier" = can transport air and missile units, but not land units ; "Missile_Carrier" = can transport only missiles, but no aircraft or land units ; "OneAttack" = can only make a single attack, regardless of movement points -; "Pikemen" = double defence power against "Horse" flag units -; "Horse" = (no effect) -; "IgWall" = ignore effect of city walls +; "Pikemen" = (obsolete, used only by the AI) +; "Horse" = (obsolete, used only by the AI) +; "IgWall" = ignore Unit_Defend effects at City-range and greater ; "FieldUnit" = cause unhappiness even when not being aggressive -; "AEGIS" = fivefold increased defence against air attacks and missiles +; "AEGIS" = (obsolete, used only by the AI) ; "Fighter" = can attack air units (no other units can normally do this) ; "Marines" = (land only) can attack from transports ; "Partial_Invis" = visible only to adjancent units; does not hide transported @@ -359,8 +362,15 @@ uk_shield = 1 uk_food = 0 uk_gold = 0 -flags = "Pikemen" +flags = "" roles = "DefendGood", "FirstBuild" +effect = + { "type", "amount", "aff_unit" + "Unit_Defend", 200, "Horse" + } +helptext = _("\ +Gets double defense against units specified as 'mounted'.\ +") [unit_musketeers] name = _("Musketeers") @@ -635,6 +645,9 @@ uk_gold = 0 flags = "Horse" roles = "AttackFast", "Hut", "Barbarian" +helptext = _("\ +Counts as 'mounted' against certain defenders.\ +") [unit_chariot] name = _("Chariot") @@ -663,6 +676,9 @@ uk_gold = 0 flags = "Horse" roles = "AttackFast", "Hut" +helptext = _("\ +Counts as 'mounted' against certain defenders.\ +") [unit_elephants] name = _("Elephants") @@ -719,6 +735,9 @@ uk_gold = 0 flags = "Horse" roles = "AttackFast" +helptext = _("\ +Counts as 'mounted' against certain defenders.\ +") [unit_knights] name = _("Knights") @@ -748,6 +767,9 @@ flags = "Horse" roles = "AttackFast", "HutTech", "BarbarianTech", "BarbarianBuildTech", "BarbarianSeaTech" +helptext = _("\ +Counts as 'mounted' against certain defenders.\ +") [unit_dragoons] name = _("Dragoons") @@ -776,6 +798,9 @@ uk_gold = 0 flags = "Horse" roles = "AttackFast", "BarbarianBuildTech", "BarbarianSeaTech" +helptext = _("\ +Counts as 'mounted' against certain defenders.\ +") [unit_cavalry] name = _("Cavalry") @@ -1028,11 +1053,18 @@ uk_gold = 0 flags = "FieldUnit", "OneAttack" roles = "" +effect = + { "type", "amount", "aff_unit" + "Unit_Defend", 50, "Fighter" + "Unit_Defend_Firepower", 50, "Fighter" + } helptext = _("\ The Helicopter is a very powerful unit, as it can both fly and\ conquer cities. Care must be exercised, because Helicopters lose a\ small amount of health for every turn not spent in a city, unless\ you have the United Nations wonder.\ +Note that Helicopters suffer halved defense strength and firepower\ + when defending against Fighters.\ ") [unit_stealth_fighter] @@ -1324,8 +1356,17 @@ uk_shield = 1 uk_food = 0 uk_gold = 0 -flags = "AEGIS" +flags = "" roles = "DefendGood" +effect = + { "type", "amount", "aff_unit" + "Unit_Defend", 500, "Air" + "Unit_Defend", 500, "Heli" + "Unit_Defend", 500, "Missile" + } +helptext = _("\ +Gets quintuple defence against missiles, aeroplanes, and helicopters.\ +") [unit_battleship] name = _("Battleship") @@ -1555,6 +1596,10 @@ uk_gold = 0 flags = "Diplomat", "IgZOC", "NonMil" roles = "" +effect = + { "type", "cond_gov" + "Unit_Veteran", "Communism" + } helptext = _("\ - A Diplomat can establish embassies with other civilizations\ by moving into another player's city.\ @@ -1601,6 +1646,10 @@ uk_gold = 0 flags = "Diplomat", "IgZOC", "NonMil", "Spy" roles = "" +effect = + { "type", "cond_gov" + "Unit_Veteran", "Communism" + } helptext = _("\ A Spy is a full time professional and as such is much more\ skilled in the arts of espionage than her Diplomat predecessor.\ diff -Nur -Xfreecivdiff.ignore freeciv-cvs/doc/README.effects freeciv-patched/doc/README.effects --- freeciv-cvs/doc/README.effects 2003-06-30 12:18:28.000000000 +0100 +++ freeciv-patched/doc/README.effects 2003-06-30 12:18:17.000000000 +0100 @@ -39,6 +39,10 @@ "Food_Bonus" - food production is increased by AMOUNT percent (all Food_Bonus' are summed before being applied) +"Food_Pct" - multiplies total food production by AMOUNT percent + (applied after Food_Bonus; multiple effects are + multiplicative) + "Food_Inc_Tile" - each worked tile that is already producing some food produces AMOUNT additional food @@ -57,16 +61,35 @@ "Luxury_Bonus" - luxury production increased by AMOUNT percent (all Luxury_Bonus' are summed before being applied) -"Luxury_Pct" - increases luxury production by AMOUNT percent - -"Make_Content" - makes AMOUNT unhappy citizens content +"Luxury_Pct" - multiplies luxury production by AMOUNT percent + (applied after Luxury_Bonus; multiple effects + are multiplicative) -"Make_Content_Mil"- makes AMOUNT per unit of unhappy citizens caused +"Make_Content_Mil"- makes AMOUNT unhappy citizens caused by units outside of a city content -"Make_Content_Pct"- increase "Make_Content" by AMOUNT percent +"Make_Content_Mil_Per"- makes AMOUNT _per unit_ of unhappy citizens + caused by units outside of a city content + +"Make_Content" - makes AMOUNT unhappy citizens content + (applied *before* martial law and aggressive units; + all Make_Content's are summed before being applied) + +"Make_Content_Pct"- multiplies total "Make_Content" by AMOUNT percent + (multiple effects are multiplicative) "Make_Happy" - makes AMOUNT content citizens happy + (all Make_Happy's are summed before being applied) + +"Force_Content" - makes AMOUNT unhappy citizens content + (applied *after* martial law and aggressive units; + all Force_Content's are summed before being applied) + +"Force_Content_Pct"- increase total "Force_Content" by AMOUNT percent + (multiple effects are multiplicative) + +"Force_Happy" - makes AMOUNT content citizens happy + (all Force_Happy's are summed before being applied) "May_Declare_War"- allowed to declare war at least AMOUNT percent of the time @@ -77,27 +100,28 @@ "Nuke_Proof" - nuclear attacks will fail within AMOUNT distance -"Pollu_Adj" - multiplies pollution by AMOUNT percent +"Pollu_Adj" - pollution is increased by AMOUNT percent (all Pollu_Adj's are summed before being applied) -"Pollu_Adj_Pop" - multiplies pollution caused by population by AMOUNT - percent - (all Pollu_Adj_Pop's are summed before being applied) - -"Pollu_Adj_Prod"- multiplies pollution caused by shield production by - AMOUNT percent - (all Pollu_Adj_Prod's are summed before being applied) - -"Pollu_Set" - sets pollution to AMOUNT; override all other effects - (all Pollu_Set's are summed before being applied) - -"Pollu_Set_Pop" - sets pollution caused by population to AMOUNT; - override all other effects - (all Pollu_Set_Pop's are summed before being applied) - -"Pollu_Set_Prod"- sets pollution caused by shield production to AMOUNT; - override all other effects - (all Pollu_Set_Prod's are summed before being applied) +"Pollu_Pct" - multiplies pollution by AMOUNT percent + (applied after Pollu_Adj; multiple effects + are multiplicative) + +"Pollu_Pop_Adj" - pollution caused by population is increased by + AMOUNT percent (all Pollu_Pop_Adj's are summed + before being applied) + +"Pollu_Pop_Pct" - multiplies pollution caused by population by + AMOUNT percent (applied after Pollu_Pop_Adj; + multiple effects are multiplicative) + +"Pollu_Prod_Adj"- pollution caused by shield production is increased by + AMOUNT percent (all Pollu_Prod_Adj's are summed + before being applied) + +"Pollu_Prod_Pct"- multiplies pollution caused by shield production by + AMOUNT percent (applied after Pollu_Prod_Adj; + multiple effects are multiplicative) "Prod_Add_Tile" - each worked tile produces AMOUNT additional shield production @@ -105,6 +129,10 @@ "Prod_Bonus" - shield production is increased by AMOUNT percent (all Prod_Bonus' are summed before being applied) +"Prod_Pct" - multiplies total shield production by AMOUNT percent + (applied after Prod_Bonus; multiple effects are + multiplicative) + "Prod_Inc_Tile" - each worked tile that is already producing some shields produces AMOUNT additional shields @@ -113,21 +141,32 @@ "Prod_To_Gold" - convert production to gold at AMOUNT percent rate -"Reduce_Corrupt"- reduces corruption by AMOUNT percent +"Corrupt_Adj" - corruption is increased by AMOUNT percent + (all Corrupt_Adj's are summed before being applied) -"Reduce_Waste" - reduces waste by AMOUNT percent +"Corrupt_Pct" - multiplies total corruption by AMOUNT percent + (applied after Corrupt_Adj; multiple effects are + multiplicative) "Reveal_Cities" - make all city tiles known "Reveal_Map" - make entire map known -"Revolt_Dist" - multiplies effective distance to the capital by +"Revolt_Dist_Adj"- effective distance to the capital is increased by + AMOUNT percent for purpose of computing revolt cost + (all Revolt_Dist_Adj's are summed before being applied) + +"Revolt_Dist_Pct"- multiplies effective distance to the capital by AMOUNT percent for purpose of computing revolt cost + (applied after Revolt_Dist_Adj; multiple effects are + multiplicative) "Science_Bonus" - science research is increased by AMOUNT percent (all Science_Bonus' are summed before being applied) -"Science_Pct" - increases science research by AMOUNT percent +"Science_Pct" - multiplies total science by AMOUNT percent + (applied after Science_Bonus; multiple effects are + multiplicative) "Size_Unlimit" - cities not affected will not grow beyond AMOUNT @@ -150,13 +189,19 @@ "Tax_Bonus" - tax revenues are increased by AMOUNT percent (all Tax_Bonus' are summed before being applied) -"Tax_Pct" - increases tax revenues by AMOUNT percent +"Tax_Pct" - multiplies total tax by AMOUNT percent + (applied after Tax_Bonus; multiple effects are + multiplicative) "Trade_Add_Tile"- each worked tile produces AMOUNT additional trade "Trade_Bonus" - trade generated is increased by AMOUNT percent (all Trade_Bonus' are summed before being applied) +"Trade_Pct" - multiplies total trade by AMOUNT percent + (applied after Trade_Bonus; multiple effects are + multiplicative) + "Trade_Inc_Tile"- each worked tile that is already producing some trade produces AMOUNT additional trade @@ -166,7 +211,19 @@ "Trade_Route_Pct"- increases trade from trade routes by AMOUNT percent "Unit_Defend" - multiplies defense by AMOUNT percent against units - of class .aff_unit + of class .aff_unit (multiple effects are + multiplicative) + +"Unit_Attack" - multiplies attack by AMOUNT percent against units + of class .aff_unit (multiple effects are multiplicative) + +"Unit_Attack_Firepower"- multiplies firepower by AMOUNT percent + when attacking units of class .aff_unit (multiple + effects are multiplicative) + +"Unit_Defend_Firepower"- multiplies firepower by AMOUNT percent + when defending against units of class .aff_unit + (multiple effects are multiplicative) "Unit_Move" - adds AMOUNT of movement points to units of class .aff_unit @@ -187,6 +244,22 @@ "Unit_Veteran" - all units of class .aff_unit produced are veteran units +"Unit_Cost" - increase the cost of building a unit of class + .aff_unit in the city by AMOUNT percent + (multiple effects are additive) + +"Building_Cost" - increase the cost of building an improvement + in the city by AMOUNT percent + (multiple effects are additive) + +"Unit_Cost_Pct" - multiply the cost of building a unit of class + .aff_unit in the city by AMOUNT percent + (multiple effects are multiplicative) + +"Building_Cost_Pct"- multiply the cost of building an improvement + in the city by AMOUNT percent + (multiple effects are multiplicative) + "Upgrade_One_Step"- upgrade one obsolete unit per turn, stepping to each intermediate type; chance to upgrade each unit is AMOUNT percent @@ -213,7 +286,17 @@ (must be in the range -32767 to 32767) .survives = 1 if effect survives destruction (wonders only) (if unspecified, 0 (doesn't survive) is assumed) -.cond_bldg = must have this building in same city for effect +.outside = 1 if, for effects at greater than City-range, the effect + is active in all tiles, not just the city center. For tax + and science city bonuses, this means that the TOTAL science + output of all cities affected is subject to the bonus, + rather than the output of each city individually. + Applicable effects: Science_Bonus, Science_Pct, + Tax_Bonus, Tax_Pct, Unit_Defend, Unit_Attack, + Unit_Attack_Firepower, Unit_Defend_Firepower, + Unit_Recover, Unit_Vet_Combat, Upgrade_* + (if unspecified, 0 (city center only) is assumed) +.cond_bldg = effect is only active in cities that have this building (if unspecified, effect not conditional on building) .cond_nat = must be this nation for effect (if unspecified, effect not conditional on nation) @@ -221,10 +304,12 @@ (if unspecified, effect not conditional on government) .cond_adv = must know this advance for effect (if unspecified, effect not conditional on advance) -.cond_eff = must be affected by this effect for effect +.cond_eff = the improvement conferring the effect must in turn be + first affected by the given effect (if unspecified, effect not conditional on other effect) .aff_unit = affects only those units of this class; one of: - "Air", "Helicopter", "Land", "Missile", "Nuclear", "Sea" + "Air", "Heli", "Land", "Missile", "Nuclear", "Sea", + a unit flag, or a class defined in units.ruleset (if unspecified, may affect units of all classes) .aff_terr = affects only those squares of this terrain type (if unspecified, may affect all squares; if "None", diff -Nur -Xfreecivdiff.ignore freeciv-cvs/doc/README.impr-gen freeciv-patched/doc/README.impr-gen --- freeciv-cvs/doc/README.impr-gen 1970-01-01 01:00:00.000000000 +0100 +++ freeciv-patched/doc/README.impr-gen 2003-06-30 12:17:12.000000000 +0100 @@ -0,0 +1,162 @@ +---------------------------------------------------------------------- + Generalised improvements +---------------------------------------------------------------------- + Ben Webb, ben@bellatrix.pcl.ox.ac.uk + +Overview +-------- + Freeciv uses rulesets to configure much of its behaviour (see + README.rulesets). However, what is not immediately apparent is that + the ruleset for city improvements is not as flexible as it would + appear; you cannot change the total number of buildings, for example, + and even if you change the name or stated behaviour of a building, + it will still function as before (for example, the first building + will always act as an Airport, no matter what you do). The minor + differences in building behaviour between CivI and CivII are + accommodated by means of the "variant" option, which again has a + fixed effect in the Freeciv code. + + Generalised improvements aims to eliminate this problem, to allow + modpack and ruleset writers much greater flexibility. A major drive + in the code development was support for alternative Civ-like games + such as Alpha Centauri and Call To Power, which have radically + different sets of buildings and effects. + +Rulesets +-------- + With generalised improvements, you must specify what effect each + building has by filling in the "effect" field. + data/default/buildings.ruleset lists the valid values for this field, + and supplies suitable values for standard Freeciv behaviour, so it + should be straightforward to modify this for your own buildings. There + are, however, a number of caveats: + + - The AI does not yet understand generalised improvements, so will + get very upset if you change things too radically. However, this + will shortly be remedied. + + - Any effect, from any building, can have a "World" range - i.e. it + affects every city and every unit in the game. Players, however, can + usually only see the buildings in their own cities, plus any Wonders, + so World-range effects for ordinary buildings can cause confusion + (since a player won't know _why_ his/her cities are under a particular + effect). It is therefore recommeded that you only use World-range + effects for Wonders. + + - Island-range effects affect every city and unit on the same island as + the building which confers the effect. However, players' ideas of what + constitutes an "island" can differ from the server, since they only + know part of the map (for instance, say you have discovered the southern + tips of two islands, but don't yet know whether they are linked by a + land bridge). This can also cause confusion, and so Island-range effects + are best treated with caution until this difference between client and + server is remedied in Freeciv. + + - Some effects do not have a defined "range" (and the range field will be + ignored even if you supply it). These are Adv_Parasite, Have_Embassies, + Reveal_Cities, Reveal_Map, No_Anarchy, Any_Government and Give_Imm_Adv. + + - Not all effects are currently implemented: + - Enemy_Peaceful, Improve_Rep and May_Declare_War involve diplomacy, + which is not yet fully supported by Freeciv. + - Barb_Attack, Barb_Defend, Slow_Nuke_Winter, Slow_Global_Warm and + Trade_Route_Pct are not yet coded for, but are not used in standard + Freeciv rulesets anyway. + - I'm not sure what Capital_Exists was intended to do... + + - Although effects with the "survives" field set persist even if the Wonder + that originally conferred them is destroyed (e.g. the Apollo or Manhattan + Wonders in the default ruleset) the client may lose track of these effects + if the game is saved and then reloaded. + +Non-building effects +-------------------- + impr-gen effects were originally designed for implementing the behaviour of + city improvements. However, the system has proved flexible enough to be able + to describe the effects of other game entities as well. To date, units, + governments and nations can all use impr-gen effects. It is simple to add + these effects - just add an effects field to the relevant unit, government + or nation in the ruleset, in exactly the same way as it's done in + buildings.ruleset. Note, however, that: + + - "range" has no meaning for effects in units.ruleset. All effects are + assumed to act only on the unit itself. This may be expanded in future. + + - Nation and government effects can only specify the ranges "Player" and + "World". + + - Many effects make little sense in units.ruleset; you are largely + restricted to the various Unit_xxx effects. + +Code +---- + The bulk of the generalised improvements code is located in + common/improvement.c and common/game.c. This takes care of making lists + of all active effects, which can then be iterated over elsewhere in the + code when effects are required. All effects generated by a city's + improvements are stored in pcity->effects, which is a list of eff_city + structures. To save on having to iterate over all cities affected by a + given effect, effects that have Island-, Player-, or World- range are + also copied into lists of eff_global structures (which are derived from + eff_city, with a city ID added) which are stored as + pplayer->island_effects, pplayer->effects, and game.effects + respectively. (For the purpose of considering the "equiv_range" + behaviour of buildings, similar lists of buildings are also + maintained, as pplayer->island_improv, pplayer->improvements, and + game.improvements, respectively.) + + When buildings are built, moved, or destroyed, their effects can also + change. Since buildings can deactivate other buildings (e.g. the Great + Wall wonder renders all City Walls redundant in the default ruleset) + while effects can have dependencies, including depending on other + effects, whenever this occurs all effects are updated by calling + update_all_effects(). Since this scales as O(N**2) with the number + of cities in the game, this function (and all calls to it) are prime + targets for optimisation. The update_all_effects() function marks + buildings as active, obsolete (due to tech advances) or redundant + (replaced by another building, via. equiv_repl/equiv_dupl) and also + marks which effects of the active buildings are themselves active. + Effects that can also dependent on terrain/special type, or other + buildings, must be explicitly tested for activation when they are + used (but the effect iterators - see below - handle this automatically). + + Effects of buildings that do not yet exist (for example, during AI + calculations) will not be activated. Thus, the is_effect_activated, + is_city_affected and is_unit_terrain_affected functions must be + called in this case to ascertain whether an effect is active. (Bear + in mind, however, that this may still be inaccurate, as another + effect may depend on this effect, and so will not be properly + activated without a call to update_all_effects). + + A number of functions and macros are provided for iterating over all + active effects. The eff_iterator_impr_init() function is used for + considering all effects that affect a given building in a given city, + owned by a given player (although arguments can be ommitted, so for + example, it could be used to only look at Player-range effects by + passing a NULL city pointer). The impr_effects_iterate and + city_effects_iterate macros are useful shortcuts for calling these + functions (the city_ variants assume that the player is the owner of + the city, and that no specific building in the city is targetted). + Additional macros ending in _full allow the terrain type of the city + tile to be overridden with passed values, e.g. for considering worked + tiles outside the city centre. eff_iterator_unit_init() considers + effects that influence a unit at given map coordinates, which may or + may not be within a city, and the unit_effects_iterate macro is a + useful shortcut. + + Older Freeciv code simply checked the current city's list of buildings + and the global list of Wonders; for instance, when calculating defence + strength, it simply needed to test if the current city had the City Walls + building, and if the current player owned the Great Wall Wonder. This + is extremely fast when compared to iterating over a list of multiple + effects at several ranges, particularly as many such lookups are typically + performed for each city and unit during the game. Thus, the aim is to + reduce the number of effect iterations as much as possible, storing + intermediate data in various game structures. For instance, the + calculation of city bonuses, happiness, and pollution modifiers is all + rolled into one effect iteration (in common/city.c). + +Credits +------- + - Code: Ben Webb + - Bug fixes and ruleset tweaks: Davide Pagnin diff -Nur -Xfreecivdiff.ignore freeciv-cvs/doc/README.rulesets freeciv-patched/doc/README.rulesets --- freeciv-cvs/doc/README.rulesets 2003-06-30 12:18:26.000000000 +0100 +++ freeciv-patched/doc/README.rulesets 2003-06-30 12:17:12.000000000 +0100 @@ -108,6 +108,10 @@ "TODO Variants" section below for which variant effects which have been implemented so far. +- Please note: the rulesets for buildings (city improvements) are currently + in the process of being generalised. Thus, some or all of the documentation + here will be out of date - see README.impr-gen for more details. + - Units have a new field, "roles", which is like "flags", but determines which units are used in various circumstances of the game (rather than intrinsic properties of the unit). diff -Nur -Xfreecivdiff.ignore freeciv-cvs/server/citytools.c freeciv-patched/server/citytools.c --- freeciv-cvs/server/citytools.c 2003-06-30 12:18:29.000000000 +0100 +++ freeciv-patched/server/citytools.c 2003-06-30 12:18:03.000000000 +0100 @@ -510,15 +510,18 @@ **************************************************************************/ bool do_make_unit_veteran(struct city *pcity, Unit_Type_id id) { - if (unit_type_flag(id, F_DIPLOMAT)) - return government_has_flag(get_gov_pcity(pcity), G_BUILD_VETERAN_DIPLOMAT); + struct map_position mappos; - if (is_ground_unittype(id) || improvement_variant(B_BARRACKS)==1) - return city_got_barracks(pcity); - else if (is_water_unit(id)) - return (city_affected_by_wonder(pcity, B_LIGHTHOUSE) || city_got_building(pcity, B_PORT)); - else - return city_got_building(pcity, B_AIRPORT); + mappos.x = pcity->x; + mappos.y = pcity->y; + /* Do we have any veteran-making effects? */ + unittype_effects_iterate(iter, id, id, city_owner(pcity), &mappos) { + if (iter.imeff->type == EFT_UNIT_VETERAN) { + return TRUE; + } + } unittype_effects_iterate_end; + + return FALSE; } /************************************************************************** @@ -550,14 +553,14 @@ **************************************************************************/ bool wants_to_be_bigger(struct city *pcity) { - if (pcity->size < game.aqueduct_size) return TRUE; - if (city_got_building(pcity, B_SEWER)) return TRUE; - if (city_got_building(pcity, B_AQUEDUCT) - && pcity->size < game.sewer_size) return TRUE; - if (!pcity->is_building_unit) { - if (pcity->currently_building == B_SEWER && pcity->did_buy) return TRUE; - if (pcity->currently_building == B_AQUEDUCT && pcity->did_buy) return TRUE; - } /* saves a lot of stupid flipflops -- Syela */ + if (pcity->size_limit < 0) return TRUE; /* No size restriction */ + if (pcity->size < pcity->size_limit) return TRUE; + + /* saves a lot of stupid flipflops -- Syela */ + if (!pcity->is_building_unit + && pcity->currently_building == pcity->unlimit_impr + && pcity->did_buy) return TRUE; + return FALSE; } @@ -826,7 +829,7 @@ then restore the local improvement list - we need this to restore the global effects for the new city owner) */ built_impr_iterate(pcity, i) { - city_remove_improvement(pcity, i); + city_remove_improvement(pcity, i, FALSE); pcity->improvements[i] = I_ACTIVE; } built_impr_iterate_end; @@ -970,19 +973,21 @@ pcity->id = get_next_id_number(); idex_register_city(pcity); - if (!pplayer->capital) { - pplayer->capital = TRUE; - city_add_improvement(pcity, B_PALACE); - } - /* Before arranging workers to show unknown land */ map_unfog_pseudo_city_area(pplayer, x, y); map_set_city(x, y, pcity); city_list_insert(&pplayer->cities, pcity); + if (!pplayer->capital) { + pplayer->capital = TRUE; + city_add_improvement(pcity, B_PALACE); + update_all_effects(); + } add_city_to_minimap(x, y); + update_city_bonuses(pcity); + /* it is possible to build a city on a tile that is already worked * this will displace the worker on the newly-built city's tile -- Syela */ for (y_itr = 0; y_itr < CITY_MAP_SIZE; y_itr++) { @@ -1057,7 +1062,7 @@ built_impr_iterate(pcity, i) { effect_update = TRUE; - city_remove_improvement(pcity, i); + city_remove_improvement(pcity, i, TRUE); } built_impr_iterate_end; /* This is cutpasted with modifications from transfer_city_units. Yes, it is ugly. @@ -1557,6 +1562,9 @@ packet->pollution=pcity->pollution; packet->city_options=pcity->city_options; + + packet->buildflags = pcity->buildflags; + packet->upkeep_free = pcity->upkeep_free; packet->is_building_unit=pcity->is_building_unit; packet->currently_building=pcity->currently_building; @@ -1708,6 +1716,7 @@ if (!is_wonder(id)) { pplayer->economic.gold += improvement_value(id); building_lost(pcity, id); + update_all_effects(); } } @@ -1718,7 +1727,7 @@ { struct player *owner = city_owner(pcity); - city_remove_improvement(pcity,id); + city_remove_improvement(pcity, id, FALSE); if (id == B_PALACE && ((owner->spaceship.state == SSHIP_STARTED) || (owner->spaceship.state == SSHIP_LAUNCHED))) { diff -Nur -Xfreecivdiff.ignore freeciv-cvs/server/cityturn.c freeciv-patched/server/cityturn.c --- freeciv-cvs/server/cityturn.c 2003-06-30 12:18:27.000000000 +0100 +++ freeciv-patched/server/cityturn.c 2003-06-30 12:18:17.000000000 +0100 @@ -369,10 +369,8 @@ } } - can_grow = (city_got_building(pcity, B_AQUEDUCT) - || pcity->size < game.aqueduct_size) - && (city_got_building(pcity, B_SEWER) - || pcity->size < game.sewer_size); + can_grow = (pcity->size_limit < 0 + || pcity->size < pcity->size_limit); if ((turns_growth <= 0) && !city_celebrating(pcity) && can_grow) { notify_conn_ex(dest, pcity->x, pcity->y, @@ -402,16 +400,36 @@ } /************************************************************************** + N.B. We call update_tech for the total science output, rather than for + each city individually, to reduce rounding error when applying science + bonuses. +***************************************************************************/ +static void update_all_tech(struct player *pplayer) +{ + int current_output = total_science_output(pplayer); + + update_tech(pplayer, current_output); +} + +/************************************************************************** ... **************************************************************************/ void update_city_activities(struct player *pplayer) { - int gold; + int gold, goldmade; gold=pplayer->economic.gold; pplayer->got_tech = FALSE; city_list_iterate(pplayer->cities, pcity) update_city_activity(pplayer, pcity); city_list_iterate_end; + + /* Apply impr. gen. global tax bonuses */ + goldmade = pplayer->economic.gold - gold; + goldmade = goldmade * pplayer->tax_bonus / 100; + goldmade = goldmade * game.tax_bonus / 100; + pplayer->economic.gold = gold + goldmade; + + update_all_tech(pplayer); pplayer->ai.prev_gold = gold; if (gold-(gold-pplayer->economic.gold)*3<0) { notify_player_ex(pplayer, -1, -1, E_LOW_ON_FUNDS, @@ -471,44 +489,26 @@ { struct player *powner = city_owner(pcity); bool have_square; - bool has_granary = city_got_effect(pcity, B_GRANARY); bool rapture_grow = city_rapture_grow(pcity); /* check before size increase! */ int new_food; - if (!city_got_building(pcity, B_AQUEDUCT) - && pcity->size>=game.aqueduct_size) {/* need aqueduct */ - if (!pcity->is_building_unit && pcity->currently_building == B_AQUEDUCT) { + /* Check for needed aqueduct/sewer system/etc. */ + if (pcity->size_limit >= 0 && pcity->size >= pcity->size_limit) { + if (!pcity->is_building_unit + && pcity->currently_building == pcity->unlimit_impr) { notify_player_ex(powner, pcity->x, pcity->y, E_CITY_AQ_BUILDING, _("Game: %s needs %s (being built) " "to grow any further."), pcity->name, - improvement_types[B_AQUEDUCT].name); + get_improvement_name(pcity->unlimit_impr)); } else { notify_player_ex(powner, pcity->x, pcity->y, E_CITY_AQUEDUCT, _("Game: %s needs %s to grow any further."), - pcity->name, improvement_types[B_AQUEDUCT].name); + pcity->name, get_improvement_name(pcity->unlimit_impr)); } /* Granary can only hold so much */ new_food = (city_granary_size(pcity->size) * - (100 - game.aqueductloss / (1 + (has_granary ? 1 : 0)))) / 100; - pcity->food_stock = MIN(pcity->food_stock, new_food); - return; - } - - if (!city_got_building(pcity, B_SEWER) - && pcity->size>=game.sewer_size) {/* need sewer */ - if (!pcity->is_building_unit && pcity->currently_building == B_SEWER) { - notify_player_ex(powner, pcity->x, pcity->y, E_CITY_AQ_BUILDING, - _("Game: %s needs %s (being built) " - "to grow any further."), pcity->name, - improvement_types[B_SEWER].name); - } else { - notify_player_ex(powner, pcity->x, pcity->y, E_CITY_AQUEDUCT, - _("Game: %s needs %s to grow any further."), - pcity->name, improvement_types[B_SEWER].name); - } - /* Granary can only hold so much */ - new_food = (city_granary_size(pcity->size) * - (100 - game.aqueductloss / (1 + (has_granary ? 1 : 0)))) / 100; + (100 - game.aqueductloss * + (100 - pcity->growth_food) / 100)) / 100; pcity->food_stock = MIN(pcity->food_stock, new_food); return; } @@ -518,10 +518,7 @@ if (rapture_grow) { new_food = city_granary_size(pcity->size); } else { - if (has_granary) - new_food = city_granary_size(pcity->size) / 2; - else - new_food = 0; + new_food = city_granary_size(pcity->size) * pcity->growth_food / 100; } pcity->food_stock = MIN(pcity->food_stock, new_food); @@ -588,10 +585,8 @@ pcity->name, utname); gamelog(GAMELOG_UNITFS, _("%s lose %s (famine)"), get_nation_name_plural(city_owner(pcity)->nation), utname); - if (city_got_effect(pcity, B_GRANARY)) - pcity->food_stock=city_granary_size(pcity->size)/2; - else - pcity->food_stock=0; + pcity->food_stock = city_granary_size(pcity->size) * + pcity->growth_food / 100; return; } } @@ -599,10 +594,8 @@ notify_player_ex(city_owner(pcity), pcity->x, pcity->y, E_CITY_FAMINE, _("Game: Famine causes population loss in %s."), pcity->name); - if (city_got_effect(pcity, B_GRANARY)) - pcity->food_stock = city_granary_size(pcity->size - 1) / 2; - else - pcity->food_stock = 0; + pcity->food_stock = city_granary_size(pcity->size - 1) * + pcity->growth_food / 100; city_reduce_size(pcity, 1); } } @@ -912,11 +905,47 @@ } /************************************************************************** + Gives immediate tech advances if we've just built a city improvement + that confers the Give_Imm_Adv effect (e.g. Darwin's Voyage) +**************************************************************************/ +static void check_boost_research(struct city *pcity, struct player *pplayer) +{ + int numadv = 0; + + impr_effects_iterate(iter, B_LAST, pcity, NULL) { + if (iter.imeff->type == EFT_GIVE_IMM_ADV + && eff_iterator_get_improvement(&iter) == pcity->currently_building) { + numadv = iter.imeff->amount; + break; + } + } impr_effects_iterate_end; + + if (numadv) { + notify_player(pplayer, PL_("Game: %s boosts research; you gain %d " + "immediate advance.", "Game: %s boosts research; you " + "gain %d immediate advances.", numadv), + get_improvement_name(pcity->currently_building), + numadv); + while (numadv) { + do_free_cost(pplayer); + notify_embassies(pplayer, NULL, + _("Game: The %s have acquired %s from %s."), + get_nation_name_plural(pplayer->nation), + get_tech_name(pplayer, pplayer->research.researching), + get_improvement_name(pcity->currently_building)); + found_new_tech(pplayer, pplayer->research.researching, TRUE, TRUE); + numadv--; + } + } +} + +/************************************************************************** ... **************************************************************************/ static bool city_build_building(struct player *pplayer, struct city *pcity) { bool space_part; + int imp_value; if (pcity->currently_building == B_CAPITAL) { assert(pcity->shield_surplus >= 0); @@ -936,11 +965,13 @@ currently_building)); return TRUE; } - if (pcity->shield_stock >= improvement_value(pcity->currently_building)) { + + imp_value = improvement_value_adjusted(pcity->currently_building, pcity); + if (pcity->shield_stock >= imp_value) { if (pcity->currently_building == B_PALACE) { city_list_iterate(pplayer->cities, palace) if (city_got_building(palace, B_PALACE)) { - city_remove_improvement(palace, B_PALACE); + city_remove_improvement(palace, B_PALACE, FALSE); break; } city_list_iterate_end; @@ -957,9 +988,8 @@ space_part = FALSE; city_add_improvement(pcity, pcity->currently_building); } - pcity->before_change_shields -= - improvement_value(pcity->currently_building); - pcity->shield_stock -= improvement_value(pcity->currently_building); + pcity->before_change_shields -= imp_value; + pcity->shield_stock -= imp_value; pcity->turn_last_built = game.year; /* to eliminate micromanagement */ if (is_wonder(pcity->currently_building)) { @@ -984,31 +1014,8 @@ _("Game: %s has finished building %s."), pcity->name, improvement_types[pcity->currently_building].name); - if (pcity->currently_building == B_DARWIN) { - Tech_Type_id first, second; - char buffer[200]; - - notify_player(pplayer, _("Game: %s boosts research, " - "you gain 2 immediate advances."), - improvement_types[B_DARWIN].name); + check_boost_research(pcity, pplayer); - do_free_cost(pplayer); - first = pplayer->research.researching; - found_new_tech(pplayer, pplayer->research.researching, TRUE, TRUE); - - do_free_cost(pplayer); - second = pplayer->research.researching; - found_new_tech(pplayer, pplayer->research.researching, TRUE, TRUE); - - (void) mystrlcpy(buffer, get_tech_name(pplayer, first), - sizeof(buffer)); - - notify_embassies(pplayer, NULL, - _("Game: The %s have acquired %s and %s from %s."), - get_nation_name_plural(pplayer->nation), buffer, - get_tech_name(pplayer, second), - improvement_types[B_DARWIN].name); - } if (space_part && pplayer->spaceship.state == SSHIP_NONE) { notify_player_ex(NULL, pcity->x, pcity->y, E_SPACESHIP, _("Game: The %s have started " @@ -1042,9 +1049,13 @@ **************************************************************************/ static bool city_build_unit(struct player *pplayer, struct city *pcity) { + int unit_val; + upgrade_unit_prod(pcity); - if (pcity->shield_stock >= unit_value(pcity->currently_building)) { + unit_val = unit_value_adjusted(pcity->currently_building, pcity); + + if (pcity->shield_stock >= unit_val) { int pop_cost = unit_pop_value(pcity->currently_building); /* Should we disband the city? -- Massimo */ @@ -1077,8 +1088,8 @@ /* to eliminate micromanagement, we only subtract the unit's cost */ - pcity->before_change_shields -= unit_value(pcity->currently_building); - pcity->shield_stock -= unit_value(pcity->currently_building); + pcity->before_change_shields -= unit_val; + pcity->shield_stock -= unit_val; notify_player_ex(pplayer, pcity->x, pcity->y, E_UNIT_BUILD, _("Game: %s is finished building %s."), @@ -1320,11 +1331,6 @@ pcity->did_sell=FALSE; pcity->did_buy = FALSE; - if (city_got_building(pcity, B_AIRPORT)) - pcity->airlift=TRUE; - else - pcity->airlift=FALSE; - update_tech(pplayer, pcity->science_total); pplayer->economic.gold+=pcity->tax_total; pay_for_buildings(pplayer, pcity); diff -Nur -Xfreecivdiff.ignore freeciv-cvs/server/diplomats.c freeciv-patched/server/diplomats.c --- freeciv-cvs/server/diplomats.c 2003-06-30 12:18:26.000000000 +0100 +++ freeciv-patched/server/diplomats.c 2003-06-30 12:17:26.000000000 +0100 @@ -827,7 +827,8 @@ - Check for infiltration success. Our saboteur may not survive this. - Check for basic success. Again, our saboteur may not survive this. - Determine target, given arguments and constraints. - - If specified, city walls and anything in a capital are 50% likely to fail. + - If specified, city walls and anything in a capital are 50% likely to + fail (with default rulesets, due to Spy_Resistant improvement effects). - Do sabotage! - The saboteur may be captured and executed, or escape to its home town. @@ -838,7 +839,6 @@ struct player *cplayer; int count, which, target; const char *prod; - struct city *capital; /* Fetch target city's player. Sanity checks. */ if (!pcity) @@ -992,12 +992,19 @@ /* * One last chance to get caught: - * If target was specified, and it is in the capital or are - * City Walls, then there is a 50% chance of getting caught. + * If target was specified, we check for any active Spy_Resistant + * effects (e.g. from the Palace or City Walls) which define the + * chance of getting caught. */ - capital = find_palace (city_owner (pcity)); - if ((pcity == capital) || (improvement == B_CITY)) { - if (myrand (2) == 1) { + if (improvement >= 0 && improvement != B_LAST) { + int failchance = 0; + impr_effects_iterate(iter, improvement, pcity, cplayer) { + if (iter.imeff->type == EFT_SPY_RESISTANT) { + failchance += iter.imeff->amount; + } + } impr_effects_iterate_end; + + if (myrand(100) < failchance) { /* Caught! */ notify_player_ex(pplayer, pcity->x, pcity->y, E_MY_DIPLOMAT_FAILED, _("Game: Your %s was caught in the attempt" diff -Nur -Xfreecivdiff.ignore freeciv-cvs/server/gamehand.c freeciv-patched/server/gamehand.c --- freeciv-cvs/server/gamehand.c 2003-06-30 12:18:29.000000000 +0100 +++ freeciv-patched/server/gamehand.c 2003-06-30 12:17:47.000000000 +0100 @@ -154,6 +154,12 @@ /* Initialise list of improvements with world-wide equiv_range */ improvement_status_init(game.improvements, ARRAY_SIZE(game.improvements)); + + /* Free vector of effects with world-wide range. */ + geff_vector_free(&game.effects); + + /* Free vector of destroyed effects */ + ceff_vector_free(&game.destroyed_effects); } /************************************************************************** @@ -242,6 +248,10 @@ ginfo.global_advances[i] = game.global_advances[i]; for (i = 0; i < B_LAST /*game.num_impr_types */ ; i++) ginfo.global_wonders[i] = game.global_wonders[i]; + for (i = 0; i < B_LAST; i++) { + ginfo.destroyed_owner[i] = game.destroyed_owner[i] ? + game.destroyed_owner[i]->player_no : -1; + } /* the following values are computed every time a packet_game_info packet is created */ if (game.timeout != 0) { diff -Nur -Xfreecivdiff.ignore freeciv-cvs/server/gotohand.c freeciv-patched/server/gotohand.c --- freeciv-cvs/server/gotohand.c 2003-06-30 12:18:26.000000000 +0100 +++ freeciv-patched/server/gotohand.c 2003-06-30 12:17:19.000000000 +0100 @@ -705,8 +705,7 @@ && !same_pos(x1, y1, dest_x, dest_y)) { continue; } - else if (unit_flag(punit, F_TRIREME) - && trireme_loss_pct(unit_owner(punit), x1, y1) > 0) { + else if (ship_loss_pct(punit, x1, y1) > 0) { move_cost = 2*SINGLE_MOVE+1; } else { move_cost = SINGLE_MOVE; diff -Nur -Xfreecivdiff.ignore freeciv-cvs/server/plrhand.c freeciv-patched/server/plrhand.c --- freeciv-cvs/server/plrhand.c 2003-06-30 12:18:27.000000000 +0100 +++ freeciv-patched/server/plrhand.c 2003-06-30 12:18:03.000000000 +0100 @@ -120,34 +120,41 @@ } /************************************************************************** -... + Grants techs that other players have researched, as a result of owning + an active Great Library Wonder, or other building that confers the + Adv_Parasite effect **************************************************************************/ void great_library(struct player *pplayer) { - int i; - if (wonder_obsolete(B_GREAT)) - return; - if (find_city_wonder(B_GREAT)) { - if (pplayer->player_no==find_city_wonder(B_GREAT)->owner) { - for (i=0;i=2) { - notify_player_ex(pplayer, -1, -1, E_TECH_GAIN, - _("Game: %s acquired from The Great Library!"), - advances[i].name); - gamelog(GAMELOG_TECH, _("%s discover %s (Library)"), - get_nation_name_plural(pplayer->nation), advances[i].name); - notify_embassies(pplayer, NULL, - _("Game: The %s have acquired %s" - " from the Great Library."), - get_nation_name_plural(pplayer->nation), - advances[i].name); - - do_free_cost(pplayer); - found_new_tech(pplayer, i, FALSE, FALSE); - break; - } + int i, playreq = 0; + Impr_Type_id impr = B_LAST; + + impr_effects_iterate(iter, B_LAST, NULL, pplayer) { + if (iter.imeff->type == EFT_ADV_PARASITE + && (playreq == 0 || iter.imeff->amount < playreq)) { + playreq = iter.imeff->amount; + impr = eff_iterator_get_improvement(&iter); + } + } impr_effects_iterate_end; + + if (playreq > 0 && impr != B_LAST) { + for (i = 0; i < game.num_tech_types; i++) { + if (get_invention(pplayer, i) != TECH_KNOWN + && tech_is_available(pplayer, i) + && game.global_advances[i] >= playreq) { + notify_player_ex(pplayer, -1, -1, E_TECH_GAIN, + _("Game: %s acquired from %s!"), + advances[i].name, get_improvement_name(impr)); + gamelog(GAMELOG_TECH, _("%s discover %s (Library)"), + get_nation_name_plural(pplayer->nation), advances[i].name); + notify_embassies(pplayer, NULL, + _("Game: The %s have acquired %s from %s."), + get_nation_name_plural(pplayer->nation), + advances[i].name, get_improvement_name(impr)); + + do_free_cost(pplayer); + found_new_tech(pplayer, i, FALSE, FALSE); + break; } } } @@ -172,6 +179,34 @@ } /************************************************************************** + Called when a player is eliminated; moves all of the player's destroyed + effects to the global destroyed effects list. +**************************************************************************/ +static void handle_player_destroyed_effects(struct player *pplayer) +{ + struct eff_city *plrceff, *gameceff; + struct ceff_vector *plrceffs, *gameceffs; + int i, needupdate = FALSE; + + plrceffs = &pplayer->destroyed_effects; + gameceffs = &game.destroyed_effects; + + for (i = 0; i < ceff_vector_size(plrceffs); ++i) { + plrceff = ceff_vector_get(plrceffs, i); + if (plrceff->impr < B_LAST) { + gameceff = append_ceff(gameceffs); + gameceff->impr = plrceff->impr; + gameceff->active = 0; + needupdate = TRUE; + freelog(LOG_DEBUG, "Effects of %s moved to global destroyed effects list", + get_improvement_name(plrceff->impr)); + game.destroyed_owner[plrceff->impr] = NULL; + } + } + if (needupdate) update_all_effects(); +} + +/************************************************************************** ... **************************************************************************/ void update_player_aliveness(struct player *pplayer) @@ -183,6 +218,7 @@ notify_player_ex(pplayer, -1, -1, E_GAME_END, _("Game: You are dead!")); pplayer->is_alive = FALSE; + handle_player_destroyed_effects(pplayer); if( !is_barbarian(pplayer) ) { notify_player_ex(NULL, -1, -1, E_DESTROYED, _("Game: The %s are no more!"), @@ -738,6 +774,9 @@ } check_player_government_rates(pplayer); + player_update_global_effects(pplayer); + update_all_effects(); + global_city_refresh(pplayer); send_player_info(pplayer, pplayer); } @@ -763,8 +802,10 @@ check_player_government_rates(pplayer); global_city_refresh(pplayer); send_player_info(pplayer, pplayer); - if (player_owns_active_govchange_wonder(pplayer)) - pplayer->revolution=1; + /* Skip revolution period if we have the relevant effect */ + impr_effects_iterate(iter, B_LAST, NULL, pplayer) { + if (iter.imeff->type == EFT_NO_ANARCHY) pplayer->revolution = 1; + } impr_effects_iterate_end; } /************************************************************************** diff -Nur -Xfreecivdiff.ignore freeciv-cvs/server/ruleset.c freeciv-patched/server/ruleset.c --- freeciv-cvs/server/ruleset.c 2003-06-30 12:18:28.000000000 +0100 +++ freeciv-patched/server/ruleset.c 2003-06-30 12:18:10.000000000 +0100 @@ -74,6 +74,7 @@ static void load_tech_names(struct section_file *file); static void load_unit_names(struct section_file *file); +static void load_unit_userflags(struct section_file *file); static void load_building_names(struct section_file *file); static void load_government_names(struct section_file *file); static void load_terrain_names(struct section_file *file); @@ -661,6 +662,242 @@ } /************************************************************************** + ... +**************************************************************************/ +static void load_unit_userflags(struct section_file *file) +{ + char **slist; + int nval, i; + + game.num_unit_flags = F_LAST; + slist = secfile_lookup_str_vec(file, &nval, "userflags.names"); + if (nval > F_MAX - F_LAST) { + freelog(LOG_ERROR, "Too many user-specified unit flags (%d, max %d)", + nval, F_MAX - F_LAST); + exit(EXIT_FAILURE); + } + + game.num_unit_flags = F_LAST + nval; + for (i = 0; i < nval; ++i) { + unit_flag_set_name(F_LAST + i, slist[i]); + } + free(slist); +} + +/************************************************************************** +... +**************************************************************************/ +static struct impr_effect *load_effects(const char *secname, const char *name, + struct section_file *file, + bool unit_effects, bool all_survive) +{ + int count, j, k; + char *item; + struct impr_effect *effects, *e; + const char *filename = secfile_filename(file); + bool problem; + + for (count = 0; + secfile_lookup_str_default(file, NULL, "%s.effect%d.type", secname, + count); count++) { + /* nothing */ + } + + if (count > MAX_EFFECTS) { + freelog(LOG_FATAL, "For %s maximum number of effects (%d) exceeded", + name, MAX_EFFECTS); + exit(EXIT_FAILURE); + } + + effects = fc_malloc((count + 1) * sizeof(struct impr_effect)); + k = 0; + for (j = 0; j < count; j++) { + e = &effects[k]; + problem = FALSE; + + item = secfile_lookup_str(file, "%s.effect%d.type", secname, j); + e->type = effect_type_from_str(item); + if (e->type == EFT_LAST) { + freelog(LOG_ERROR, + "for %s effect[%d].type couldn't match type \"%s\" (%s)", + name, j, item, filename); + problem = TRUE; + } + + item = secfile_lookup_str_default(file, "None", + "%s.effect%d.range", secname, j); + e->range = effect_range_from_str(item); + if (e->range == EFR_LAST) { + freelog(LOG_ERROR, + "for %s effect[%d].range couldn't match range \"%s\" (%s)", + name, j, item, filename); + problem = TRUE; + } + if (unit_effects) { + if (e->range != EFR_NONE) { + freelog(LOG_ERROR, + "for %s effect ranges are not currently supported (%s)", + name, filename); + problem = TRUE; + } + } + + /* For some effects the range doesn't really matter - so we fix it + to the most convenient range for the code to deal with */ + switch(e->type) { + case EFT_ADV_PARASITE: + case EFT_HAVE_EMBASSIES: + case EFT_REVEAL_CITIES: + case EFT_REVEAL_MAP: + case EFT_NO_ANARCHY: + case EFT_ANY_GOVERNMENT: + e->range = EFR_PLAYER; break; + case EFT_GIVE_IMM_ADV: + e->range = EFR_CITY; break; + default: + break; + } + + e->amount = secfile_lookup_int_default(file, 0, "%s.effect%d.amount", + secname, j); + + if (all_survive) { + e->survives = 1; + } else { + e->survives = secfile_lookup_int_default(file, 0, "%s.effect%d.survives", + secname, j); + } + e->outside = secfile_lookup_int_default(file, 0, "%s.effect%d.outside", + secname, j); + + item = secfile_lookup_str_default(file, "", "%s.effect%d.cond_bldg", + secname, j); + if (*item != '\0') { + e->cond_bldg = find_improvement_by_name(item); + if (e->cond_bldg == B_LAST) { + freelog(LOG_ERROR, "for %s effect[%d].cond_bldg couldn't match " + "improvement \"%s\" (%s)", + name, j, item, filename); + problem = TRUE; + } + } else { + e->cond_bldg = B_LAST; + } + + item = secfile_lookup_str_default(file, "", "%s.effect%d.cond_gov", + secname, j); + if (*item != '\0') { + struct government *g = find_government_by_name(item); + if (!g) { + freelog(LOG_ERROR, "for %s effect[%d].cond_gov couldn't match " + "government \"%s\" (%s)", + name, j, item, filename); + e->cond_gov = game.government_count; + problem = TRUE; + } else { + e->cond_gov = g->index; + } + } else { + e->cond_gov = game.government_count; + } + + item = secfile_lookup_str_default(file, "None", "%s.effect%d.cond_adv", + secname, j); + if (*item != '\0') { + e->cond_adv = find_tech_by_name(item); + if (e->cond_adv == A_LAST) { + freelog(LOG_ERROR, + "for %s effect[%d].cond_adv couldn't match tech \"%s\" (%s)", + name, j, item, filename); + problem = TRUE; + } + } else { + e->cond_adv = A_NONE; + } + + item = secfile_lookup_str_default(file, "", "%s.effect%d.cond_eff", + secname, j); + if (*item != '\0') { + e->cond_eff = effect_type_from_str(item); + if (e->cond_eff == EFT_LAST) { + freelog(LOG_ERROR, + "for %s effect[%d].cond_eff couldn't match effect \"%s\" (%s)", + name, j, item, filename); + problem = TRUE; + } + } else { + e->cond_eff = EFT_LAST; + } + + item = secfile_lookup_str_default(file, "", "%s.effect%d.aff_unit", + secname, j); + e->aff_unit = UCL_LAST; + if (*item != '\0') { + int index; + + index = unit_flag_from_str(item); + if (index != F_MAX) { + e->aff_unit = index; + } else { + index = unit_move_type_from_str(item); + if (index > 0) { + e->aff_unit = F_MAX + index; + } else { + freelog(LOG_ERROR, + "for %s effect[%d].aff_unit couldn't match type \"%s\" (%s)", + name, j, item, filename); + problem = TRUE; + } + } + } + + item = secfile_lookup_str_default(file, "", "%s.effect%d.aff_terr", + secname, j); + if (*item != '\0') { + if (0 == strcmp("None", item)) { + e->aff_terr = T_LAST; + } else { + e->aff_terr = get_terrain_by_name(item); + if (e->aff_terr >= T_UNKNOWN) { + freelog(LOG_ERROR, "for %s effect[%d].aff_terr couldn't match " + "terrain \"%s\" (%s)", + name, j, item, filename); + e->aff_terr = T_LAST; + problem = TRUE; + } + } + } else { + e->aff_terr = T_UNKNOWN; + } + + item = secfile_lookup_str_default(file, "", "%s.effect%d.aff_spec", + secname, j); + if (*item != '\0') { + if (0 == strcmp("None", item)) { + e->aff_spec = S_NO_SPECIAL; + } else { + e->aff_spec = get_special_by_name(item); + if (e->aff_spec == S_NO_SPECIAL) { + freelog(LOG_ERROR, "for %s effect[%d].aff_spec couldn't match " + "special \"%s\" (%s)", + name, j, item, filename); + problem = TRUE; + } + } + } else { + e->aff_spec = S_ALL; + } + + if (!problem) { + k++; + } + } + effects[k].type = EFT_LAST; + + return effects; +} + +/************************************************************************** ... **************************************************************************/ static void load_ruleset_units(struct section_file *file) @@ -763,6 +1000,8 @@ u->food_cost = secfile_lookup_int(file, "%s.uk_food", sec[i]); u->gold_cost = secfile_lookup_int(file, "%s.uk_gold", sec[i]); + u->effect = load_effects(sec[i], u->name, file, TRUE, FALSE); + u->helptext = lookup_helptext(file, sec[i]); } unit_type_iterate_end; @@ -779,7 +1018,7 @@ continue; } ival = unit_flag_from_str(sval); - if (ival==F_LAST) { + if (ival==F_MAX) { freelog(LOG_ERROR, "for unit_type \"%s\": bad flag name \"%s\" (%s)", u->name, sval, filename); } @@ -961,8 +1200,10 @@ } /* FIXME: Remove this restriction when gen-impr implemented. */ if (nval != B_LAST_ENUM) { - freelog(LOG_FATAL, "Bad number of buildings %d (%s)", nval, filename); - exit(EXIT_FAILURE); + freelog(LOG_ERROR, _("ERROR! Bad number of buildings %d (%s). Although " + "this should still work, many building effects are still " + "hardcoded, and as such your modified buildings may not behave as " + "expected. Proceed with caution!"), nval, filename); } /* REMOVE TO HERE when gen-impr implemented. */ game.num_impr_types = nval; @@ -984,9 +1225,7 @@ char *datafile_options; char **sec, *item, **list; int i, j, k, nval, count; - bool problem; struct impr_type *b; - struct impr_effect *e; const char *filename = secfile_filename(file); datafile_options = check_ruleset_capabilities(file, "+1.10.1", filename); @@ -1093,164 +1332,7 @@ b->sabotage = secfile_lookup_int(file, "%s.sabotage", sec[i]); - for (count = 0; - secfile_lookup_str_default(file, NULL, "%s.effect%d.type", sec[i], - count); count++) { - /* nothing */ - } - - if (count>MAX_EFFECTS) { - freelog(LOG_FATAL, "For %s maximum number of effects (%d) exceeded", - b->name, MAX_EFFECTS); - exit(EXIT_FAILURE); - } - - b->effect = fc_malloc((count + 1) * sizeof(b->effect[0])); - k = 0; - for (j = 0; j < count; j++) { - e = &b->effect[k]; - problem = FALSE; - - item = secfile_lookup_str(file, "%s.effect%d.type", sec[i], j); - e->type = effect_type_from_str(item); - if (e->type == EFT_LAST) { - freelog(LOG_ERROR, - "for %s effect[%d].type couldn't match type \"%s\" (%s)", - b->name, j, item, filename); - problem = TRUE; - } - - item = - secfile_lookup_str_default(file, "None", "%s.effect%d.range", sec[i], j); - e->range = effect_range_from_str(item); - if (e->range == EFR_LAST) { - freelog(LOG_ERROR, - "for %s effect[%d].range couldn't match range \"%s\" (%s)", - b->name, j, item, filename); - problem = TRUE; - } - - e->amount = - secfile_lookup_int_default(file, 0, "%s.effect%d.amount", sec[i], j); - - e->survives = - secfile_lookup_int_default(file, 0, "%s.effect%d.survives", sec[i], j); - - item = - secfile_lookup_str_default(file, "", "%s.effect%d.cond_bldg", sec[i], j); - if (*item != '\0') { - e->cond_bldg = find_improvement_by_name(item); - if (e->cond_bldg == B_LAST) { - freelog(LOG_ERROR, - "for %s effect[%d].cond_bldg couldn't match improvement \"%s\" (%s)", - b->name, j, item, filename); - problem = TRUE; - } - } else { - e->cond_bldg = B_LAST; - } - - item = - secfile_lookup_str_default(file, "", "%s.effect%d.cond_gov", sec[i], j); - if (*item != '\0') { - struct government *g = find_government_by_name(item); - if (!g) { - freelog(LOG_ERROR, - "for %s effect[%d].cond_gov couldn't match government \"%s\" (%s)", - b->name, j, item, filename); - e->cond_gov = game.government_count; - problem = TRUE; - } else { - e->cond_gov = g->index; - } - } else { - e->cond_gov = game.government_count; - } - - item = - secfile_lookup_str_default(file, "None", "%s.effect%d.cond_adv", sec[i], j); - if (*item != '\0') { - e->cond_adv = find_tech_by_name(item); - if (e->cond_adv == A_LAST) { - freelog(LOG_ERROR, - "for %s effect[%d].cond_adv couldn't match tech \"%s\" (%s)", - b->name, j, item, filename); - problem = TRUE; - } - } else { - e->cond_adv = A_NONE; - } - - item = - secfile_lookup_str_default(file, "", "%s.effect%d.cond_eff", sec[i], j); - if (*item != '\0') { - e->cond_eff = effect_type_from_str(item); - if (e->cond_eff == EFT_LAST) { - freelog(LOG_ERROR, - "for %s effect[%d].cond_eff couldn't match effect \"%s\" (%s)", - b->name, j, item, filename); - problem = TRUE; - } - } else { - e->cond_eff = EFT_LAST; - } - - item = - secfile_lookup_str_default(file, "", "%s.effect%d.aff_unit", sec[i], j); - if (*item != '\0') { - e->aff_unit = unit_class_from_str(item); - if (e->aff_unit == UCL_LAST) { - freelog(LOG_ERROR, - "for %s effect[%d].aff_unit couldn't match class \"%s\" (%s)", - b->name, j, item, filename); - problem = TRUE; - } - } else { - e->aff_unit = UCL_LAST; - } - - item = - secfile_lookup_str_default(file, "", "%s.effect%d.aff_terr", sec[i], j); - if (*item != '\0') { - if (0 == strcmp("None", item)) { - e->aff_terr = T_LAST; - } else { - e->aff_terr = get_terrain_by_name(item); - if (e->aff_terr >= T_UNKNOWN) { - freelog(LOG_ERROR, - "for %s effect[%d].aff_terr couldn't match terrain \"%s\" (%s)", - b->name, j, item, filename); - e->aff_terr = T_LAST; - problem = TRUE; - } - } - } else { - e->aff_terr = T_UNKNOWN; - } - - item = - secfile_lookup_str_default(file, "", "%s.effect%d.aff_spec", sec[i], j); - if (*item != '\0') { - if (0 == strcmp("None", item)) { - e->aff_spec = S_NO_SPECIAL; - } else { - e->aff_spec = get_special_by_name(item); - if (e->aff_spec == S_NO_SPECIAL) { - freelog(LOG_ERROR, - "for %s effect[%d].aff_spec couldn't match special \"%s\" (%s)", - b->name, j, item, filename); - problem = TRUE; - } - } - } else { - e->aff_spec = S_ALL; - } - - if (!problem) { - k++; - } - } - b->effect[k].type = EFT_LAST; + b->effect = load_effects(sec[i], b->name, file, FALSE, FALSE); /* FIXME: remove when gen-impr obsoletes */ b->variant = secfile_lookup_int_default(file, 0, "%s.variant", sec[i]); @@ -1268,6 +1350,19 @@ b->helptext = lookup_helptext(file, sec[i]); } + /* 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); + } + /* Some more consistency checking: */ impr_type_iterate(i) { b = &improvement_types[i]; @@ -1657,6 +1752,8 @@ = secfile_lookup_int(file, "%s.production_shield_penalty,1", sec[i]); g->celeb_food_before_penalty = secfile_lookup_int(file, "%s.production_food_penalty,1", sec[i]); + + g->effect = load_effects(sec[i], g->name, file, FALSE, TRUE); g->helptext = lookup_helptext(file, sec[i]); } @@ -1808,6 +1905,7 @@ packet.government_when_anarchy = game.government_when_anarchy; packet.default_government = game.default_government; + packet.num_unit_flags = game.num_unit_flags; packet.num_unit_types = game.num_unit_types; packet.num_impr_types = game.num_impr_types; packet.num_tech_types = game.num_tech_types; @@ -2261,6 +2359,8 @@ } pl->goals.government = val; + pl->effect = load_effects(sec[i], pl->name, file, FALSE, TRUE); + /* read "normal" city names */ pl->city_names = load_city_name_list(file, sec[i], ".cities"); @@ -2521,6 +2621,7 @@ packet.paratroopers_range = u->paratroopers_range; packet.paratroopers_mr_req = u->paratroopers_mr_req; packet.paratroopers_mr_sub = u->paratroopers_mr_sub; + packet.effect = u->effect; /* pointer assignment */ packet.helptext = u->helptext; /* pointer assignment */ lsend_packet_ruleset_unit(dest, &packet); @@ -2728,6 +2829,7 @@ sz_strlcpy(gov.graphic_str, g->graphic_str); sz_strlcpy(gov.graphic_alt, g->graphic_alt); + gov.effect = g->effect; /* pointer assignment */ gov.helptext = g->helptext; /* pointer assignment */ lsend_packet_ruleset_government(dest, &gov); @@ -2777,6 +2879,8 @@ sz_strlcpy(packet.class, n->class); sz_strlcpy(packet.legend, n->legend); + packet.effect = n->effect; /* pointer assignment */ + lsend_packet_ruleset_nation(dest, &packet); } } @@ -2858,6 +2962,7 @@ openload_ruleset_file(&unitfile, "units"); load_unit_names(&unitfile); + load_unit_userflags(&unitfile); openload_ruleset_file(&terrfile, "terrain"); load_terrain_names(&terrfile); diff -Nur -Xfreecivdiff.ignore freeciv-cvs/server/savegame.c freeciv-patched/server/savegame.c --- freeciv-cvs/server/savegame.c 2003-06-30 12:18:29.000000000 +0100 +++ freeciv-patched/server/savegame.c 2003-06-30 12:18:03.000000000 +0100 @@ -157,7 +157,7 @@ and rulesets */ #define SAVEFILE_OPTIONS "startoptions spacerace2 rulesets" \ " diplchance_percent worklists2 map_editor known32fix turn " \ -"attributes watchtower rulesetdir client_worklists" +"attributes watchtower rulesetdir client_worklists impr_gen" static const char hex_chars[] = "0123456789abcdef"; static const char terrain_chars[] = "adfghjm prstu"; @@ -615,6 +615,18 @@ plr->city_style=secfile_lookup_int_default(file, get_nation_city_style(plr->nation), "player%d.city_style", plrno); + if (has_capability("impr_gen", savefile_options)) { + /* destroyed wonders: */ + p = secfile_lookup_str_default(file, "", "player%d.destroyed_wonders", + plrno); + for (i = 0; i < game.num_impr_types && p[i] != '\0'; i++) { + if (p[i] == '1') { + game.global_wonders[i] = -1; /* destroyed! */ + add_destroyed_improvement(plr, i); + } + } + } + plr->nturns_idle=0; plr->is_male=secfile_lookup_bool_default(file, TRUE, "player%d.is_male", plrno); plr->is_alive=secfile_lookup_bool(file, "player%d.is_alive", plrno); @@ -887,6 +899,16 @@ unit_list_init(&pcity->units_supported); + /* Initialise list of improvements with City- and Building-wide + equiv_ranges */ + improvement_status_init(pcity->improvements, + ARRAY_SIZE(pcity->improvements)); + + /* Initialise city's vector of improvement effects. */ + ceff_vector_init(&pcity->effects); + + update_city_bonuses(pcity); + /* Initialize pcity->city_map[][], using set_worker_city() so that ptile->worked gets initialized correctly. The pre-initialisation to C_TILE_EMPTY is necessary because set_worker_city() accesses @@ -935,11 +957,6 @@ p=secfile_lookup_str(file, "player%d.c%d.improvements", plrno, i); - /* Initialise list of improvements with City- and Building-wide - equiv_ranges */ - improvement_status_init(pcity->improvements, - ARRAY_SIZE(pcity->improvements)); - impr_type_iterate(x) { if (*p != '\0' && *p++=='1') { city_add_improvement(pcity,x); @@ -1291,6 +1308,26 @@ } /*************************************************************** + Makes a list of any destroyed improvements +***************************************************************/ +static void make_destroyed_list(char *temp, struct ceff_vector *ceff) +{ + int i, efflen = ceff_vector_size(ceff); + + for (i = 0; i < game.num_impr_types; i++) { + temp[i] = '0'; + } + + for (i = 0; i < efflen; i++) { + struct eff_city *eff; + + eff = ceff_vector_get(ceff, i); + temp[eff->impr] = '1'; + } + temp[game.num_impr_types] = '\0'; +} + +/*************************************************************** ... ***************************************************************/ static void player_save(struct player *plr, int plrno, @@ -1299,6 +1336,7 @@ int i; char invs[A_LAST+1]; struct player_spaceship *ship = &plr->spaceship; + char temp[B_LAST + 1]; secfile_insert_str(file, plr->name, "player%d.name", plrno); secfile_insert_str(file, plr->username, "player%d.username", plrno); @@ -1570,6 +1608,10 @@ } city_list_iterate_end; + /* destroyed wonders: */ + make_destroyed_list(temp, &plr->destroyed_effects); + secfile_insert_str(file, temp, "player%d.destroyed_wonders", plrno); + /********** Put the players private map **********/ /* Otherwise the player can see all, and there's no reason to save the private map. */ if (game.fogofwar @@ -2105,6 +2147,7 @@ } if (string[i] == '1') { game.global_wonders[i] = -1; /* destroyed! */ + add_destroyed_improvement(NULL, i); } } impr_type_iterate_end; out: @@ -2162,8 +2205,19 @@ } if (!game.is_new_game) { + players_iterate(pplayer) { + player_update_global_effects(pplayer); + } players_iterate_end; + /* Set active city improvements/wonders and their effects */ - improvements_update_obsolete(); + update_all_effects(); + + /* Make sure the move/hitpoint modifiers are set */ + players_iterate(pplayer) { + unit_list_iterate(pplayer->units, punit) { + unit_update_modifiers(pplayer, punit); + } unit_list_iterate_end; + } players_iterate_end; } game.player_idx=0; @@ -2180,7 +2234,6 @@ int i; int version; char options[512]; - char temp[B_LAST+1]; version = MAJOR_VERSION *10000 + MINOR_VERSION *100 + PATCH_VERSION; secfile_insert_int(file, version, "game.version"); @@ -2326,16 +2379,10 @@ secfile_insert_bool(file, game.save_options.save_players, "game.save_players"); if (game.save_options.save_players) { + char temp[B_LAST + 1]; + /* destroyed wonders: */ - impr_type_iterate(i) { - if (is_wonder(i) && game.global_wonders[i]!=0 - && !find_city_by_id(game.global_wonders[i])) { - temp[i] = '1'; - } else { - temp[i] = '0'; - } - } impr_type_iterate_end; - temp[game.num_impr_types] = '\0'; + make_destroyed_list(temp, &game.destroyed_effects); secfile_insert_str(file, temp, "game.destroyed_wonders"); calc_unit_ordering(); diff -Nur -Xfreecivdiff.ignore freeciv-cvs/server/settlers.c freeciv-patched/server/settlers.c --- freeciv-cvs/server/settlers.c 2003-06-30 12:18:26.000000000 +0100 +++ freeciv-patched/server/settlers.c 2003-06-30 12:16:50.000000000 +0100 @@ -861,8 +861,7 @@ struct city *pcity = map_get_city(punit->x, punit->y); assert(pcity != NULL); - cost = city_granary_size(pcity->size); - if (city_got_effect(pcity, B_GRANARY)) { cost /= 2; } + cost = city_granary_size(pcity->size) * (100 - pcity->growth_food) / 100; } return cost; diff -Nur -Xfreecivdiff.ignore freeciv-cvs/server/srv_main.c freeciv-patched/server/srv_main.c --- freeciv-cvs/server/srv_main.c 2003-06-30 12:18:26.000000000 +0100 +++ freeciv-patched/server/srv_main.c 2003-06-30 12:18:03.000000000 +0100 @@ -295,41 +295,37 @@ } /************************************************************************** -... -**************************************************************************/ -static void do_apollo_program(void) -{ - struct city *pcity = find_city_wonder(B_APOLLO); - - if (pcity) { - struct player *pplayer = city_owner(pcity); - - if (game.civstyle == 1) { - players_iterate(other_player) { - city_list_iterate(other_player->cities, pcity) { - show_area(pplayer, pcity->x, pcity->y, 0); - } city_list_iterate_end; - } players_iterate_end; - } else { - map_know_all(pplayer); - send_all_known_tiles(&pplayer->connections); - send_all_known_cities(&pplayer->connections); - } - } -} - -/************************************************************************** -... + Carries out any actions that affect the whole world - e.g. Marco Polo, + Apollo Program **************************************************************************/ -static void marco_polo_make_contact(void) +static void check_global_effects(void) { - struct city *pcity = find_city_wonder(B_MARCO); - if (pcity) { - players_iterate(pplayer) { - make_contact(city_owner(pcity), pplayer, pcity->x, pcity->y); - } players_iterate_end; - } + players_iterate(pplayer) { + impr_effects_iterate(iter, B_LAST, NULL, pplayer) { + switch(iter.imeff->type) { + case EFT_HAVE_EMBASSIES: + players_iterate(othplayer) { + make_contact(pplayer, othplayer, -1, -1); + } players_iterate_end; + break; + case EFT_REVEAL_MAP: + map_know_all(pplayer); + send_all_known_tiles(&pplayer->connections); + send_all_known_cities(&pplayer->connections); + break; + case EFT_REVEAL_CITIES: + players_iterate(othplayer) { + city_list_iterate(othplayer->cities, pcity) { + show_area(pplayer, pcity->x, pcity->y, 0); + } city_list_iterate_end; + } players_iterate_end; + break; + default: + break; + } + } impr_effects_iterate_end; + } players_iterate_end; } /************************************************************************** @@ -522,8 +518,7 @@ &game.nuclearwinter, &game.coolinglevel, nuclear_winter); update_diplomatics(); - do_apollo_program(); - marco_polo_make_contact(); + check_global_effects(); make_history_report(); send_player_turn_notifications(NULL); freelog(LOG_DEBUG, "Turn ended."); @@ -1727,6 +1722,7 @@ init_tech(pplayer, game.tech); player_limit_to_government_rates(pplayer); pplayer->economic.gold = game.gold; + player_update_global_effects(pplayer); } players_iterate_end; game.max_players = game.nplayers; diff -Nur -Xfreecivdiff.ignore freeciv-cvs/server/unithand.c freeciv-patched/server/unithand.c --- freeciv-cvs/server/unithand.c 2003-06-30 12:18:27.000000000 +0100 +++ freeciv-patched/server/unithand.c 2003-06-30 12:16:57.000000000 +0100 @@ -160,7 +160,7 @@ { const Unit_Type_id from_unittype = packet->type; const Unit_Type_id to_unittype = can_upgrade_unittype(pplayer, - from_unittype); + from_unittype, TRUE); if (to_unittype == -1) { notify_player(pplayer, @@ -228,7 +228,7 @@ return; } from_unit = punit->type; - if((to_unit=can_upgrade_unittype(pplayer, punit->type)) == -1) { + if((to_unit=can_upgrade_unittype(pplayer, punit->type, TRUE)) == -1) { notify_player(pplayer, _("Game: Illegal package, can't upgrade %s (yet)."), unit_type(punit)->name); return; @@ -507,15 +507,9 @@ break; case AB_NO_AQUEDUCT: notify_player_ex(pplayer, punit->x, punit->y, E_NOEVENT, - _("Game: %s needs %s to grow, so you cannot add %s."), - pcity->name, get_improvement_name(B_AQUEDUCT), - unit_name); - break; - case AB_NO_SEWER: - notify_player_ex(pplayer, punit->x, punit->y, E_NOEVENT, - _("Game: %s needs %s to grow, so you cannot add %s."), - pcity->name, get_improvement_name(B_SEWER), - unit_name); + _("Game: %s needs an improvement to grow, so " + "you cannot add %s."), + pcity->name, unit_name); break; default: /* Shouldn't happen */ @@ -737,7 +731,7 @@ if (punit->hp && (pcity=map_get_city(def_x, def_y)) && pcity->size>1 && - !city_got_citywalls(pcity) && + !is_under_effect(B_LAST,pcity,NULL,EFT_UNIT_NO_LOSE_POP) && kills_citizen_after_attack(punit)) { city_reduce_size(pcity,1); city_refresh(pcity); diff -Nur -Xfreecivdiff.ignore freeciv-cvs/server/unittools.c freeciv-patched/server/unittools.c --- freeciv-cvs/server/unittools.c 2003-06-30 12:18:27.000000000 +0100 +++ freeciv-patched/server/unittools.c 2003-06-30 12:18:10.000000000 +0100 @@ -70,7 +70,8 @@ static struct unit *choose_more_important_refuel_target(struct unit *punit1, struct unit *punit2); static bool maybe_cancel_patrol_due_to_enemy(struct unit *punit); -static int hp_gain_coord(struct unit *punit); +static int hp_gain_coord(struct unit *punit, struct city *pcity, + bool fullrepair); /************************************************************************** returns a unit type with a given role, use -1 if you don't want a tech @@ -110,12 +111,19 @@ **************************************************************************/ void maybe_make_veteran(struct unit *punit) { + int amount = 50; /* Normally there's a 50% chance... */ + if (punit->veteran) return; - if (player_owns_active_wonder(unit_owner(punit), B_SUNTZU)) - punit->veteran = TRUE; - else - punit->veteran = (myrand(2) == 1); + + /* Do we have any veteran-making effects? */ + unit_effects_iterate(iter, punit, punit) { + if (iter.imeff->type == EFT_UNIT_VET_COMBAT) { + amount = MAX(amount, iter.imeff->amount); + } + } unit_effects_iterate_end; + + punit->veteran = (myrand(100) < amount ? 1 : 0); } /************************************************************************** @@ -209,42 +217,67 @@ } /*************************************************************************** -Do Leonardo's Workshop upgrade(s). Select unit to upgrade by random. --Zamar -Now be careful not to strand units at sea with the Workshop. --dwp + Possibly upgrade units belonging to the given player (restricted to the + given continent or city, if not -1 and NULL respectively) by applying the + effect pointed to by "iter" ****************************************************************************/ -static void handle_leonardo(struct player *pplayer) +static void handle_unit_upgrades(struct player *pplayer, int continent, + struct city *pcity, struct eff_iter *iter) { - int upgrade_type; - int leonardo_variant; - + enum effect_type eff_type; + Impr_Type_id impr; struct unit_list candidates; - int candidate_to_upgrade=-1; - - int i; - - leonardo_variant = improvement_variant(B_LEONARDO); - + int upgrade_all, upgrade_leap, i, candidate_to_upgrade = -1; + int upgrade_type; + struct unit_classes aff_unit; + enum tile_special_type spec_t; + enum tile_terrain_type tile_t; + + assert(iter != NULL); + eff_type = iter->imeff->type; + impr = eff_iterator_get_improvement(iter); + + upgrade_all = (eff_type == EFT_UPGRADE_ALL_STEP + || eff_type == EFT_UPGRADE_ALL_LEAP); + upgrade_leap = (eff_type == EFT_UPGRADE_ONE_LEAP + || eff_type == EFT_UPGRADE_ALL_LEAP); + unit_list_init(&candidates); unit_list_iterate(pplayer->units, punit) { - if ((upgrade_type=can_upgrade_unittype(pplayer, punit->type))!=-1 && - !upgrade_would_strand(punit, upgrade_type)) + struct city *mapcity = map_get_city(punit->x, punit->y); + + tile_t = map_get_terrain(punit->x, punit->y); + spec_t = map_get_special(punit->x, punit->y); + get_unit_classes(punit, &aff_unit); + + if (is_unit_terrain_affected(iter->imeff, &aff_unit, tile_t, spec_t) && + is_effect_outside(iter->imeff, mapcity) && + (continent == -1 || + continent == map_get_continent(punit->x, punit->y)) && + (!pcity || pcity == mapcity) && + (upgrade_type = can_upgrade_unittype(pplayer, punit->type, + upgrade_leap)) != -1 && + !upgrade_would_strand(punit, upgrade_type)) { unit_list_insert(&candidates, punit); /* Potential candidate :) */ + } } unit_list_iterate_end; - + if (unit_list_size(&candidates) == 0) return; /* We have Leonardo, but nothing to upgrade! */ - - if (leonardo_variant == 0) + + if (!upgrade_all) { candidate_to_upgrade=myrand(unit_list_size(&candidates)); + } - i=0; + i=0; unit_list_iterate(candidates, punit) { - if (leonardo_variant != 0 || i == candidate_to_upgrade) { - upgrade_type=can_upgrade_unittype(pplayer, punit->type); + if ((upgrade_all || i == candidate_to_upgrade) && + myrand(100) < iter->imeff->amount) { + upgrade_type = can_upgrade_unittype(pplayer, punit->type, upgrade_leap); notify_player(pplayer, _("Game: %s has upgraded %s to %s%s."), - improvement_types[B_LEONARDO].name, + get_improvement_name(impr), unit_type(punit)->name, get_unit_type(upgrade_type)->name, get_location_str_in(pplayer, punit->x, punit->y)); @@ -253,11 +286,72 @@ } i++; } unit_list_iterate_end; - + unit_list_unlink_all(&candidates); } /*************************************************************************** +Do Leonardo's Workshop upgrade(s). Select unit to upgrade by random. --Zamar +Now be careful not to strand units at sea with the Workshop. --dwp +Use any active effects from rulesets. --bmw +****************************************************************************/ +static void handle_leonardo(struct player *pplayer) +{ + struct eff_iter iter; + bool *cont_done; + bool player_done = FALSE; + int this_cont; + + /* Since the effect may be restricted to a city, iterate over all cities + to find all such effect instances. Keep track of which Island- and + Player- range effects we pick up, though, so that we don't apply them + multiple times (these effects are shared by multiple cities) */ + cont_done = fc_calloc(map.num_continents + 1, sizeof(bool)); + city_list_iterate(pplayer->cities, pcity) { + + /* Don't mark the player/island as "done" until we've looked at all + effects (as multiple effects at each range are possible) */ + bool this_playerdone = FALSE, this_contdone = FALSE; + + this_cont = map_get_continent(pcity->x, pcity->y); + eff_iterator_impr_init(&iter, B_LAST, pcity, + (player_done && cont_done[this_cont]) ? NULL : pplayer); + while (eff_iterator_next(&iter)) { + switch(iter.imeff->type) { + case EFT_UPGRADE_ONE_STEP: case EFT_UPGRADE_ONE_LEAP: + case EFT_UPGRADE_ALL_STEP: case EFT_UPGRADE_ALL_LEAP: + switch(iter.imeff->range) { + case EFR_PLAYER: case EFR_WORLD: + if (!player_done) { + handle_unit_upgrades(pplayer, -1, NULL, &iter); + } + this_playerdone = TRUE; + break; + case EFR_ISLAND: + if (!cont_done[this_cont]) { + handle_unit_upgrades(pplayer, this_cont, NULL, &iter); + } + this_contdone = TRUE; + break; + case EFR_CITY: + handle_unit_upgrades(pplayer, -1, pcity, &iter); + break; + default: + break; + } + break; + default: + break; + } + } + player_done |= this_playerdone; + cont_done[this_cont] |= this_contdone; + } city_list_iterate_end; + + free(cont_done); +} + +/*************************************************************************** Select which unit is more important to refuel: It's more important to refuel plane which has less fuel. If those are equal then we refuel more valuable unit. @@ -382,11 +476,13 @@ void player_restore_units(struct player *pplayer) { /* 1) get Leonardo out of the way first: */ - if (player_owns_active_wonder(pplayer, B_LEONARDO)) - handle_leonardo(pplayer); + handle_leonardo(pplayer); unit_list_iterate(pplayer->units, punit) { + /* Set relevant hp/move modifiers from generalised improvements */ + unit_update_modifiers(pplayer, punit); + /* 2) Modify unit hitpoints. Helicopters can even lose them. */ unit_restore_hitpoints(pplayer, punit); @@ -406,8 +502,7 @@ } /* 4) Check that triremes are near coastline, otherwise... */ - if (unit_flag(punit, F_TRIREME) - && myrand(100) < trireme_loss_pct(pplayer, punit->x, punit->y)) { + if (myrand(100) < ship_loss_pct(punit, punit->x, punit->y)) { notify_player_ex(pplayer, punit->x, punit->y, E_UNIT_LOST, _("Game: Your %s has been lost on the high seas."), unit_name(punit->type)); @@ -476,6 +571,36 @@ } /**************************************************************************** + Apply hitpoint and move modifiers to the given unit, to save on + recalculation every time we update moverate/hitpoints +*****************************************************************************/ +void unit_update_modifiers(struct player *pplayer, struct unit *punit) +{ + bool fullrepair = FALSE; + int hp_recover = 0, move_modifier = 0; + + /* Add effects of generalised improvements */ + unit_effects_iterate(iter, punit, punit) { + switch(iter.imeff->type) { + case EFT_UNIT_REPAIR: + fullrepair = TRUE; + break; + case EFT_UNIT_RECOVER: + hp_recover += iter.imeff->amount; + break; + case EFT_UNIT_MOVE: + move_modifier += iter.imeff->amount; + break; + default: + break; + } + } unit_effects_iterate_end; + + punit->hp_recover = (fullrepair ? -1 : hp_recover); + punit->move_modifier = move_modifier * SINGLE_MOVE; +} + +/**************************************************************************** add hitpoints to the unit, hp_gain_coord returns the amount to add united nations will speed up the process by 2 hp's / turn, means helicopters will actually not loose hp's every turn if player have that wonder. @@ -485,18 +610,19 @@ static void unit_restore_hitpoints(struct player *pplayer, struct unit *punit) { bool was_lower; + struct city *pcity = map_get_city(punit->x, punit->y); + bool fullrepair = (punit->hp_recover == -1); was_lower=(punit->hp < unit_type(punit)->hp); if(!punit->moved) { - punit->hp+=hp_gain_coord(punit); + punit->hp += hp_gain_coord(punit, pcity, fullrepair); } - if (player_owns_active_wonder(pplayer, B_UNITED)) - punit->hp+=2; - + /* hp_recover is set by unit_update_modifiers */ + if (!fullrepair) punit->hp += punit->hp_recover; + if(is_heli_unit(punit)) { - struct city *pcity = map_get_city(punit->x,punit->y); if(!pcity) { if (!map_has_special(punit->x, punit->y, S_AIRBASE)) punit->hp-=unit_type(punit)->hp/10; @@ -542,27 +668,19 @@ ports will regen navalunits completely fortify will add a little extra. ***************************************************************************/ -static int hp_gain_coord(struct unit *punit) +static int hp_gain_coord(struct unit *punit, struct city *pcity, + bool fullrepair) { int hp; - struct city *pcity; if (unit_on_fortress(punit)) hp=unit_type(punit)->hp/4; else hp=0; - if((pcity=map_get_city(punit->x,punit->y))) { - if ((city_got_barracks(pcity) && - (is_ground_unit(punit) || improvement_variant(B_BARRACKS)==1)) || - (city_got_building(pcity, B_AIRPORT) && is_air_unit(punit)) || - (city_got_building(pcity, B_AIRPORT) && is_heli_unit(punit)) || - (city_got_building(pcity, B_PORT) && is_sailing_unit(punit))) { - hp=unit_type(punit)->hp; - } - else - hp=unit_type(punit)->hp/3; - } - else if (!is_heli_unit(punit)) + if (pcity) { + hp = fullrepair ? unit_type(punit)->hp : unit_type(punit)->hp / 3; + } else if (!is_heli_unit(punit)) { hp++; + } if(punit->activity==ACTIVITY_FORTIFIED) hp++; @@ -1457,9 +1575,14 @@ punit->hp = hp_left; } + /* Apply/remove effects of Magellan etc. */ + unit_update_modifiers(pplayer, punit); + if (moves_left >= 0) { /* Override default full MP */ punit->moves_left = moves_left; + } else { + punit->moves_left = unit_move_rate(punit); } /* Assume that if moves_left < 0 then the unit is "fresh", diff -Nur -Xfreecivdiff.ignore freeciv-cvs/server/unittools.h freeciv-patched/server/unittools.h --- freeciv-cvs/server/unittools.h 2003-06-30 12:18:27.000000000 +0100 +++ freeciv-patched/server/unittools.h 2003-06-30 12:16:19.000000000 +0100 @@ -35,6 +35,7 @@ void update_unit_activities(struct player *pplayer); /* various */ +void unit_update_modifiers(struct player *pplayer, struct unit *punit); char *get_location_str_in(struct player *pplayer, int x, int y); char *get_location_str_at(struct player *pplayer, int x, int y); enum goto_move_restriction get_activity_move_restriction(enum unit_activity activity);