<URL: http://bugs.freeciv.org/Ticket/Display.html?id=40612 >

The migration patch (version 6)

changes:

- split the patch in three parts:
  * the patch
  * patch to add an option for the food check
  * some freelog(LOG_NORMAL,...) debugging messages

The patch was tested.

Matthias

diff -ur -X./freeciv-2.1.99svn15393/diff_ignore freeciv-2.1.99svn.patch_migration_food/server/cityturn.c freeciv-2.1.99svn.patch_migration_debug/server/cityturn.c
--- freeciv-2.1.99svn.patch_migration_food/server/cityturn.c	2009-01-04 21:28:25.000000000 +0100
+++ freeciv-2.1.99svn.patch_migration_debug/server/cityturn.c	2009-01-04 22:19:27.000000000 +0100
@@ -1466,7 +1466,7 @@
         utype_name_translation(utype));
 
     /* Log before signal emitting, so pointers are certainly valid */
-    freelog(LOG_VERBOSE, "%s %s tried to build %s, which is not available.",
+    freelog(LOG_NORMAL, "%s %s tried to build %s, which is not available.",
             nation_rule_name(nation_of_city(pcity)),
             city_name(pcity),
             utype_rule_name(utype));
@@ -2076,7 +2076,7 @@
                   player_name(pplayer_from), city_name(pcity_to));
   }
 
