Index: common/city.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/common/city.c,v retrieving revision 1.163 diff -u -r1.163 city.c --- common/city.c 2002/07/15 19:30:58 1.163 +++ common/city.c 2002/07/25 10:32:17 @@ -1062,7 +1062,7 @@ /************************************************************************** ... **************************************************************************/ -struct city *city_list_find_name(struct city_list *This, char *name) +struct city *city_list_find_name(struct city_list *This, const char *name) { struct genlist_iterator myiter; Index: common/city.h =================================================================== RCS file: /home/freeciv/CVS/freeciv/common/city.h,v retrieving revision 1.110 diff -u -r1.110 city.h --- common/city.h 2002/07/15 17:06:22 1.110 +++ common/city.h 2002/07/25 10:32:18 @@ -408,7 +408,7 @@ /* list functions */ struct city *city_list_find_id(struct city_list *This, int id); -struct city *city_list_find_name(struct city_list *This, char *name); +struct city *city_list_find_name(struct city_list *This, const char *name); int city_name_compare(const void *p1, const void *p2); Index: common/game.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/common/game.c,v retrieving revision 1.142 diff -u -r1.142 game.c --- common/game.c 2002/07/15 19:30:59 1.142 +++ common/game.c 2002/07/25 10:32:18 @@ -544,7 +544,7 @@ /************************************************************************** ... **************************************************************************/ -struct city *game_find_city_by_name(char *name) +struct city *game_find_city_by_name(const char *name) { players_iterate(pplayer) { struct city *pcity = city_list_find_name(&pplayer->cities, name); @@ -711,6 +711,7 @@ game.randseed=GAME_DEFAULT_RANDSEED; game.watchtower_vision=GAME_DEFAULT_WATCHTOWER_VISION; game.watchtower_extra_vision=GAME_DEFAULT_WATCHTOWER_EXTRA_VISION, + game.allowed_city_names = GAME_DEFAULT_ALLOWED_CITY_NAMES; sz_strlcpy(game.rulesetdir, GAME_DEFAULT_RULESETDIR); Index: common/game.h =================================================================== RCS file: /home/freeciv/CVS/freeciv/common/game.h,v retrieving revision 1.108 diff -u -r1.108 game.h --- common/game.h 2002/07/15 19:30:59 1.108 +++ common/game.h 2002/07/25 10:32:19 @@ -155,6 +155,7 @@ int watchtower_extra_vision; int watchtower_vision; + int allowed_city_names; char rulesetdir[MAX_LEN_NAME]; int firepower_factor; /* See README.rulesets */ @@ -231,7 +232,7 @@ void game_advance_year(void); int civ_population(struct player *pplayer); -struct city *game_find_city_by_name(char *name); +struct city *game_find_city_by_name(const char *name); struct unit *find_unit_by_id(int id); struct city *find_city_by_id(int id); @@ -449,6 +450,10 @@ #define GAME_DEFAULT_WATCHTOWER_EXTRA_VISION 0 #define GAME_MIN_WATCHTOWER_EXTRA_VISION 0 #define GAME_MAX_WATCHTOWER_EXTRA_VISION 2 + +#define GAME_DEFAULT_ALLOWED_CITY_NAMES 1 +#define GAME_MIN_ALLOWED_CITY_NAMES 0 +#define GAME_MAX_ALLOWED_CITY_NAMES 3 #define GAME_START_YEAR -4000 Index: server/cityhand.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/server/cityhand.c,v retrieving revision 1.114 diff -u -r1.114 cityhand.c --- server/cityhand.c 2002/06/12 07:24:48 1.114 +++ server/cityhand.c 2002/07/25 10:32:19 @@ -386,14 +386,19 @@ return; } - if((cp=get_sane_name(preq->name))) { - /* more sanity tests! any existing city with that name? */ - sz_strlcpy(pcity->name, cp); - city_refresh(pcity); - send_city_info(NULL, pcity); - } else { + cp = get_sane_name(preq->name); + if (!cp) { notify_player(pplayer, _("Game: %s is not a valid name."), preq->name); + return; } + + if (!is_allowed_city_name(pplayer, cp, pcity->x, pcity->y, TRUE)) { + return; + } + + sz_strlcpy(pcity->name, cp); + city_refresh(pcity); + send_city_info(NULL, pcity); } /************************************************************************** Index: server/citytools.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/server/citytools.c,v retrieving revision 1.183 diff -u -r1.183 citytools.c --- server/citytools.c 2002/07/03 07:59:02 1.183 +++ server/citytools.c 2002/07/25 10:32:21 @@ -55,7 +55,8 @@ static int evaluate_city_name_priority(int x, int y, struct city_name *city_name, int default_priority); -static char *search_for_city_name(int x, int y, struct city_name *city_names); +static char *search_for_city_name(int x, int y, struct city_name *city_names, + struct player *pplayer); static void server_set_tile_city(struct city *pcity, int city_x, int city_y, enum city_tile_type type); @@ -145,6 +146,24 @@ return (int)priority; } +/************************************************************************** +Checks if a city name belongs to default city names of a particular +player. +**************************************************************************/ +static bool is_default_city_name(const char *name, struct player *pplayer) +{ + struct nation_type *nation = get_nation_by_plr(pplayer); + int choice; + + for (choice = 0; nation->city_names[choice].name; choice++) { + if (mystrcasecmp(name, nation->city_names[choice].name) == 0) { + return TRUE; + } + } + + return FALSE; +} + /**************************************************************** Searches through a city name list (a struct city_name array) to pick the best available city name, and returns a pointer to @@ -153,19 +172,21 @@ city name. If the list has no valid entries in it, NULL will be returned. *****************************************************************/ -static char *search_for_city_name(int x, int y, struct city_name *city_names) +static char *search_for_city_name(int x, int y, struct city_name *city_names, + struct player *pplayer) { int choice, best_priority = -1; char* best_name = NULL; for (choice = 0; city_names[choice].name; choice++) { - if (!game_find_city_by_name(city_names[choice].name)) { + if (!game_find_city_by_name(city_names[choice].name) && + is_allowed_city_name(pplayer, city_names[choice].name, x, y, FALSE)) { int priority = evaluate_city_name_priority(x, y, &city_names[choice], - choice); + choice); if (best_priority == -1 || priority < best_priority) { - best_priority = priority; - best_name = city_names[choice].name; + best_priority = priority; + best_name = city_names[choice].name; } } } @@ -173,6 +194,75 @@ return best_name; } +/************************************************************************** +Checks, if a city name is allowed for a player. If not, reports a +reason for rejection. There's 4 different modes: +0: no restrictions, +1: a city name has to be unique to player +2: a city name has to be globally unique +3: a city name has to be globally unique, and players can't use names + that are in another player's default city names. (E.g. Swedish may not + call new cities or rename old cities as Helsinki, because it's in + Finns' default city names) +**************************************************************************/ +bool is_allowed_city_name(struct player *pplayer, const char *city_name, + int x, int y, bool notify_player) +{ + /* Mode 0: No restrictions to city names */ + if (game.allowed_city_names == 0) { + return TRUE; + } + + /* Mode 1: A city name has to be unique to player */ + if (game.allowed_city_names == 1 && + city_list_find_name(&pplayer->cities, city_name)) { + if (notify_player) { + notify_player_ex(pplayer, x, y, E_NOEVENT, + _("Game: You already have a city called %s"), + city_name); + } + return FALSE; + } + + /* + * Mode 3: Check first that the proposed city name is not in another + * player's default city names. + */ + if (game.allowed_city_names == 3) { + struct player *pother = NULL; + + players_iterate(player2) { + if (player2 != pplayer && is_default_city_name(city_name, player2)) { + pother = player2; + break; + } + } players_iterate_end; + + if (pother != NULL) { + if (notify_player) { + notify_player_ex(pplayer, x, y, E_NOEVENT, + _("Game: Can't use %s as a city name. " + "It is reserved for %s."), + city_name, get_nation_name_plural(pother->nation)); + } + return FALSE; + } + } + + /* Modes 2,3: A city name has to be globally unique */ + if ((game.allowed_city_names == 2 || + game.allowed_city_names == 3) && game_find_city_by_name(city_name)) { + if (notify_player) { + notify_player_ex(pplayer, x, y, E_NOEVENT, + _("Game: A city called %s already exists."), + city_name); + } + return FALSE; + } + + return TRUE; +} + /**************************************************************** Come up with a default name when a new city is about to be built. Handle running out of names etc. gracefully. Maybe we should keep @@ -197,13 +287,12 @@ CHECK_MAP_POS(x,y); - /* coastal */ - name = search_for_city_name(x, y, nation->city_names); + name = search_for_city_name(x, y, nation->city_names, pplayer); if (name) { return name; } - name = search_for_city_name(x, y, misc_city_names); + name = search_for_city_name(x, y, misc_city_names, pplayer); if (name) { return name; } @@ -841,6 +930,7 @@ struct player *pgiver = city_owner(pcity); int old_trade_routes[NUM_TRADEROUTES]; bool had_palace = pcity->improvements[B_PALACE] != I_NONE; + char old_city_name[MAX_LEN_NAME]; assert(pgiver != ptaker); @@ -865,6 +955,17 @@ give_citymap_from_player_to_player(pcity, pgiver, ptaker); map_unfog_pseudo_city_area(ptaker, pcity->x, pcity->y); + + sz_strlcpy(old_city_name, pcity->name); + if (game.allowed_city_names == 1 && + city_list_find_name(&ptaker->cities, pcity->name)) { + sz_strlcpy(pcity->name, + city_name_suggestion(ptaker, pcity->x, pcity->y)); + notify_player_ex(ptaker, pcity->x, pcity->y, E_NOEVENT, + _("You already had a city called %s." + " The city was renamed to %s."), old_city_name, + pcity->name); + } /* Has to follow the unfog call above. */ city_list_unlink(&pgiver->cities, pcity); Index: server/citytools.h =================================================================== RCS file: /home/freeciv/CVS/freeciv/server/citytools.h,v retrieving revision 1.39 diff -u -r1.39 citytools.h --- server/citytools.h 2002/04/30 12:52:58 1.39 +++ server/citytools.h 2002/07/25 10:32:21 @@ -80,6 +80,8 @@ void change_build_target(struct player *pplayer, struct city *pcity, int target, bool is_unit, int event); +bool is_allowed_city_name(struct player *pplayer, const char *city_name, + int x, int y, bool notify_player); char *city_name_suggestion(struct player *pplayer, int x, int y); extern struct city_name *misc_city_names; Index: server/savegame.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/server/savegame.c,v retrieving revision 1.83 diff -u -r1.83 savegame.c --- server/savegame.c 2002/07/15 19:31:01 1.83 +++ server/savegame.c 2002/07/25 10:32:22 @@ -1822,6 +1822,9 @@ "game.occupychance"); game.randseed = secfile_lookup_int_default(file, game.randseed, "game.randseed"); + game.allowed_city_names = + secfile_lookup_int_default(file, game.allowed_city_names, + "game.allowed_city_names"); if(game.civstyle == 1) { string = "civ1"; @@ -2147,6 +2150,7 @@ secfile_insert_str(file, game.demography, "game.demography"); secfile_insert_int(file, game.watchtower_vision, "game.watchtower_vision"); secfile_insert_int(file, game.watchtower_extra_vision, "game.watchtower_extra_vision"); + secfile_insert_int(file, game.allowed_city_names, "game.allowed_city_names"); if (TRUE) { /* Now always save these, so the server options reflect the Index: server/stdinhand.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/server/stdinhand.c,v retrieving revision 1.236 diff -u -r1.236 stdinhand.c --- server/stdinhand.c 2002/07/23 19:24:08 1.236 +++ server/stdinhand.c 2002/07/25 10:32:25 @@ -543,6 +543,19 @@ GAME_MIN_WATCHTOWER_EXTRA_VISION, GAME_MAX_WATCHTOWER_EXTRA_VISION, GAME_DEFAULT_WATCHTOWER_EXTRA_VISION) + GEN_INT("citynames", game.allowed_city_names, SSET_RULES, SSET_TO_CLIENT, + N_("Allowed city names"), + N_("If set to 0, there are no restrictions: players can have " + "multiple cities with the same names. " + "If set to 1, city names have to be unique to a player: " + "one player can't have multiple cities with the same name. " + "If set to 2 or 3, city names have to be globally unique: " + "all cities in a game have to have different names. " + "If set to 3, a player isn't allowed to use a default city name " + "of another nations."),NULL, + GAME_MIN_ALLOWED_CITY_NAMES, GAME_MAX_ALLOWED_CITY_NAMES, + GAME_DEFAULT_ALLOWED_CITY_NAMES) + /* Flexible rules: these can be changed after the game has started. * * The distinction between "rules" and "flexible rules" is not always Index: server/unithand.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/server/unithand.c,v retrieving revision 1.228 diff -u -r1.228 unithand.c --- server/unithand.c 2002/07/18 08:58:59 1.228 +++ server/unithand.c 2002/07/25 10:32:26 @@ -532,13 +532,18 @@ char *name) { char *city_name = get_sane_name(name); - + if (!city_name) { notify_player_ex(pplayer, punit->x, punit->y, E_NOEVENT, _("Game: Let's not build a city with " "such a stupid name.")); return; } + + if (!is_allowed_city_name(pplayer, city_name, punit->x, punit->y, TRUE)) { + return; + } + create_city(pplayer, punit->x, punit->y, city_name); wipe_unit(punit); }