<URL: http://bugs.freeciv.org/Ticket/Display.html?id=40607 >
2009/5/8 <>: > >> [cazf...@gmail.com - Mo 04. Mai. 2009, 09:53:10]: >> >> 2009/1/7 Marko Lindqvist <cazf...@gmail.com>: >> > 2008/12/21 Yoav Luft: >> >> >> >> Hi, I think it's pretty much finished, at least the basic > functionality. >> >> The attached .diff file adds the functionality of cities losing >> >> population when they grow too large due to diseases. >> > >> > I found additional comments in RT ticket that were not sent to >> > mailing list. I looked latest version of the patch, >> > 40607-freeciv-svn15395-health.patch.diff. >> >> Is anybody working on this? >> > > I did try to adapt the patch, especially the AI effect evaluation. But > I did not understand how and why it is calculated ... After some time > I moved on to the gold upkeep patch. I still observe this patch but at > the moment my time to work on this is very limited and if I find some > time it is the upkeep patch. > > I attached my last version. Changes: > > * reduced formula for the AI effect evaluation > * change calculation of trade illness > * save plague status for each city (never or turn x) > * ruleset updates Ok, I updated this myself - Updated against svn - Adjusted AI effect evaluation - Marked illness text for translation - Typo & style fixes - Renamed "Illness" effect as "Health" and now positive effects are good - Removed plague from default ruleset - Renamed event E_CITY_ILL as E_CITY_PLAGUE - Illness value was not saved nor recalculated at game load. Fixed this by moving city_illness() call from update_city_activity() to city_refresh_from_main_map() - Send illness information to client side - Added illness value initialization to #ifdef'd part of create_city_virtual() for completeness (it's allocated with fc_calloc() anyway) - Fixed initialization of pcity->turn_plague to value -1 (was inside #ifdef) Untested > PS: If it is possible I would like to get an email if a comment to > this patch is added (matthias.pfaffer...@mapfa.de) Added you to ticket watchers. - ML
diff -Nurd -X.diff_ignore freeciv/ai/aicity.c freeciv/ai/aicity.c --- freeciv/ai/aicity.c 2008-10-27 04:13:32.000000000 +0200 +++ freeciv/ai/aicity.c 2009-05-11 00:57:24.000000000 +0300 @@ -520,6 +520,12 @@ case EFT_GROWTH_FOOD: v += c * 4 + (amount / 7) * pcity->surplus[O_FOOD]; break; + case EFT_HEALTH: + /* Is plague possible */ + if (game.info.plague_on && pcity->size > 1) { + v += c * 5 + (amount / 5) * pcity->illness; + } + break; case EFT_AIRLIFT: /* FIXME: We need some smart algorithm here. The below is * totally braindead. */ diff -Nurd -X.diff_ignore freeciv/client/citydlg_common.c freeciv/client/citydlg_common.c --- freeciv/client/citydlg_common.c 2009-01-25 16:51:10.000000000 +0200 +++ freeciv/client/citydlg_common.c 2009-05-11 00:25:51.000000000 +0300 @@ -490,6 +490,28 @@ } /************************************************************************** + Return text describing the chance for a plague. +**************************************************************************/ +void get_city_dialog_illness_text(const struct city *pcity, + char *buf, size_t bufsz) +{ + int illness, trade, effects, from_size; + + illness = city_illness(pcity, &trade, &effects, &from_size); + buf[0] = '\0'; + + cat_snprintf(buf, bufsz, _("%+2.1f : Risk from trade\n"), + ((float)(trade) / 10.0)); + cat_snprintf(buf, bufsz, _("%+2.1f : Risk from over crowdness\n"), + ((float)(from_size) / 10.0)); + cat_snprintf(buf, bufsz, _("%+2.1f : Effect of buildings\n"), + ((float)(effects) / 10.0)); + cat_snprintf(buf, bufsz, _("==== : Adds up to\n")); + cat_snprintf(buf, bufsz, _("%2.1f : Total chance for a plague"), + ((float)(illness) / 10.0)); +} + +/************************************************************************** Return text describing the pollution output. **************************************************************************/ void get_city_dialog_pollution_text(const struct city *pcity, diff -Nurd -X.diff_ignore freeciv/client/citydlg_common.h freeciv/client/citydlg_common.h --- freeciv/client/citydlg_common.h 2008-10-27 04:14:03.000000000 +0200 +++ freeciv/client/citydlg_common.h 2009-05-11 00:16:36.000000000 +0300 @@ -49,6 +49,8 @@ char *buffer, size_t bufsz); void get_city_dialog_pollution_text(const struct city *pcity, char *buffer, size_t bufsz); +void get_city_dialog_illness_text(const struct city *pcity, + char *buf, size_t bufsz); int get_city_citizen_types(struct city *pcity, enum citizen_feeling index, enum citizen_category *citizens); diff -Nurd -X.diff_ignore freeciv/client/gui-gtk-2.0/citydlg.c freeciv/client/gui-gtk-2.0/citydlg.c --- freeciv/client/gui-gtk-2.0/citydlg.c 2009-01-29 13:44:59.000000000 +0200 +++ freeciv/client/gui-gtk-2.0/citydlg.c 2009-05-11 00:16:36.000000000 +0300 @@ -95,7 +95,7 @@ enum info_style { NORMAL, ORANGE, RED, NUM_INFO_STYLES }; #define NUM_CITIZENS_SHOWN 23 -#define NUM_INFO_FIELDS 11 /* number of fields in city_info */ +#define NUM_INFO_FIELDS 12 /* number of fields in city_info */ #define NUM_PAGES 6 /* the number of pages in city dialog notebook * (+1) if you change this, you must add an * entry to misc_whichtab_label[] */ @@ -501,7 +501,7 @@ enum { FIELD_FOOD, FIELD_SHIELD, FIELD_TRADE, FIELD_GOLD, FIELD_LUXURY, FIELD_SCIENCE, FIELD_GRANARY, FIELD_GROWTH, FIELD_CORRUPTION, - FIELD_WASTE, FIELD_POLLUTION + FIELD_WASTE, FIELD_POLLUTION, FIELD_ILLNESS }; /**************************************************************** @@ -541,6 +541,9 @@ case FIELD_POLLUTION: get_city_dialog_pollution_text(pdialog->pcity, buf, sizeof(buf)); break; + case FIELD_ILLNESS: + get_city_dialog_illness_text(pdialog->pcity, buf, sizeof(buf)); + break; default: return TRUE; } @@ -589,7 +592,8 @@ N_("Change in:"), N_("Corruption:"), N_("Waste:"), - N_("Pollution:") + N_("Pollution:"), + N_("Plague Risk:") }; static bool output_label_done; @@ -1401,8 +1405,9 @@ char buf[NUM_INFO_FIELDS][512]; struct city *pcity = pdialog->pcity; int granaryturns; + enum { FOOD, SHIELD, TRADE, GOLD, LUXURY, SCIENCE, - GRANARY, GROWTH, CORRUPTION, WASTE, POLLUTION + GRANARY, GROWTH, CORRUPTION, WASTE, POLLUTION, ILLNESS }; /* fill the buffers with the necessary info */ @@ -1444,6 +1449,8 @@ pcity->waste[O_SHIELD]); my_snprintf(buf[POLLUTION], sizeof(buf[POLLUTION]), "%2d", pcity->pollution); + my_snprintf(buf[ILLNESS], sizeof(buf[ILLNESS]), "%2.1f", + ((float)(city_illness(pcity, NULL, NULL, NULL)) / 10.0)); /* stick 'em in the labels */ @@ -1466,6 +1473,9 @@ style = (pcity->pollution >= 10) ? RED : NORMAL; gtk_widget_modify_style(info_label[POLLUTION], info_label_style[style]); + + style = (pcity->illness >= 20) ? RED : NORMAL; + gtk_widget_modify_style(info_label[ILLNESS], info_label_style[style]); } /**************************************************************** diff -Nurd -X.diff_ignore freeciv/client/packhand.c freeciv/client/packhand.c --- freeciv/client/packhand.c 2009-05-06 11:51:00.000000000 +0300 +++ freeciv/client/packhand.c 2009-05-11 01:05:13.000000000 +0300 @@ -617,6 +617,7 @@ pcity->shield_stock = packet->shield_stock; } pcity->pollution=packet->pollution; + pcity->illness = packet->illness; if (city_is_new || !are_universals_equal(&pcity->production, &product)) { @@ -934,6 +935,7 @@ pcity->food_stock = 0; pcity->shield_stock = 0; pcity->pollution = 0; + pcity->illness = 0; BV_CLR_ALL(pcity->city_options); pcity->production.kind = VUT_NONE; pcity->production.value.building = NULL; diff -Nurd -X.diff_ignore freeciv/common/city.c freeciv/common/city.c --- freeciv/common/city.c 2008-11-09 00:26:56.000000000 +0200 +++ freeciv/common/city.c 2009-05-11 01:13:45.000000000 +0300 @@ -2063,6 +2063,66 @@ } /************************************************************************** + Gets whether cities that pcity trades with had the plague. If so, it + returns the health penalty. + *************************************************************************/ +static int get_trade_illness(const struct city *pcity) +{ + int i; + int total_penalty = 0; + + for (i = 0 ; i < NUM_TRADEROUTES ; i++) { + struct city *trade_city = game_find_city_by_number(pcity->trade[i]); + if (trade_city != NULL + && trade_city->turn_plague != -1 + && trade_city->turn_plague - game.info.turn < 5) { + total_penalty += game.info.health_trade_penalty; + } + } + + return total_penalty; +} + +/************************************************************************** + Gets any effects ragarding health the city might have from buildings or + sabotage. +**************************************************************************/ +static int get_city_health(const struct city *pcity) +{ + return get_city_bonus(pcity, EFT_HEALTH); +} + +/************************************************************************** + Set city's illness. Illness cannot exceed 999, or be less then 0 + City illness is: (city_size - min_illness_size) + 10 * trade + routes to plagued cities - effect of buildings + *************************************************************************/ +int city_illness(const struct city *pcity, int *trade_ill, int *effects, + int *from_size) +{ + int size_mod = game.info.illness_safe_mod; + int trade_penalty = get_trade_illness(pcity); + int city_health_effects = get_city_health(pcity); + int illness = (pcity->size * pcity->size) - size_mod + trade_penalty + + pcity->pollution + city_health_effects; + + /* returning other data */ + if (trade_ill) { + *trade_ill = trade_penalty; + } + + if (effects) { + *effects = city_health_effects; + } + + if (from_size) { + *from_size = (pcity->size * pcity->size) - size_mod; + } + + return CLIP(0, illness , 999); +} + +/************************************************************************** Set food, trade and shields production in a city. This initializes the prod[] and waste[] arrays. It assumes that @@ -2256,6 +2316,9 @@ citizen_base_mood(pcity); pcity->pollution = city_pollution(pcity, pcity->prod[O_SHIELD]); + /* This must be after pollution */ + pcity->illness = city_illness(pcity, NULL, NULL, NULL); + happy_copy(pcity, FEELING_LUXURY); citizen_happy_luxury(pcity); /* with our new found luxuries */ @@ -2492,6 +2555,7 @@ pcity->food_stock = 0; pcity->shield_stock = 0; pcity->pollution = 0; + pcity->illness = 0; pcity->airlift = 0; pcity->debug = FALSE; @@ -2508,6 +2572,8 @@ pcity->steal = 0; #endif + pcity->turn_plague = -1; /* -1 = never */ + pcity->turn_founded = game.info.turn; pcity->turn_last_built = game.info.turn; diff -Nurd -X.diff_ignore freeciv/common/city.h freeciv/common/city.h --- freeciv/common/city.h 2009-03-19 18:05:43.000000000 +0200 +++ freeciv/common/city.h 2009-05-11 00:16:36.000000000 +0300 @@ -316,6 +316,7 @@ int food_stock; int shield_stock; int pollution; /* not saved */ + int illness; /* not saved */ /* turn states */ int airlift; @@ -324,6 +325,7 @@ bool did_sell; bool is_updated; /* not saved */ bool was_happy; + int turn_plague; /* last turn with plague in the city */ int anarchy; /* anarchy rounds count */ int rapture; /* rapture rounds count */ @@ -642,6 +644,8 @@ int city_pollution_types(const struct city *pcity, int shield_total, int *pollu_prod, int *pollu_pop, int *pollu_mod); int city_pollution(const struct city *pcity, int shield_total); +int city_illness(const struct city *pcity, int *trade_ill, int *effects, + int *from_size); bool city_exist(int id); diff -Nurd -X.diff_ignore freeciv/common/effects.c freeciv/common/effects.c --- freeciv/common/effects.c 2009-05-06 11:50:59.000000000 +0300 +++ freeciv/common/effects.c 2009-05-11 00:34:02.000000000 +0300 @@ -54,6 +54,7 @@ /* TODO: "Force_Content_Pct", */ "Give_Imm_Tech", "Growth_Food", + "Health", "Have_Embassies", "Make_Content", "Make_Content_Mil", diff -Nurd -X.diff_ignore freeciv/common/effects.h freeciv/common/effects.h --- freeciv/common/effects.h 2009-05-07 23:43:31.000000000 +0300 +++ freeciv/common/effects.h 2009-05-11 00:34:14.000000000 +0300 @@ -40,6 +40,7 @@ /* TODO: EFT_FORCE_CONTENT_PCT, */ EFT_GIVE_IMM_TECH, EFT_GROWTH_FOOD, + EFT_HEALTH, /* reduced illness due to buildings, ... */ EFT_HAVE_EMBASSIES, EFT_MAKE_CONTENT, EFT_MAKE_CONTENT_MIL, diff -Nurd -X.diff_ignore freeciv/common/events.c freeciv/common/events.c --- freeciv/common/events.c 2008-10-27 04:13:30.000000000 +0200 +++ freeciv/common/events.c 2009-05-11 00:40:31.000000000 +0300 @@ -98,6 +98,7 @@ GEN_EV(E_CITY_GRAN_THROTTLE, E_S_CITY, N_("Suggest Growth Throttling")), GEN_EV(E_CITY_TRANSFER, E_S_CITY, N_("Transfer")), GEN_EV(E_CITY_BUILD, E_S_CITY, N_("Was Built")), + GEN_EV(E_CITY_PLAGUE, E_S_CITY, N_("Has Plague")), GEN_EV(E_WORKLIST, E_S_CITY, N_("Worklist Events")), GEN_EV(E_CITY_PRODUCTION_CHANGED, E_S_CITY, N_("Production changed")), GEN_EV(E_MY_DIPLOMAT_BRIBE, E_S_D_ME, N_("Bribe")), diff -Nurd -X.diff_ignore freeciv/common/events.h freeciv/common/events.h --- freeciv/common/events.h 2008-10-27 04:13:30.000000000 +0200 +++ freeciv/common/events.h 2009-05-11 00:40:40.000000000 +0300 @@ -126,6 +126,7 @@ E_LOG_FATAL, E_TECH_GOAL, /* Changed tech goal */ E_UNIT_LOST_MISC, /* Non-battle unit deaths */ + E_CITY_PLAGUE, /* Plague within a city */ /* * Note: If you add a new event, make sure you make a similar change * to the events array in common/events.c using GEN_EV, diff -Nurd -X.diff_ignore freeciv/common/packets.def freeciv/common/packets.def --- freeciv/common/packets.def 2009-05-06 11:50:59.000000000 +0300 +++ freeciv/common/packets.def 2009-05-11 01:02:51.000000000 +0300 @@ -448,6 +448,9 @@ UINT8 granary_food_ini[MAX_GRANARY_INIS]; UINT8 granary_num_inis; UINT8 granary_food_inc; + BOOL plague_on; + UINT8 illness_safe_mod; + UINT8 health_trade_penalty; UINT8 tech_cost_style; UINT8 tech_leakage; YEAR tech_cost_double_year; @@ -538,6 +541,7 @@ UINT8 trade_value[NUM_TRADEROUTES]; UINT16 pollution; + UINT16 illness; UINT8 production_kind; UINT8 production_value; diff -Nurd -X.diff_ignore freeciv/data/civ1/game.ruleset freeciv/data/civ1/game.ruleset --- freeciv/data/civ1/game.ruleset 2009-04-29 20:40:46.000000000 +0300 +++ freeciv/data/civ1/game.ruleset 2009-05-11 00:16:36.000000000 +0300 @@ -77,6 +77,14 @@ granary_food_ini = 20 granary_food_inc = 10 +; Whether plagues are possible +plague_on = 0 +; how much trading with a plagued city increases our city's chance for plague +; (in tenth of percents) +health_trade_penalty = 10 +; how much to reduce from the basic chance (in tenth of percents) +illness_safe_mod = 20 + ; Method of calculating technology costs ; 0 - Civ (I|II) style. Every new tech add researchcost to cost of next tech. ; 1 - Cost of technology is diff -Nurd -X.diff_ignore freeciv/data/civ2/game.ruleset freeciv/data/civ2/game.ruleset --- freeciv/data/civ2/game.ruleset 2009-04-29 20:40:46.000000000 +0300 +++ freeciv/data/civ2/game.ruleset 2009-05-11 00:16:36.000000000 +0300 @@ -71,6 +71,14 @@ granary_food_ini = 20 granary_food_inc = 10 +; Whether plagues are possible +plague_on = 0 +; how much trading with a plagued city increases our city's chance for plague +; (in tenth of percents) +health_trade_penalty = 10 +; how much to reduce from the basic chance (in tenth of percents) +illness_safe_mod = 20 + ; Method of calculating technology costs ; 0 - Civ (I|II) style. Every new tech add researchcost to cost of next tech. ; 1 - Cost of technology is diff -Nurd -X.diff_ignore freeciv/data/default/game.ruleset freeciv/data/default/game.ruleset --- freeciv/data/default/game.ruleset 2009-04-29 20:40:46.000000000 +0300 +++ freeciv/data/default/game.ruleset 2009-05-11 00:18:53.000000000 +0300 @@ -89,6 +89,14 @@ granary_food_ini = 20 granary_food_inc = 10 +; Whether plagues are possible +plague_on = 0 +; how much trading with a plagued city increases our city's chance for plague +; (in tenth of percents) +health_trade_penalty = 10 +; how much to reduce from the basic chance (in tenth of percents) +illness_safe_mod = 20 + ; Method of calculating technology costs ; 0 - Civ (I|II) style. Every new tech add researchcost to cost of next tech. ; 1 - Cost of technology is diff -Nurd -X.diff_ignore freeciv/data/stdsounds.soundspec freeciv/data/stdsounds.soundspec --- freeciv/data/stdsounds.soundspec 2008-10-27 04:13:58.000000000 +0200 +++ freeciv/data/stdsounds.soundspec 2009-05-11 00:42:17.000000000 +0300 @@ -221,6 +221,7 @@ ;e_city_nuked = "" ;e_city_production_changed = "" ;e_city_transfer = "" +;e_city_plague = "" ;e_civil_war = "" ;e_connection = "" ;e_destroyed = "" diff -Nurd -X.diff_ignore freeciv/server/citytools.c freeciv/server/citytools.c --- freeciv/server/citytools.c 2009-05-06 11:50:59.000000000 +0300 +++ freeciv/server/citytools.c 2009-05-11 01:03:21.000000000 +0300 @@ -1805,6 +1805,7 @@ packet->food_stock=pcity->food_stock; packet->shield_stock=pcity->shield_stock; packet->pollution=pcity->pollution; + packet->illness = pcity->illness; packet->city_options = pcity->city_options; packet->production_kind = pcity->production.kind; diff -Nurd -X.diff_ignore freeciv/server/cityturn.c freeciv/server/cityturn.c --- freeciv/server/cityturn.c 2009-05-07 23:43:31.000000000 +0300 +++ freeciv/server/cityturn.c 2009-05-11 00:58:29.000000000 +0300 @@ -117,6 +117,7 @@ static void define_orig_production_values(struct city *pcity); static void update_city_activity(struct city *pcity); static void nullify_caravan_and_disband_plus(struct city *pcity); +static bool check_plague(const struct city * pcity); static float city_migration_score(const struct city *pcity); static bool do_city_migration(struct city *pcity_from, @@ -2081,6 +2082,18 @@ } pcity->was_happy = city_happy(pcity); + /* Handle the illness. */ + if (game.info.plague_on && pcity->size > 1) { + /* illness only if the city has a size greater than 1 */ + if (check_plague(pcity)) { + notify_player(pplayer, city_tile(pcity), E_CITY_PLAGUE, + _("%s had been struck by a plague! Population lost!"), + city_name(pcity)); + city_reduce_size(pcity, 1, NULL); + pcity->turn_plague = game.info.turn; + } + } + /* City population updated here, after the rapture stuff above. --Jing */ saved_id = pcity->id; city_populate(pcity); @@ -2139,6 +2152,18 @@ } } +/***************************************************************************** + check if city suffers from a plague. Return TRUE if it does, FALSE if not. + ****************************************************************************/ +static bool check_plague(const struct city * pcity) +{ + if (myrand(1000) < pcity->illness) { + return TRUE; + } + + return FALSE; +} + /************************************************************************** Disband a city into the built unit, supported by the closest city. **************************************************************************/ diff -Nurd -X.diff_ignore freeciv/server/ruleset.c freeciv/server/ruleset.c --- freeciv/server/ruleset.c 2009-05-07 23:43:31.000000000 +0300 +++ freeciv/server/ruleset.c 2009-05-11 00:16:36.000000000 +0300 @@ -2987,6 +2987,12 @@ game.control.description[0] = '\0'; } + game.info.health_trade_penalty = + secfile_lookup_int_default(&file, 10, "civstyle.health_trade_penalty"); + game.info.illness_safe_mod = + secfile_lookup_int_default(&file, 20, "civstyle.illness_safe_mod"); + game.info.plague_on = + secfile_lookup_int_default(&file, 0, "civstyle.plague_on"); game.info.base_pollution = secfile_lookup_int_default(&file, -20, "civstyle.base_pollution"); game.info.happy_cost = diff -Nurd -X.diff_ignore freeciv/server/savegame.c freeciv/server/savegame.c --- freeciv/server/savegame.c 2009-04-29 20:40:45.000000000 +0300 +++ freeciv/server/savegame.c 2009-05-11 00:16:36.000000000 +0300 @@ -2471,11 +2471,13 @@ secfile_lookup_int(file, "player%d.c%d.shield_stock", plrno, i); pcity->airlift = - secfile_lookup_int_default(file, 0, "player%d.c%d.airlift", - plrno,i); + secfile_lookup_int_default(file, 0, "player%d.c%d.airlift", plrno,i); pcity->was_happy = secfile_lookup_bool_default(file, FALSE, "player%d.c%d.was_happy", plrno,i); + pcity->turn_plague = + secfile_lookup_int_default(file, 0, "player%d.c%d.turn_plague", + plrno,i); pcity->anarchy = secfile_lookup_int(file, "player%d.c%d.anarchy", plrno, i); @@ -3596,9 +3598,11 @@ plrno, i); secfile_insert_int(file, pcity->airlift, "player%d.c%d.airlift", - plrno, i); + plrno, i); secfile_insert_bool(file, pcity->was_happy, "player%d.c%d.was_happy", plrno, i); + secfile_insert_int(file, pcity->turn_plague, "player%d.c%d.turn_plague", + plrno, i); secfile_insert_int(file, pcity->anarchy, "player%d.c%d.anarchy", plrno,i); secfile_insert_int(file, pcity->rapture, "player%d.c%d.rapture", plrno,i); diff -Nurd -X.diff_ignore freeciv/server/scripting/api.pkg freeciv/server/scripting/api.pkg --- freeciv/server/scripting/api.pkg 2009-01-25 16:51:09.000000000 +0200 +++ freeciv/server/scripting/api.pkg 2009-05-11 00:42:51.000000000 +0300 @@ -406,6 +406,7 @@ E_CITY_TRANSFER @ CITY_TRANSFER, E_CITY_BUILD @ CITY_BUILD, E_CITY_PRODUCTION_CHANGED @ CITY_PRODUCTION_CHANGED, + E_CITY_PLAGUE @ CITY_PLAGUE, E_WORKLIST @ WORKLIST, E_UPRISING @ UPRISING, E_CIVIL_WAR @ CIVIL_WAR, diff -Nurd -X.diff_ignore freeciv/version.in freeciv/version.in --- freeciv/version.in 2009-05-06 11:51:00.000000000 +0300 +++ freeciv/version.in 2009-05-11 01:02:59.000000000 +0300 @@ -23,5 +23,5 @@ # - Avoid adding a new mandatory capability to the development branch for # as long as possible. We want to maintain network compatibility with # the stable branch for as long as possible. -NETWORK_CAPSTRING_MANDATORY="+Freeciv.Devel.2009.May.06" +NETWORK_CAPSTRING_MANDATORY="+Freeciv.Devel.2009.May.11" NETWORK_CAPSTRING_OPTIONAL=""
_______________________________________________ Freeciv-dev mailing list Freeciv-dev@gna.org https://mail.gna.org/listinfo/freeciv-dev