-  freelog(LOG_DEBUG, "[M] T%d migration successful (%s -> %s)",
+  freelog(LOG_NORMAL, "[M] T%d migration successful (%s -> %s)",
           game.info.turn, city_from_name, city_name(pcity_to));
 
   return TRUE;
@@ -2136,7 +2136,7 @@
     float score_from = city_score(pcity) * 3;
     float score_tmp = 0;
 
-    freelog(LOG_DEBUG, "[M] T%d check city: %s score: %6.3f (%s)",
+    freelog(LOG_NORMAL, "[M] T%d check city: %s score: %6.3f (%s)",
             game.info.turn, city_name(pcity), score_from,
             player_name(pplayer));
 
@@ -2161,7 +2161,7 @@
       score_tmp = city_score(acity) * (GAME_MAX_MIGRATION_DIST + 1 - dist)
                   / (GAME_MAX_MIGRATION_DIST + 1);
 
-      freelog(LOG_DEBUG, "[M] T%d - compare city: %s (%s) dist: %d "
+      freelog(LOG_NORMAL, "[M] T%d - compare city: %s (%s) dist: %d "
               "score: %6.3f", game.info.turn, city_name(acity),
               player_name(city_owner(acity)), dist, score_tmp);
 
@@ -2173,7 +2173,7 @@
           best_city_player_score = score_tmp;
           best_city_player = acity;
 
-          freelog(LOG_DEBUG, "[M] T%d - best city (player): %s (%s) score: "
+          freelog(LOG_NORMAL, "[M] T%d - best city (player): %s (%s) score: "
                   "%6.3f (> %6.3f)", game.info.turn,
                   city_name(best_city_player), player_name(pplayer),
                   best_city_player_score, score_from);
@@ -2187,7 +2187,7 @@
           best_city_world_score = score_tmp;
           best_city_world = acity;
 
-          freelog(LOG_DEBUG, "[M] T%d - best city (world): %s (%s) score: "
+          freelog(LOG_NORMAL, "[M] T%d - best city (world): %s (%s) score: "
                   "%6.3f (> %6.3f)", game.info.turn,
                   city_name(best_city_world),
                   player_name(city_owner(best_city_world)),
diff -ur -X./freeciv-2.1.99svn15393/diff_ignore freeciv-2.1.99svn.patch_migration/common/game.c freeciv-2.1.99svn.patch_migration_food/common/game.c
--- freeciv-2.1.99svn.patch_migration/common/game.c	2009-01-04 21:22:21.000000000 +0100
+++ freeciv-2.1.99svn.patch_migration_food/common/game.c	2009-01-04 21:22:39.000000000 +0100
@@ -276,6 +276,7 @@
   game.info.savepalace    = GAME_DEFAULT_SAVEPALACE;
   game.info.natural_city_names = GAME_DEFAULT_NATURALCITYNAMES;
   game.info.migrationturn = GAME_DEFAULT_MIGRATION_TURN;
+  game.info.migrationturn = GAME_DEFAULT_MIGRATION_FOOD;
   game.info.migrationdist = GAME_DEFAULT_MIGRATION_DIST;
   game.info.migrationworld = GAME_DEFAULT_MIGRATION_WORLD;
   game.info.migrationplayer = GAME_DEFAULT_MIGRATION_PLAYER;
diff -ur -X./freeciv-2.1.99svn15393/diff_ignore freeciv-2.1.99svn.patch_migration/common/game.h freeciv-2.1.99svn.patch_migration_food/common/game.h
--- freeciv-2.1.99svn.patch_migration/common/game.h	2009-01-04 21:22:21.000000000 +0100
+++ freeciv-2.1.99svn.patch_migration_food/common/game.h	2009-01-04 21:22:39.000000000 +0100
@@ -254,6 +254,8 @@
 #define GAME_MIN_MIGRATION_TURN      0
 #define GAME_MAX_MIGRATION_TURN      100
 
+#define GAME_DEFAULT_MIGRATION_FOOD   TRUE
+
 #define GAME_DEFAULT_MIGRATION_DIST   3
 #define GAME_MIN_MIGRATION_DIST       1
 #define GAME_MAX_MIGRATION_DIST       7
diff -ur -X./freeciv-2.1.99svn15393/diff_ignore freeciv-2.1.99svn.patch_migration/common/packets.def freeciv-2.1.99svn.patch_migration_food/common/packets.def
--- freeciv-2.1.99svn.patch_migration/common/packets.def	2009-01-04 21:22:21.000000000 +0100
+++ freeciv-2.1.99svn.patch_migration_food/common/packets.def	2009-01-04 21:22:39.000000000 +0100
@@ -415,6 +415,7 @@
   BOOL savepalace;
   BOOL natural_city_names;
   UINT8 migrationturn;
+  BOOL migrationfood;
   UINT8 migrationdist;
   UINT8 migrationplayer;
   UINT8 migrationworld;
diff -ur -X./freeciv-2.1.99svn15393/diff_ignore freeciv-2.1.99svn.patch_migration/server/cityturn.c freeciv-2.1.99svn.patch_migration_food/server/cityturn.c
--- freeciv-2.1.99svn.patch_migration/server/cityturn.c	2009-01-04 21:27:30.000000000 +0100
+++ freeciv-2.1.99svn.patch_migration_food/server/cityturn.c	2009-01-04 21:28:25.000000000 +0100
@@ -1970,7 +1970,7 @@
   char city_from_name[MAX_LEN_NAME];
   struct city *rcity = NULL;
 
-  if (pcity_to->surplus[O_FOOD] < 0) {
+  if (game.info.migrationfood && pcity_to->surplus[O_FOOD] < 0) {
     /* insufficiency food in receiver city; no additional citizens */
     if (pplayer_from == pplayer_to) {
       /* migration between one nation */
diff -ur -X./freeciv-2.1.99svn15393/diff_ignore freeciv-2.1.99svn.patch_migration/server/savegame.c freeciv-2.1.99svn.patch_migration_food/server/savegame.c
--- freeciv-2.1.99svn.patch_migration/server/savegame.c	2009-01-04 21:22:21.000000000 +0100
+++ freeciv-2.1.99svn.patch_migration_food/server/savegame.c	2009-01-04 21:22:39.000000000 +0100
@@ -4294,6 +4294,9 @@
     game.info.migrationturn =
       secfile_lookup_int_default(file, game.info.migrationturn,
                                  "game.migrationturn");
+    game.info.migrationfood =
+      secfile_lookup_bool_default(file, game.info.migrationfood,
+                                 "game.migrationfood");
     game.info.migrationdist =
       secfile_lookup_int_default(file, game.info.migrationdist,
                                  "game.migrationdist");
@@ -4956,6 +4959,7 @@
   secfile_insert_int(file, game.info.diplomacy, "game.diplomacy");
   secfile_insert_int(file, game.info.allowed_city_names, "game.allowed_city_names");
   secfile_insert_int(file, game.info.migrationturn, "game.migrationturn");
+  secfile_insert_bool(file, game.info.migrationfood, "game.migrationfood");
   secfile_insert_int(file, game.info.migrationdist, "game.migrationdist");
   secfile_insert_int(file, game.info.migrationplayer, "game.migrationplayer");
   secfile_insert_int(file, game.info.migrationworld, "game.migrationworld");
diff -ur -X./freeciv-2.1.99svn15393/diff_ignore freeciv-2.1.99svn.patch_migration/server/settings.c freeciv-2.1.99svn.patch_migration_food/server/settings.c
--- freeciv-2.1.99svn.patch_migration/server/settings.c	2009-01-04 21:22:21.000000000 +0100
+++ freeciv-2.1.99svn.patch_migration_food/server/settings.c	2009-01-04 21:22:39.000000000 +0100
@@ -854,6 +854,13 @@
           GAME_MIN_MIGRATION_TURN, GAME_MAX_MIGRATION_TURN,
           GAME_DEFAULT_MIGRATION_TURN)
 
+  GEN_BOOL("migrationfood", game.info.migrationfood,
+          SSET_RULES_FLEXIBLE, SSET_SOCIOLOGY, SSET_RARE, SSET_TO_CLIENT,
+           N_("Migration is limited by the amount of food available"),
+           N_("If enabled, migration is limited by the amount of food "
+              "available in the receiving city."),
+           NULL, GAME_DEFAULT_MIGRATION_FOOD)
+
   GEN_INT("migrationdist", game.info.migrationdist,
           SSET_RULES_FLEXIBLE, SSET_SOCIOLOGY, SSET_RARE, SSET_TO_CLIENT,
           N_("Distance for migration between cities"),
diff -ur -X./freeciv-2.1.99svn15393/diff_ignore freeciv-2.1.99svn15393/common/game.c freeciv-2.1.99svn.patch_migration/common/game.c
--- freeciv-2.1.99svn15393/common/game.c	2008-12-25 19:45:44.000000000 +0100
+++ freeciv-2.1.99svn.patch_migration/common/game.c	2009-01-04 21:22:21.000000000 +0100
@@ -275,6 +275,10 @@
   game.info.celebratesize = GAME_DEFAULT_CELEBRATESIZE;
   game.info.savepalace    = GAME_DEFAULT_SAVEPALACE;
   game.info.natural_city_names = GAME_DEFAULT_NATURALCITYNAMES;
+  game.info.migrationturn = GAME_DEFAULT_MIGRATION_TURN;
+  game.info.migrationdist = GAME_DEFAULT_MIGRATION_DIST;
+  game.info.migrationworld = GAME_DEFAULT_MIGRATION_WORLD;
+  game.info.migrationplayer = GAME_DEFAULT_MIGRATION_PLAYER;
   game.info.angrycitizen  = GAME_DEFAULT_ANGRYCITIZEN;
   game.info.foodbox       = GAME_DEFAULT_FOODBOX;
   game.info.shieldbox = GAME_DEFAULT_SHIELDBOX;
diff -ur -X./freeciv-2.1.99svn15393/diff_ignore freeciv-2.1.99svn15393/common/game.h freeciv-2.1.99svn.patch_migration/common/game.h
--- freeciv-2.1.99svn15393/common/game.h	2008-12-30 23:29:33.000000000 +0100
+++ freeciv-2.1.99svn.patch_migration/common/game.h	2009-01-04 21:22:21.000000000 +0100
@@ -250,6 +250,22 @@
 
 #define GAME_DEFAULT_NATURALCITYNAMES TRUE
 
+#define GAME_DEFAULT_MIGRATION_TURN  0 /* 0 = no migration */
+#define GAME_MIN_MIGRATION_TURN      0
+#define GAME_MAX_MIGRATION_TURN      100
+
+#define GAME_DEFAULT_MIGRATION_DIST   3
+#define GAME_MIN_MIGRATION_DIST       1
+#define GAME_MAX_MIGRATION_DIST       7
+
+#define GAME_DEFAULT_MIGRATION_PLAYER 50
+#define GAME_MIN_MIGRATION_PLAYER    0
+#define GAME_MAX_MIGRATION_PLAYER    100
+
+#define GAME_DEFAULT_MIGRATION_WORLD 10
+#define GAME_MIN_MIGRATION_WORLD     0
+#define GAME_MAX_MIGRATION_WORLD     100
+
 #define GAME_DEFAULT_AQUEDUCTLOSS    0
 #define GAME_MIN_AQUEDUCTLOSS        0
 #define GAME_MAX_AQUEDUCTLOSS        100
diff -ur -X./freeciv-2.1.99svn15393/diff_ignore freeciv-2.1.99svn15393/common/packets.def freeciv-2.1.99svn.patch_migration/common/packets.def
--- freeciv-2.1.99svn15393/common/packets.def	2008-12-30 23:29:33.000000000 +0100
+++ freeciv-2.1.99svn.patch_migration/common/packets.def	2009-01-04 21:22:21.000000000 +0100
@@ -414,6 +414,10 @@
   UINT8 razechance;
   BOOL savepalace;
   BOOL natural_city_names;
+  UINT8 migrationturn;
+  UINT8 migrationdist;
+  UINT8 migrationplayer;
+  UINT8 migrationworld;
   BOOL turnblock;
   BOOL fixedlength;
   BOOL auto_ai_toggle;
diff -ur -X./freeciv-2.1.99svn15393/diff_ignore freeciv-2.1.99svn15393/server/cityturn.c freeciv-2.1.99svn.patch_migration/server/cityturn.c
--- freeciv-2.1.99svn15393/server/cityturn.c	2008-12-25 19:45:49.000000000 +0100
+++ freeciv-2.1.99svn.patch_migration/server/cityturn.c	2009-01-04 21:27:30.000000000 +0100
@@ -90,6 +90,12 @@
 static void update_city_activity(struct player *pplayer, struct city *pcity);
 static void nullify_caravan_and_disband_plus(struct city *pcity);
 
+static float city_score(const struct city *pcity);
+static bool make_city_migration(const struct player *pplayer_from,
+                                struct city *pcity_from,
+                                const struct player *pplayer_to,
+                                struct city *pcity_to);
+
 /**************************************************************************
 ...
 **************************************************************************/
@@ -1867,3 +1873,363 @@
   remove_city(pcity);
   return TRUE;
 }
+
+/***************************************************************************
+  Helper function to calculate  a score of a city. It indicates the
+  "migration desirability" of the city. The higher the score the more likely
+  citizens will migrate to it.
+
+  The score depends on the city size, the feeling of its citizens and the 
+  surplus of trade, luxury and science.
+
+  formula:
+    score = ([city size] + feeling) * factors
+
+  * feeling of the citizens
+    feeling = 1.00 * happy citizens
+            + 0.00 * content citizens
+            - 0.25 * unhappy citizens
+            - 0.50 * unhappy citizens
+
+  * factors
+    * the build costs of all buildings
+      f = (1 + (1 - exp(-[build shield cost]/1000))/5)
+    * the trade of the city
+      f = (1 + (1 - exp(-[city surplus trade]/100))/5)
+    * the luxury within the city
+      f = (1 + (1 - exp(-[city surplus luxury]/100))/5)
+    * the science within the city
+      f = (1 + (1 - exp(-[city surplus science]/100))/5)
+
+  all factors f have values between 1 and 1.2; the overall factor will be
+  between 1.0 (smaller cities) and 2.0 (bigger cities)
+
+  [build shield cost], [city surplus trade], [city surplus luxury] and
+  [city surplus science] _must_ be >= 0!
+
+  * if the city has at least one wonder a factor of 1.25 is added
+  * for the capital an additional factor of 1.25 is used
+**************************************************************************/
+static float city_score(const struct city *pcity)
+{
+  float score = 0.0;
+  int build_shield_cost = 0;
+  bool has_wonder = FALSE;
+
+  if (!pcity) {
+    return 0.0;
+  }
+
+  /* feeling of the citizens */
+  score = pcity->size + 1.00 * pcity->feel[CITIZEN_HAPPY][FEELING_FINAL]
+          + 0.00 * pcity->feel[CITIZEN_CONTENT][FEELING_FINAL]
+          - 0.25 * pcity->feel[CITIZEN_UNHAPPY][FEELING_FINAL]
+          - 0.50 * pcity->feel[CITIZEN_ANGRY][FEELING_FINAL];
+
+  /* calculate shield build cost for all buildings */
+  city_built_iterate(pcity, pimprove) {
+    build_shield_cost += impr_build_shield_cost(pimprove);
+    if (is_wonder(pimprove)) {
+      /* this city has a wonder */
+      has_wonder = TRUE;
+    }
+  } city_built_iterate_end;
+  /* take shield costs of all buidings into account; normalized by 1000 */
+  score *= (1 + (1 - exp(-(float)build_shield_cost/1000))/5);
+  /* take trade into account; normalized by 100 */
+  score *= (1 + (1 - exp(-(float)pcity->surplus[O_TRADE]/100))/5);
+  /* take luxury into account; normalized by 100 */
+  score *= (1 + (1 - exp(-(float)pcity->surplus[O_LUXURY]/100))/5);
+  /* take science into account; normalized by 100 */
+  score *= (1 + (1 - exp(-(float)pcity->surplus[O_SCIENCE]/100))/5);
+
+  if (has_wonder) {
+    /* people like wonders */
+    score *= 1.25;
+  }
+
+  if (is_capital(pcity)) {
+    /* the capital is a magnet for the citizens */
+    score *= 1.25;
+  }
+
+  freelog(LOG_DEBUG, "[M] %s score: %.3f", city_name(pcity), score);
+
+  return score;
+}
+
+/**************************************************************************
+  Do the migrations between the cities that overlap, if the growth of the
+  target city is not blocked due to a missing improvement or missing food.
+**************************************************************************/
+static bool make_city_migration(const struct player *pplayer_from,
+                                struct city *pcity_from,
+                                const struct player *pplayer_to,
+                                struct city *pcity_to)
+{
+  char city_from_name[MAX_LEN_NAME];
+  struct city *rcity = NULL;
+
+  if (pcity_to->surplus[O_FOOD] < 0) {
+    /* insufficiency food in receiver city; no additional citizens */
+    if (pplayer_from == pplayer_to) {
+      /* migration between one nation */
+      notify_player(pplayer_to, city_tile(pcity_to), E_CITY_TRANSFER,
+                    _("Migrants from %s can't go to %s because there is "
+                      "not enough food available!"),
+                    city_name(pcity_from), city_name(pcity_to));
+    } else {
+      /* migration between different nations */
+      notify_player(pplayer_from, city_tile(pcity_to), E_CITY_TRANSFER,
+                    _("Migrants from %s can't go to %s (%s) because there "
+                      "is not enough food available!"),
+                    city_name(pcity_from), city_name(pcity_to),
+                    player_name(pplayer_to));
+      notify_player(pplayer_to, city_tile(pcity_to), E_CITY_TRANSFER,
+                    _("Migrants from %s (%s) can't go to %s because there "
+                      "is not enough food available!"),
+                    city_name(pcity_from), player_name(pplayer_from),
+                    city_name(pcity_to));
+    }
+
+    return FALSE;
+  }
+
+  if (!city_can_grow_to(pcity_to, pcity_to->size + 1)) {
+    /* receiver city can't grow  */
+    if (pplayer_from == pplayer_to) {
+      /* migration between one nation */
+      notify_player(pplayer_to, city_tile(pcity_to), E_CITY_TRANSFER,
+                    _("Migrants from %s can't go to %s because it needs "
+                      "an improvement to grow!"),
+                    city_name(pcity_from), city_name(pcity_to));
+    } else {
+      /* migration between different nations */
+      notify_player(pplayer_from, city_tile(pcity_to), E_CITY_TRANSFER,
+                    _("Migrants from %s can't go to %s of %s because it "
+                      "needs an improvement to grow!"),
+                    city_name(pcity_from), city_name(pcity_to),
+                    player_name(pplayer_to));
+      notify_player(pplayer_to, city_tile(pcity_to), E_CITY_TRANSFER,
+                    _("Migrants from %s of %s can't go to %s because it "
+                      "needs an improvement to grow!"),
+                    city_name(pcity_from), player_name(pplayer_from),
+                    city_name(pcity_to));
+    }
+
+    return FALSE;
+  }
+
+  /* we copy the city name, maybe we disband it! */
+  sz_strlcpy(city_from_name, city_name(pcity_from));
+  /* reduce size of giver */
+  if (pcity_from->size == 1) {
+    /* do not destroy wonders */
+    city_built_iterate(pcity_from, pimprove) {
+      if (is_wonder(pimprove)) {
+        return FALSE;
+      }
+    } city_built_iterate_end;
+
+    /* find closest city other of the same player than pcity_from */
+    rcity = find_closest_owned_city(pplayer_from, city_tile(pcity_from),
+                                    FALSE, pcity_from);
+
+    if (rcity) {
+      /* transfer all units to the closest city */
+      transfer_city_units(pplayer_from, pplayer_from,
+                          pcity_from->units_supported, rcity, pcity_from,
+                          -1, TRUE);
+      remove_city(pcity_from);
+
+      notify_player(pplayer_from, city_tile(pcity_from), E_CITY_TRANSFER,
+                    _("%s was disbanded by its citizens."),
+                    city_from_name);
+    } else {
+      /* it's the only city of the nation */
+      return FALSE;
+    }
+  } else {
+    city_reduce_size(pcity_from, 1, pplayer_from);
+    city_refresh_vision(pcity_from);
+    city_refresh(pcity_from);
+  }
+  /* raise size of receiver city */
+  city_increase_size(pcity_to);
+  city_refresh_vision(pcity_to);
+  city_refresh(pcity_to);
+
+  if (pplayer_from == pplayer_to) {
+    /* migration between one nation */
+    notify_player(pplayer_from, city_tile(pcity_to), E_CITY_TRANSFER,
+                  _("Migrants from %s moved to %s in search for a better "
+                    "life."), city_from_name, city_name(pcity_to));
+  } else {
+    /* migration between different nations */
+    notify_player(pplayer_from, city_tile(pcity_to), E_CITY_TRANSFER,
+                  _("Migrants from %s moved to %s (%s) in search for a "
+                    "better life."), city_from_name, city_name(pcity_to),
+                  player_name(pplayer_to));
+    notify_player(pplayer_to, city_tile(pcity_to), E_CITY_TRANSFER,
+                  _("Migrants from %s (%s) moved to %s in search for a "
+                    "better life."), city_from_name,
+                  player_name(pplayer_from), city_name(pcity_to));
+  }
+
+  freelog(LOG_DEBUG, "[M] T%d migration successful (%s -> %s)",
+          game.info.turn, city_from_name, city_name(pcity_to));
+
+  return TRUE;
+}
+
+/**************************************************************************
+  Check for citizens who want to migrate between the cities that overlap.
+  Migrants go to the city with higher score, if the growth of the target
+  city is not blocked due to a missing improvement.
+
+  The following setting are used:
+
+  'game.info.migrationsturn' controls the number of turns between
+  migration checks for one city (counted from the founding). If this
+  setting is zero, or it is the first turn (T0), migration does no occur.
+
+  'game.info.migrationsdist' is the maximal distance for migration.
+
+  'game.info.migrationsplayer' gives the chance for migration within one
+  nation.
+
+  'game.info.migrationsworld' gives the chance for migration between all
+  nations.
+**************************************************************************/
+void check_city_migrations(struct player *pplayer)
+{
+  if (!pplayer || game.info.migrationturn <= 0 || game.info.turn <= 0
+      || (game.info.migrationworld == 0 && game.info.migrationplayer == 0)
+      ) {
+    return;
+  }
+
+  /* check for each city
+   * city_list_iterate_safe_end must be used because we could
+   * remove one city from the list */
+  city_list_iterate_safe(pplayer->cities, pcity) {
+    /* no migration out of the capital */
+    if (is_capital(pcity)) {
+      continue;
+    }
+
+    /* check only each (game.info.migrationturn) turn
+     * (counted from the funding turn) */
+    if (((game.info.turn - pcity->turn_founded)
+         % game.info.migrationturn) > 0) {
+      continue;
+    }
+
+    float best_city_player_score = 0;
+    struct city *best_city_player = NULL;
+
+    float best_city_world_score = 0;
+    struct city *best_city_world = NULL;
+
+    /* score of the actual city
+     * taking into account a persistence factor of 3 */
+    float score_from = city_score(pcity) * 3;
+    float score_tmp = 0;
+
+    freelog(LOG_DEBUG, "[M] T%d check city: %s score: %6.3f (%s)",
+            game.info.turn, city_name(pcity), score_from,
+            player_name(pplayer));
+
+    best_city_player_score = 0;
+    best_city_world_score = 0;
+
+    /* loop over all tiles in a circle of game.info.migrationdist radius
+     * in search for cities which overlap */
+    circle_iterate(city_tile(pcity),(game.info.migrationdist *
+                                     game.info.migrationdist + 1), ptile) {
+      struct city *acity = tile_city(ptile);
+
+      if (!acity || acity == pcity) {
+        /* no city or the city in the center */
+        continue;
+      }
+
+      /* distance between the two cities */
+      int dist = real_map_distance(city_tile(pcity), city_tile(acity));
+
+      /* score of the second city, weighted by the distance */
+      score_tmp = city_score(acity) * (GAME_MAX_MIGRATION_DIST + 1 - dist)
+                  / (GAME_MAX_MIGRATION_DIST + 1);
+
+      freelog(LOG_DEBUG, "[M] T%d - compare city: %s (%s) dist: %d "
+              "score: %6.3f", game.info.turn, city_name(acity),
+              player_name(city_owner(acity)), dist, score_tmp);
+
+      if (game.info.migrationplayer != 0 && city_owner(acity) == pplayer) {
+        /* inmigrate in cities of the same owner */
+        if ((score_tmp > score_from)
+            && (score_tmp > best_city_player_score)) {
+          /* select the best! */
+          best_city_player_score = score_tmp;
+          best_city_player = acity;
+
+          freelog(LOG_DEBUG, "[M] T%d - best city (player): %s (%s) score: "
+                  "%6.3f (> %6.3f)", game.info.turn,
+                  city_name(best_city_player), player_name(pplayer),
+                  best_city_player_score, score_from);
+        }
+      } else if (game.info.migrationworld != 0
+                 && city_owner(acity) != pplayer) {
+        /* inmigrate between cities of different owners */
+        if ((score_tmp > score_from)
+            && (score_tmp > best_city_world_score)) {
+          /* select the best! */
+          best_city_world_score = score_tmp;
+          best_city_world = acity;
+
+          freelog(LOG_DEBUG, "[M] T%d - best city (world): %s (%s) score: "
+                  "%6.3f (> %6.3f)", game.info.turn,
+                  city_name(best_city_world),
+                  player_name(city_owner(best_city_world)),
+                  best_city_world_score, score_from);
+        }
+      }
+    } circle_iterate_end;
+
+    if (best_city_player_score > 0) {
+      /* first, do the migration within one nation */
+      if (myrand (100) >= game.info.migrationplayer) {
+        /* no migration */
+        notify_player(pplayer, city_tile(pcity), E_CITY_TRANSFER,
+                      _("Citizens of %s are thinking about migration to %s "
+                        "in search for a better life."), pcity->name,
+                      city_name(best_city_player));
+      } else {
+        make_city_migration(pplayer, pcity, pplayer, best_city_player);
+      }
+
+      /* stop here */
+      continue;
+    }
+
+    if (best_city_world_score > 0) {
+      /* second, do the migration between all nations */
+      if (myrand (100) >= game.info.migrationworld) {
+        /* no migration */
+        notify_player(pplayer, city_tile(pcity), E_CITY_TRANSFER,
+                      _("Citizens of %s are thinking about migration to %s "
+                        "of %s in search for a better life."),
+                      city_name(pcity), city_name(best_city_world),
+                      player_name(city_owner(best_city_world)));
+      } else {
+        make_city_migration(pplayer, pcity, city_owner(best_city_world),
+                            best_city_world);
+      }
+
+      /* stop here */
+      continue;
+    }
+
+  } city_list_iterate_safe_end;
+
+}
diff -ur -X./freeciv-2.1.99svn15393/diff_ignore freeciv-2.1.99svn15393/server/cityturn.h freeciv-2.1.99svn.patch_migration/server/cityturn.h
--- freeciv-2.1.99svn15393/server/cityturn.h	2008-12-25 19:45:49.000000000 +0100
+++ freeciv-2.1.99svn.patch_migration/server/cityturn.h	2009-01-04 21:22:21.000000000 +0100
@@ -46,4 +46,7 @@
 void advisor_choose_build(struct player *pplayer, struct city *pcity);
 
 void nullify_prechange_production(struct city *pcity);
+
+void check_city_migrations(struct player *pplayer);
+
 #endif  /* FC__CITYTURN_H */
diff -ur -X./freeciv-2.1.99svn15393/diff_ignore freeciv-2.1.99svn15393/server/savegame.c freeciv-2.1.99svn.patch_migration/server/savegame.c
--- freeciv-2.1.99svn15393/server/savegame.c	2008-12-27 17:25:17.000000000 +0100
+++ freeciv-2.1.99svn.patch_migration/server/savegame.c	2009-01-04 21:22:21.000000000 +0100
@@ -4291,6 +4291,18 @@
     game.info.allowed_city_names =
       secfile_lookup_int_default(file, game.info.allowed_city_names,
                                  "game.allowed_city_names"); 
+    game.info.migrationturn =
+      secfile_lookup_int_default(file, game.info.migrationturn,
+                                 "game.migrationturn");
+    game.info.migrationdist =
+      secfile_lookup_int_default(file, game.info.migrationdist,
+                                 "game.migrationdist");
+    game.info.migrationplayer =
+      secfile_lookup_int_default(file, game.info.migrationplayer,
+                                 "game.migrationplayer");
+    game.info.migrationworld =
+      secfile_lookup_int_default(file, game.info.migrationworld,
+                                 "game.migrationworld");
 
     if(civstyle == 1) {
       string = "civ1";
@@ -4943,6 +4955,10 @@
   secfile_insert_bool(file, game.info.happyborders, "game.happyborders");
   secfile_insert_int(file, game.info.diplomacy, "game.diplomacy");
   secfile_insert_int(file, game.info.allowed_city_names, "game.allowed_city_names");
+  secfile_insert_int(file, game.info.migrationturn, "game.migrationturn");
+  secfile_insert_int(file, game.info.migrationdist, "game.migrationdist");
+  secfile_insert_int(file, game.info.migrationplayer, "game.migrationplayer");
+  secfile_insert_int(file, game.info.migrationworld, "game.migrationworld");
 
   {
     /* Now always save these, so the server options reflect the
diff -ur -X./freeciv-2.1.99svn15393/diff_ignore freeciv-2.1.99svn15393/server/settings.c freeciv-2.1.99svn.patch_migration/server/settings.c
--- freeciv-2.1.99svn15393/server/settings.c	2008-12-25 19:45:49.000000000 +0100
+++ freeciv-2.1.99svn.patch_migration/server/settings.c	2009-01-04 21:22:21.000000000 +0100
@@ -841,6 +841,45 @@
               "on the surrounding terrain."),
            NULL, GAME_DEFAULT_NATURALCITYNAMES)
 
+  GEN_INT("migrationturn", game.info.migrationturn,
+          SSET_RULES_FLEXIBLE, SSET_SOCIOLOGY, SSET_RARE, SSET_TO_CLIENT,
+          N_("Turn count between citizen migrations"),
+          N_("This setting controls the number of turns between the "
+             "automatic transfer of citizens between overlapping cities. "
+             "Citizens will generally move to larger cities with happyer "
+             "people, improvements (especially wonders) and a surplus of "
+             "trade, luxury and science. The palace building also greatly "
+             "increases the destination desirability. If this setting is "
+             "zero, migration will be disabled."), NULL,
+          GAME_MIN_MIGRATION_TURN, GAME_MAX_MIGRATION_TURN,
+          GAME_DEFAULT_MIGRATION_TURN)
+
+  GEN_INT("migrationdist", game.info.migrationdist,
+          SSET_RULES_FLEXIBLE, SSET_SOCIOLOGY, SSET_RARE, SSET_TO_CLIENT,
+          N_("Distance for migration between cities"),
+          N_("If two cities overlap or are close to each over, citizens "
+             "can migrate between both cities. For example, when this "
+             "value is 3, there can be up to two empty fields between "
+             "two cities for citizens to migrate."), NULL,
+          GAME_MIN_MIGRATION_DIST, GAME_MAX_MIGRATION_DIST,
+          GAME_DEFAULT_MIGRATION_DIST)
+
+  GEN_INT("migrationplayer", game.info.migrationplayer,
+          SSET_RULES_FLEXIBLE, SSET_SOCIOLOGY, SSET_RARE, SSET_TO_CLIENT,
+          N_("Chance for migration within one nation"),
+          N_("Possibility of migration between overlapping cities"
+             "within one nation."),
+          NULL, GAME_MIN_MIGRATION_PLAYER, GAME_MAX_MIGRATION_PLAYER,
+          GAME_DEFAULT_MIGRATION_PLAYER)
+
+  GEN_INT("migrationworld", game.info.migrationworld,
+          SSET_RULES_FLEXIBLE, SSET_SOCIOLOGY, SSET_RARE, SSET_TO_CLIENT,
+          N_("Chance for migration between all cities"),
+          N_("Possibility of migration between overlapping cities"
+             "taking into account cities of all nations."),
+          NULL, GAME_MIN_MIGRATION_WORLD, GAME_MAX_MIGRATION_WORLD,
+          GAME_DEFAULT_MIGRATION_WORLD)
+
   /* Meta options: these don't affect the internal rules of the game, but
    * do affect players.  Also options which only produce extra server
    * "output" and don't affect the actual game.
diff -ur -X./freeciv-2.1.99svn15393/diff_ignore freeciv-2.1.99svn15393/server/srv_main.c freeciv-2.1.99svn.patch_migration/server/srv_main.c
--- freeciv-2.1.99svn15393/server/srv_main.c	2008-12-27 17:25:17.000000000 +0100
+++ freeciv-2.1.99svn.patch_migration/server/srv_main.c	2009-01-04 21:22:21.000000000 +0100
@@ -869,6 +869,13 @@
   summon_barbarians(); /* wild guess really, no idea where to put it, but
 			  I want to give them chance to move their units */
 
+  if (game.info.migrationturn > 0) {
+    freelog(LOG_DEBUG, "Season of migrations");
+    players_iterate(pplayer) {
+      check_city_migrations(pplayer);
+    } players_iterate_end;
+  }
+
   update_environmental_upset(S_POLLUTION, &game.info.heating,
 			     &game.info.globalwarming, &game.info.warminglevel,
 			     global_warming);
_______________________________________________
Freeciv-dev mailing list
Freeciv-dev@gna.org
https://mail.gna.org/listinfo/freeciv-dev

Reply via email to