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

Per I. Mathisen wrote:
> The reason for this is that specialists do not have attitude. I think
> this is a design mistake, but that is another question.
> 
In this case, it turned out to be a boon!

The discovered problem was packhand.c handle_city_short_info() setting
the city->size in the "final" citizen feeling entry, but not in the "base".
This was immediately wiped by the city.c generic_city_refresh(), as it
copied base to luxury ... to final.

That's why no server errors from the virtual city code -- it set both
specialist and "final", but the "final" entry was wiped out, so the
duplicate total in the specialist matched the city size.  Seems like
somebody was coding by trial and error.

The solution was to set the default specialist only.  The little people
display as specialists, but that's OK, as the observer doesn't have control
of the city anyway.

Anyway, changing specialists to have attitude in the future will be much
easier, as the code now has defined symbols and a bit less obscurity....

Reminder, this only fixes the 2.1.0 city display, not the other bugs
reported in the same ticket.  That was a tedious 15 or so hours of work,
I'll look at the other problems later.

Committed S2_1 revision 13911.
Committed S2_2 revision 13912.
Committed trunk revision 13913.

Final S2_2 changes for posterity (a bit different from S2_1, same as trunk):

Index: server/score.c
===================================================================
--- server/score.c      (revision 13911)
+++ server/score.c      (working copy)
@@ -259,10 +259,10 @@
   city_list_iterate(pplayer->cities, pcity) {
     int bonus;
 
-    pplayer->score.happy += pcity->ppl_happy[4];
-    pplayer->score.content += pcity->ppl_content[4];
-    pplayer->score.unhappy += pcity->ppl_unhappy[4];
-    pplayer->score.angry += pcity->ppl_angry[4];
+    pplayer->score.happy += pcity->feel[CITIZEN_HAPPY][FEELING_FINAL];
+    pplayer->score.content += pcity->feel[CITIZEN_CONTENT][FEELING_FINAL];
+    pplayer->score.unhappy += pcity->feel[CITIZEN_UNHAPPY][FEELING_FINAL];
+    pplayer->score.angry += pcity->feel[CITIZEN_ANGRY][FEELING_FINAL];
     specialist_type_iterate(sp) {
       pplayer->score.specialists[sp] += pcity->specialists[sp];
     } specialist_type_iterate_end;
Index: server/citytools.c
===================================================================
--- server/citytools.c  (revision 13911)
+++ server/citytools.c  (working copy)
@@ -1635,13 +1635,13 @@
   sz_strlcpy(packet->name, pcity->name);
 
   packet->size=pcity->size;
-  for (i=0;i<5;i++) {
-    packet->ppl_happy[i]=pcity->ppl_happy[i];
-    packet->ppl_content[i]=pcity->ppl_content[i];
-    packet->ppl_unhappy[i]=pcity->ppl_unhappy[i];
-    packet->ppl_angry[i]=pcity->ppl_angry[i];
+  for (i = 0; i < FEELING_LAST; i++) {
+    packet->ppl_happy[i] = pcity->feel[CITIZEN_HAPPY][i];
+    packet->ppl_content[i] = pcity->feel[CITIZEN_CONTENT][i];
+    packet->ppl_unhappy[i] = pcity->feel[CITIZEN_UNHAPPY][i];
+    packet->ppl_angry[i] = pcity->feel[CITIZEN_ANGRY][i];
   }
-  /* The number of data in specilists[] array */
+  /* The number of data in specialists[] array */
   packet->specialists_size = specialist_count();
   specialist_type_iterate(sp) {
     packet->specialists[sp] = pcity->specialists[sp];
Index: server/cityturn.c
===================================================================
--- server/cityturn.c   (revision 13911)
+++ server/cityturn.c   (working copy)
@@ -1491,9 +1491,9 @@
   }
 
   size = MAX(1, pcity->size
-                + pcity->ppl_happy[4]
-                - pcity->ppl_unhappy[4]
-                - pcity->ppl_angry[4] * 3);
+                + pcity->feel[CITIZEN_HAPPY][FEELING_FINAL]
+                - pcity->feel[CITIZEN_UNHAPPY][FEELING_FINAL]
+                - pcity->feel[CITIZEN_ANGRY][FEELING_FINAL] * 3);
   cost *= size;
   cost *= game.info.incite_total_factor;
   cost = cost / (dist + 3);
Index: server/savegame.c
===================================================================
--- server/savegame.c   (revision 13911)
+++ server/savegame.c   (working copy)
@@ -2185,12 +2185,13 @@
 
   for (i = 0; i < ncities; i++) { /* read the cities */
     struct city *pcity;
+    const char *kind;
+    const char *name;
+    int id, k;
+    int citizens = 0;
     int nat_x = secfile_lookup_int(file, "player%d.c%d.x", plrno, i);
     int nat_y = secfile_lookup_int(file, "player%d.c%d.y", plrno, i);
     struct tile *ptile = native_pos_to_tile(nat_x, nat_y);
-    const char* kind;
-    const char* name;
-    int id, k;
 
     pcity = create_city_virtual(plr, ptile,
                       secfile_lookup_str(file, "player%d.c%d.name", plrno, i));
@@ -2214,6 +2215,7 @@
     pcity->steal=secfile_lookup_int(file, "player%d.c%d.steal", plrno, i);
 
     specialist_type_iterate(sp) {
+      citizens +=
       pcity->specialists[sp]
        = secfile_lookup_int(file, "player%d.c%d.n%s", plrno, i,
                             specialist_rule_name(specialist_by_number(sp)));
@@ -2419,36 +2421,60 @@
     p=secfile_lookup_str(file, "player%d.c%d.workers", plrno, i);
     for(y=0; y<CITY_MAP_SIZE; y++) {
       for(x=0; x<CITY_MAP_SIZE; x++) {
-       pcity->city_map[x][y] =
-           is_valid_city_coords(x, y) ? C_TILE_EMPTY : C_TILE_UNAVAILABLE;
+        bool valid = is_valid_city_coords(x, y);
+       pcity->city_map[x][y] = valid ? C_TILE_EMPTY : C_TILE_UNAVAILABLE;
+
        if (*p == '0') {
-         set_worker_city(pcity, x, y,
-                         city_map_to_map(pcity, x, y) ?
-                         C_TILE_EMPTY : C_TILE_UNAVAILABLE);
+         if (!valid) {
+           /* oops, inconsistent savegame; minimal fix: */
+           freelog(LOG_VERBOSE, "Invalid workers '%c' for %s (%d,%d), "
+                   "ignoring", *p, pcity->name, x, y);
+         } else {
+           set_worker_city(pcity, x, y,
+                           NULL != city_map_to_map(pcity, x, y)
+                           ? C_TILE_EMPTY : C_TILE_UNAVAILABLE);
+         }
        } else if (*p=='1') {
          struct tile *ptile;
-
-         ptile = city_map_to_map(pcity, x, y);
-
-         if (ptile->worked) {
+         if (!valid) {
            /* oops, inconsistent savegame; minimal fix: */
+           freelog(LOG_VERBOSE, "Invalid workers '%c' for %s (%d,%d), "
+                   "ignoring", *p, pcity->name, x, y);
+         } else if (NULL == (ptile = city_map_to_map(pcity, x, y))
+                 || ptile->worked) {
+           /* oops, inconsistent savegame; minimal fix: */
            freelog(LOG_VERBOSE, "Inconsistent worked for %s (%d,%d), "
                    "converting to specialist", pcity->name, x, y);
            pcity->specialists[DEFAULT_SPECIALIST]++;
            set_worker_city(pcity, x, y, C_TILE_UNAVAILABLE);
+           citizens++;
          } else {
            set_worker_city(pcity, x, y, C_TILE_WORKER);
+           citizens++;
          }
-       } else {
-         assert(*p == '2');
-         if (is_valid_city_coords(x, y)) {
+       } else if (*p == '2') {
+         if (valid) {
            set_worker_city(pcity, x, y, C_TILE_UNAVAILABLE);
          }
          assert(pcity->city_map[x][y] == C_TILE_UNAVAILABLE);
+       } else {
+          freelog(LOG_VERBOSE, "Invalid workers '%c' for %s (%d,%d), "
+                  "ignoring", *p, pcity->name, x, y);
        }
         p++;
       }
     }
+    /* center tile worker isn't counted in city size */
+    if (citizens > 1) {
+      citizens--;
+    }
+    if (citizens != pcity->size) {
+      freelog(LOG_ERROR, "player_load()"
+              " %d citizens not equal %d city size in \"%s\".",
+              citizens,
+              pcity->size,
+              pcity->name);
+    }
 
     /* Initialise list of improvements with City- and Building-wide
        equiv_ranges */
@@ -3467,19 +3493,21 @@
     case C_TILE_EMPTY:
       if (!res) {
        set_worker_city(pcity, x, y, C_TILE_UNAVAILABLE);
-       freelog(LOG_DEBUG, "unavailable tile marked as empty!");
+       freelog(LOG_VERBOSE, "Unavailable tile marked as empty"
+               " for %s (%d,%d)",
+               pcity->name, x, y);
       }
       break;
     case C_TILE_WORKER:
       if (!res) {
-       struct tile *ptile;
+       struct tile *ptile= city_map_to_map(pcity, x, y);
 
        pcity->specialists[DEFAULT_SPECIALIST]++;
        set_worker_city(pcity, x, y, C_TILE_UNAVAILABLE);
-       freelog(LOG_DEBUG, "Worked tile was unavailable!");
+       freelog(LOG_VERBOSE, "Worked tile was unavailable"
+               " for %s (%d,%d)",
+               pcity->name, x, y);
 
-       ptile = city_map_to_map(pcity, x, y);
-
        map_city_radius_iterate(ptile, tile2) {
          struct city *pcity2 = tile_get_city(tile2);
          if (pcity2)
@@ -3490,7 +3518,9 @@
     case C_TILE_UNAVAILABLE:
       if (res) {
        set_worker_city(pcity, x, y, C_TILE_EMPTY);
-       freelog(LOG_DEBUG, "Empty tile Marked as unavailable!");
+       freelog(LOG_VERBOSE, "Empty tile marked as unavailable"
+               " for %s (%d,%d)",
+               pcity->name, x, y);
       }
       break;
     }
Index: common/city.c
===================================================================
--- common/city.c       (revision 13911)
+++ common/city.c       (working copy)
@@ -1084,10 +1084,10 @@
 **************************************************************************/
 bool city_happy(const struct city *pcity)
 {
-  return (pcity->ppl_happy[4] >= (pcity->size + 1) / 2
-         && pcity->ppl_unhappy[4] == 0
-          && pcity->ppl_angry[4] == 0
-          && pcity->size >= game.info.celebratesize);
+  return (pcity->size >= game.info.celebratesize
+         && pcity->feel[CITIZEN_ANGRY][FEELING_FINAL] == 0
+         && pcity->feel[CITIZEN_UNHAPPY][FEELING_FINAL] == 0
+         && pcity->feel[CITIZEN_HAPPY][FEELING_FINAL] >= (pcity->size + 1) / 
2);
 }
 
 /**************************************************************************
@@ -1096,8 +1096,9 @@
 **************************************************************************/
 bool city_unhappy(const struct city *pcity)
 {
-  return (pcity->ppl_happy[4] <
-         pcity->ppl_unhappy[4] + 2 * pcity->ppl_angry[4]);
+  return (pcity->feel[CITIZEN_HAPPY][FEELING_FINAL]
+       < pcity->feel[CITIZEN_UNHAPPY][FEELING_FINAL]
+         + 2 * pcity->feel[CITIZEN_ANGRY][FEELING_FINAL]);
 }
 
 /**************************************************************************
@@ -1715,14 +1716,14 @@
 }
 
 /**************************************************************************
-  Copy the happyness array in the city from index i to index i+1.
+  Copy the happyness array in the city to index i from index i-1.
 **************************************************************************/
-static void happy_copy(struct city *pcity, int i)
+static void happy_copy(struct city *pcity, enum citizen_feeling i)
 {
-  pcity->ppl_angry[i + 1] = pcity->ppl_angry[i];
-  pcity->ppl_unhappy[i + 1] = pcity->ppl_unhappy[i];
-  pcity->ppl_content[i + 1] = pcity->ppl_content[i];
-  pcity->ppl_happy[i + 1] = pcity->ppl_happy[i];
+  int c = 0;
+  for (; c < CITIZEN_LAST; c++) {
+    pcity->feel[c][i] = pcity->feel[c][i - 1];
+  }
 }
 
 /**************************************************************************
@@ -1745,10 +1746,15 @@
 /**************************************************************************
   Create content, unhappy and angry citizens.
 **************************************************************************/
-static void citizen_base_mood(struct player *pplayer, int specialists,
-                             int *happy, int *content,
-                             int *unhappy, int *angry, int size)
+static void citizen_base_mood(struct player *pplayer, struct city *pcity,
+                             int specialists)
 {
+  int *happy = &pcity->feel[CITIZEN_HAPPY][FEELING_BASE];
+  int *content = &pcity->feel[CITIZEN_CONTENT][FEELING_BASE];
+  int *unhappy = &pcity->feel[CITIZEN_UNHAPPY][FEELING_BASE];
+  int *angry = &pcity->feel[CITIZEN_ANGRY][FEELING_BASE];
+  int size = pcity->size;
+
   /* This is the number of citizens that may start out content, depending
    * on empire size and game's city unhappysize. This may be bigger than
    * the size of the city, since this is a potential. */
@@ -1780,10 +1786,13 @@
    * then content are made happy, then unhappy content, etc.
    * each conversions costs 2 or 4 luxuries.
 **************************************************************************/
-static inline void citizen_luxury_happy(const struct city *pcity, int 
*luxuries,
-                                        int *angry, int *unhappy, int *happy, 
-                                        int *content)
+static inline void citizen_luxury_happy(struct city *pcity, int *luxuries)
 {
+  int *happy = &pcity->feel[CITIZEN_HAPPY][FEELING_LUXURY];
+  int *content = &pcity->feel[CITIZEN_CONTENT][FEELING_LUXURY];
+  int *unhappy = &pcity->feel[CITIZEN_UNHAPPY][FEELING_LUXURY];
+  int *angry = &pcity->feel[CITIZEN_ANGRY][FEELING_LUXURY];
+
   while (*luxuries >= game.info.happy_cost && *angry > 0) {
     /* Upgrade angry to unhappy: costs HAPPY_COST each. */
     (*angry)--;
@@ -1818,18 +1827,17 @@
 {
   int x = pcity->prod[O_LUXURY];
 
-  happy_copy(pcity, 0);
-
-  citizen_luxury_happy(pcity, &x, &pcity->ppl_angry[1], 
&pcity->ppl_unhappy[1], 
-                       &pcity->ppl_happy[1], &pcity->ppl_content[1]);
+  citizen_luxury_happy(pcity, &x);
 }
 
 /**************************************************************************
   Make citizens content due to city improvements.
 **************************************************************************/
-static inline void citizen_content_buildings(struct city *pcity, int *content,
-                                             int *unhappy, int *angry)
+static inline void citizen_content_buildings(struct city *pcity)
 {
+  int *content = &pcity->feel[CITIZEN_CONTENT][FEELING_EFFECT];
+  int *unhappy = &pcity->feel[CITIZEN_UNHAPPY][FEELING_EFFECT];
+  int *angry = &pcity->feel[CITIZEN_ANGRY][FEELING_EFFECT];
   int faces = get_city_bonus(pcity, EFT_MAKE_CONTENT);
 
   /* make people content (but not happy):
@@ -1852,14 +1860,16 @@
   This function requires that pcity->martial_law and
   pcity->unit_happy_cost have already been set in city_support().
 **************************************************************************/
-static inline void citizen_happy_units(struct city *pcity, int *happy,
-                                       int *content, int *unhappy,
-                                       int *angry)
+static inline void citizen_happy_units(struct city *pcity)
 {
+  int *happy = &pcity->feel[CITIZEN_HAPPY][FEELING_MARTIAL];
+  int *content = &pcity->feel[CITIZEN_CONTENT][FEELING_MARTIAL];
+  int *unhappy = &pcity->feel[CITIZEN_UNHAPPY][FEELING_MARTIAL];
+  int *angry = &pcity->feel[CITIZEN_ANGRY][FEELING_MARTIAL];
   int amt = pcity->martial_law;
 
   /* Pacify discontent citizens through martial law.  First convert
-   * angry->unhappy and then unhappy->content. */
+   * angry => unhappy, then unhappy => content. */
   while (amt > 0 && *angry > 0) {
     (*angry)--;
     (*unhappy)++;
@@ -1872,8 +1882,8 @@
   }
 
   /* Now make citizens unhappier because of military units away from home.
-   * First make content people unhappy, then happy people unhappy,
-   * then happy people content. */
+   * First make content => unhappy, then happy => unhappy,
+   * then happy => content. */
   amt = pcity->unit_happy_upkeep;
   while (amt > 0 && *content > 0) {
     (*content)--;
@@ -1898,13 +1908,15 @@
 /**************************************************************************
   Make citizens happy due to wonders.
 **************************************************************************/
-static inline void citizen_happy_wonders(struct city *pcity, int *happy,
-                                         int *content, int *unhappy, 
-                                         int *angry)
+static inline void citizen_happy_wonders(struct city *pcity)
 {
+  int *happy = &pcity->feel[CITIZEN_HAPPY][FEELING_FINAL];
+  int *content = &pcity->feel[CITIZEN_CONTENT][FEELING_FINAL];
+  int *unhappy = &pcity->feel[CITIZEN_UNHAPPY][FEELING_FINAL];
+  int *angry = &pcity->feel[CITIZEN_ANGRY][FEELING_FINAL];
   int bonus = get_city_bonus(pcity, EFT_MAKE_HAPPY);
 
-  /* First create happy citizens from content then from unhappy
+  /* First create happy citizens from content, then from unhappy
    * citizens; we cannot help angry citizens here. */
   while (bonus > 0 && *content > 0) {
     (*content)--;
@@ -2206,24 +2218,23 @@
   add_specialist_output(pcity, pcity->citizen_base);
 
   set_city_production(pcity);
-  citizen_base_mood(pplayer, city_specialists(pcity), &pcity->ppl_happy[0], 
-                    &pcity->ppl_content[0], &pcity->ppl_unhappy[0], 
-                    &pcity->ppl_angry[0], pcity->size);
+  citizen_base_mood(pplayer, pcity, city_specialists(pcity));
   pcity->pollution = city_pollution(pcity, pcity->prod[O_SHIELD]);
+
+  happy_copy(pcity, FEELING_LUXURY);
   citizen_happy_luxury(pcity); /* with our new found luxuries */
-  happy_copy(pcity, 1);
-  citizen_content_buildings(pcity, &pcity->ppl_content[2], 
-                            &pcity->ppl_unhappy[2], &pcity->ppl_angry[2]);
-  happy_copy(pcity, 2);
+
+  happy_copy(pcity, FEELING_EFFECT);
+  citizen_content_buildings(pcity);
+
   /* Martial law & unrest from units */
-  citizen_happy_units(pcity, &pcity->ppl_happy[3],
-                      &pcity->ppl_content[3], &pcity->ppl_unhappy[3], 
-                      &pcity->ppl_angry[3]);
-  happy_copy(pcity, 3);
+  happy_copy(pcity, FEELING_MARTIAL);
+  citizen_happy_units(pcity);
+
   /* Building (including wonder) happiness effects */
-  citizen_happy_wonders(pcity, &pcity->ppl_happy[4],
-                      &pcity->ppl_content[4], &pcity->ppl_unhappy[4],
-                      &pcity->ppl_angry[4]);
+  happy_copy(pcity, FEELING_FINAL);
+  citizen_happy_wonders(pcity);
+
   unhappy_city_check(pcity);
   set_surpluses(pcity);
 }
@@ -2408,25 +2419,19 @@
                                 struct tile *ptile, const char *name)
 {
   int i;
-  struct city *pcity;
+  struct city *pcity = fc_calloc(1, sizeof(*pcity));
 
-  pcity = fc_calloc(1, sizeof(*pcity));
-
   pcity->id = 0;
   assert(pplayer != NULL); /* No unowned cities! */
+  pcity->original = pplayer;
   pcity->owner = pplayer;
   pcity->tile = ptile;
   sz_strlcpy(pcity->name, name);
-  pcity->size = 1;
+#if 0
+  /* some zero variables not compiled, but left for searching */
+  memset(pcity->feel, 0, sizeof(pcity->feel));
+  memset(pcity->specialists, 0, sizeof(pcity->specialists));
   memset(pcity->tile_output, 0, sizeof(pcity->tile_output));
-  specialist_type_iterate(sp) {
-    pcity->specialists[sp] = 0;
-  } specialist_type_iterate_end;
-  pcity->specialists[DEFAULT_SPECIALIST] = 1;
-  pcity->ppl_happy[4] = 0;
-  pcity->ppl_content[4] = 1;
-  pcity->ppl_unhappy[4] = 0;
-  pcity->ppl_angry[4] = 0;
   pcity->was_happy = FALSE;
   pcity->steal = 0;
   for (i = 0; i < NUM_TRADEROUTES; i++) {
@@ -2434,7 +2439,9 @@
   }
   pcity->food_stock = 0;
   pcity->shield_stock = 0;
-  pcity->original = pplayer;
+#endif
+  pcity->specialists[DEFAULT_SPECIALIST] =
+  pcity->size = 1;
 
   /* Initialise improvements list */
   for (i = 0; i < ARRAY_SIZE(pcity->built); i++) {
@@ -2507,6 +2514,8 @@
   pcity->ai.founder_want = 0; /* calculating this is really expensive */
   pcity->ai.next_founder_want_recalc = 0; /* turns to recalc found_want */
   pcity->ai.trade_want = 1; /* we always want some */
+#if 0
+  /* some zero variables not compiled, but left for searching */
   memset(pcity->ai.building_want, 0, sizeof(pcity->ai.building_want));
   pcity->ai.danger = 0;
   pcity->ai.urgency = 0;
@@ -2525,6 +2534,7 @@
         O_COUNT * sizeof(*pcity->unhappy_penalty));
   memset(pcity->prod, 0, O_COUNT * sizeof(*pcity->prod));
   memset(pcity->citizen_base, 0, O_COUNT * sizeof(*pcity->citizen_base));
+#endif
   output_type_iterate(o) {
     pcity->bonus[o] = 100;
   } output_type_iterate_end;
Index: common/city.h
===================================================================
--- common/city.h       (revision 13911)
+++ common/city.h       (working copy)
@@ -229,6 +229,26 @@
   int next_recalc;
 };
 
+enum citizen_category {
+  CITIZEN_HAPPY,
+  CITIZEN_CONTENT,
+  CITIZEN_UNHAPPY,
+  CITIZEN_ANGRY,
+  CITIZEN_LAST,
+  CITIZEN_SPECIALIST = CITIZEN_LAST,
+};
+
+/* changing this order will break network compatibility,
+ * and clients that don't use the symbols. */
+enum citizen_feeling {
+  FEELING_BASE,                /* before any of the modifiers below */
+  FEELING_LUXURY,      /* after luxury */
+  FEELING_EFFECT,      /* after building effects */
+  FEELING_MARTIAL,     /* after units enforce martial order */
+  FEELING_FINAL,       /* after wonders (final result) */
+  FEELING_LAST
+};
+
 struct city {
   int id;
   struct player *owner; /* Cannot be NULL. */
@@ -238,23 +258,17 @@
   /* the people */
   int size;
 
-  /* Tile output, regardless of if the tile is actually worked. */
-  unsigned char tile_output[CITY_MAP_SIZE][CITY_MAP_SIZE][O_MAX];
+  int feel[CITIZEN_LAST][FEELING_LAST];
 
-  /* How the citizens feel:
-     ppl_*[0] is distribution before any of the modifiers below.
-     ppl_*[1] is distribution after luxury.
-     ppl_*[2] is distribution after after building effects.
-     ppl_*[3] is distribution after units enfored martial order.
-     ppl_*[4] is distribution after wonders. (final result.) */
-  int ppl_happy[5], ppl_content[5], ppl_unhappy[5], ppl_angry[5];
-
   /* Specialists */
   int specialists[SP_MAX];
 
   /* trade routes */
   int trade[NUM_TRADEROUTES], trade_value[NUM_TRADEROUTES];
 
+  /* Tile output, regardless of if the tile is actually worked. */
+  unsigned char tile_output[CITY_MAP_SIZE][CITY_MAP_SIZE][O_MAX];
+
   /* the productions */
   int surplus[O_MAX]; /* Final surplus in each category. */
   int waste[O_MAX]; /* Waste/corruption in each category. */
Index: ai/aicity.c
===================================================================
--- ai/aicity.c (revision 13911)
+++ ai/aicity.c (working copy)
@@ -131,9 +131,9 @@
            + pcity->prod[O_LUXURY] * ai->luxury_priority
            + pcity->prod[O_GOLD] * ai->gold_priority
            + pcity->prod[O_SCIENCE] * ai->science_priority
-           + pcity->ppl_happy[4] * ai->happy_priority
-           - pcity->ppl_unhappy[4] * ai->unhappy_priority
-           - pcity->ppl_angry[4] * ai->angry_priority
+           + pcity->feel[CITIZEN_HAPPY][FEELING_FINAL] * ai->happy_priority
+           - pcity->feel[CITIZEN_UNHAPPY][FEELING_FINAL] * ai->unhappy_priority
+           - pcity->feel[CITIZEN_ANGRY][FEELING_FINAL] * ai->angry_priority
            - pcity->pollution * ai->pollution_priority);
 
   if (pcity->surplus[O_FOOD] < 0 || pcity->surplus[O_SHIELD] < 0) {
@@ -302,11 +302,11 @@
 
   if (get_city_bonus(pcity, EFT_NO_UNHAPPY) <= 0) {
     int i;
-    int max_converted = pcity->ppl_unhappy[4];
+    int max_converted = pcity->feel[CITIZEN_UNHAPPY][FEELING_FINAL];
 
     /* See if some step of happiness calculation gets capped */
-    for (i = happiness_step; i < 4; i++) {
-      max_converted = MIN(max_converted, pcity->ppl_unhappy[i]);
+    for (i = happiness_step; i < FEELING_FINAL; i++) {
+      max_converted = MIN(max_converted, pcity->feel[CITIZEN_UNHAPPY][i]);
     }
 
     v = MIN(amount, max_converted + get_entertainers(pcity)) * 35;
@@ -383,7 +383,7 @@
     v += c * amount / 100;
     break;
   case EFT_MAKE_HAPPY:
-    v += (get_entertainers(pcity) + pcity->ppl_unhappy[4]) * 5 * amount;
+    v += (get_entertainers(pcity) + 
pcity->feel[CITIZEN_UNHAPPY][FEELING_FINAL]) * 5 * amount;
     if (city_list_size(pplayer->cities)
        > get_player_bonus(pplayer, EFT_EMPIRE_SIZE_BASE)) {
       v += c * amount; /* offset large empire size */
@@ -394,24 +394,24 @@
     /* TODO */
     break;
   case EFT_NO_UNHAPPY:
-    v += (get_entertainers(pcity) + pcity->ppl_unhappy[4]) * 30;
+    v += (get_entertainers(pcity) + 
pcity->feel[CITIZEN_UNHAPPY][FEELING_FINAL]) * 30;
     break;
   case EFT_FORCE_CONTENT:
-    v += content_effect_value(pplayer, pcity, amount, c, 4);
+    v += content_effect_value(pplayer, pcity, amount, c, FEELING_FINAL);
     break;
   case EFT_MAKE_CONTENT:
-    v += content_effect_value(pplayer, pcity, amount, c, 2);
+    v += content_effect_value(pplayer, pcity, amount, c, FEELING_EFFECT);
     break;
   case EFT_MAKE_CONTENT_MIL_PER:
     if (get_city_bonus(pcity, EFT_NO_UNHAPPY) <= 0) {
-      v += MIN(pcity->ppl_unhappy[4] + get_entertainers(pcity),
+      v += MIN(pcity->feel[CITIZEN_UNHAPPY][FEELING_FINAL] + 
get_entertainers(pcity),
               amount) * 25;
       v += MIN(amount, 5) * c;
     }
     break;
   case EFT_MAKE_CONTENT_MIL:
     if (get_city_bonus(pcity, EFT_NO_UNHAPPY) <= 0) {
-      v += pcity->ppl_unhappy[4] * amount
+      v += pcity->feel[CITIZEN_UNHAPPY][FEELING_FINAL] * amount
         * MAX(unit_list_size(pcity->units_supported), 0) * 2;
       v += c * MAX(amount + 2, 1);
     }
@@ -1807,8 +1807,10 @@
   freelog(LOG_EMERGENCY,
           "Emergency in %s (%s, angry%d, unhap%d food%d, prod%d)",
           pcity->name, city_unhappy(pcity) ? "unhappy" : "content",
-          pcity->ppl_angry[4], pcity->ppl_unhappy[4],
-          pcity->surplus[O_FOOD], pcity->surplus[O_SHIELD]);
+          pcity->feel[CITIZEN_ANGRY][FEELING_FINAL],
+          pcity->feel[CITIZEN_UNHAPPY][FEELING_FINAL],
+          pcity->surplus[O_FOOD],
+          pcity->surplus[O_SHIELD]);
 
   minilist = city_list_new();
   map_city_radius_iterate(pcity->tile, ptile) {
Index: ai/advmilitary.c
===================================================================
--- ai/advmilitary.c    (revision 13911)
+++ ai/advmilitary.c    (working copy)
@@ -1426,7 +1426,7 @@
   } /* ok, don't need to defend */
 
   if (pcity->surplus[O_SHIELD] <= 0 
-      || pcity->ppl_unhappy[4] > pcity->ppl_unhappy[2]
+      || pcity->feel[CITIZEN_UNHAPPY][FEELING_FINAL] > 
pcity->feel[CITIZEN_UNHAPPY][FEELING_EFFECT]
       || pcity->id == ai->wonder_city) {
     /* Things we consider below are not life-saving so we don't want to 
      * build them if our populace doesn't feel like it */
Index: client/citydlg_common.c
===================================================================
--- client/citydlg_common.c     (revision 13911)
+++ client/citydlg_common.c     (working copy)
@@ -512,33 +512,39 @@
   happiness).  "citizens" should be an array large enough to hold all
   citizens (use MAX_CITY_SIZE to be on the safe side).
 **************************************************************************/
-void get_city_citizen_types(struct city *pcity, int index,
-                           struct citizen_type *citizens)
+int get_city_citizen_types(struct city *pcity, enum citizen_feeling index,
+                          enum citizen_category *citizens)
 {
   int i = 0, n;
-  assert(index >= 0 && index < 5);
+  assert(index >= 0 && index < FEELING_LAST);
 
-  for (n = 0; n < pcity->ppl_happy[index]; n++, i++) {
-    citizens[i].type = CITIZEN_HAPPY;
+  for (n = 0; n < pcity->feel[CITIZEN_HAPPY][index]; n++, i++) {
+    citizens[i] = CITIZEN_HAPPY;
   }
-  for (n = 0; n < pcity->ppl_content[index]; n++, i++) {
-    citizens[i].type = CITIZEN_CONTENT;
+  for (n = 0; n < pcity->feel[CITIZEN_CONTENT][index]; n++, i++) {
+    citizens[i] = CITIZEN_CONTENT;
   }
-  for (n = 0; n < pcity->ppl_unhappy[index]; n++, i++) {
-    citizens[i].type = CITIZEN_UNHAPPY;
+  for (n = 0; n < pcity->feel[CITIZEN_UNHAPPY][index]; n++, i++) {
+    citizens[i] = CITIZEN_UNHAPPY;
   }
-  for (n = 0; n < pcity->ppl_angry[index]; n++, i++) {
-    citizens[i].type = CITIZEN_ANGRY;
+  for (n = 0; n < pcity->feel[CITIZEN_ANGRY][index]; n++, i++) {
+    citizens[i] = CITIZEN_ANGRY;
   }
 
   specialist_type_iterate(sp) {
     for (n = 0; n < pcity->specialists[sp]; n++, i++) {
-      citizens[i].type = CITIZEN_SPECIALIST;
-      citizens[i].spec_type = sp;
+      citizens[i] = CITIZEN_SPECIALIST + sp;
     }
   } specialist_type_iterate_end;
 
-  assert(i == pcity->size);
+  if (pcity->size != i) {
+    freelog(LOG_ERROR, "get_city_citizen_types()"
+            " %d citizens not equal %d city size in \"%s\".",
+            i,
+            pcity->size,
+            pcity->name);
+  }
+  return i;
 }
 
 /**************************************************************************
@@ -546,20 +552,16 @@
 **************************************************************************/
 void city_rotate_specialist(struct city *pcity, int citizen_index)
 {
-  struct citizen_type citizens[MAX_CITY_SIZE];
+  enum citizen_category citizens[MAX_CITY_SIZE];
   Specialist_type_id from, to;
+  int num_citizens = get_city_citizen_types(pcity, FEELING_FINAL, citizens);
 
-  if (citizen_index < 0 || citizen_index >= pcity->size) {
+  if (citizen_index < 0 || citizen_index >= num_citizens
+   || citizens[citizen_index] < CITIZEN_SPECIALIST) {
     return;
   }
+  from = citizens[citizen_index] - CITIZEN_SPECIALIST;
 
-  get_city_citizen_types(pcity, 4, citizens);
-
-  if (citizens[citizen_index].type != CITIZEN_SPECIALIST) {
-    return;
-  }
-  from = citizens[citizen_index].spec_type;
-
   /* Loop through all specialists in order until we find a usable one
    * (or run out of choices). */
   to = from;
Index: client/cityrepdata.c
===================================================================
--- client/cityrepdata.c        (revision 13911)
+++ client/cityrepdata.c        (working copy)
@@ -80,9 +80,11 @@
                                    const void *data)
 {
   static char buf[32];
-  my_snprintf(buf, sizeof(buf), "%d/%d/%d/%d", pcity->ppl_happy[4],
-             pcity->ppl_content[4], pcity->ppl_unhappy[4],
-             pcity->ppl_angry[4]);
+  my_snprintf(buf, sizeof(buf), "%d/%d/%d/%d",
+             pcity->feel[CITIZEN_HAPPY][FEELING_FINAL],
+             pcity->feel[CITIZEN_CONTENT][FEELING_FINAL],
+             pcity->feel[CITIZEN_UNHAPPY][FEELING_FINAL],
+             pcity->feel[CITIZEN_ANGRY][FEELING_FINAL]);
   return buf;
 }
 
@@ -90,7 +92,7 @@
                                  const void *data)
 {
   static char buf[8];
-  my_snprintf(buf, sizeof(buf), "%2d", pcity->ppl_happy[4]);
+  my_snprintf(buf, sizeof(buf), "%2d", 
pcity->feel[CITIZEN_HAPPY][FEELING_FINAL]);
   return buf;
 }
 
@@ -98,7 +100,7 @@
                                    const void *data)
 {
   static char buf[8];
-  my_snprintf(buf, sizeof(buf), "%2d", pcity->ppl_content[4]);
+  my_snprintf(buf, sizeof(buf), "%2d", 
pcity->feel[CITIZEN_CONTENT][FEELING_FINAL]);
   return buf;
 }
 
@@ -106,7 +108,7 @@
                                    const void *data)
 {
   static char buf[8];
-  my_snprintf(buf, sizeof(buf), "%2d", pcity->ppl_unhappy[4]);
+  my_snprintf(buf, sizeof(buf), "%2d", 
pcity->feel[CITIZEN_UNHAPPY][FEELING_FINAL]);
   return buf;
 }
 
@@ -114,7 +116,7 @@
                                  const void *data)
 {
   static char buf[8];
-  my_snprintf(buf, sizeof(buf), "%2d", pcity->ppl_angry[4]);
+  my_snprintf(buf, sizeof(buf), "%2d", 
pcity->feel[CITIZEN_ANGRY][FEELING_FINAL]);
   return buf;
 }
 
Index: client/citydlg_common.h
===================================================================
--- client/citydlg_common.h     (revision 13911)
+++ client/citydlg_common.h     (working copy)
@@ -19,23 +19,11 @@
 #include "shared.h"            /* bool type */
 #include "fc_types.h"
 
+#include "city.h"
+
 struct canvas;
 struct worklist;
 
-enum citizen_category {
-  CITIZEN_HAPPY,
-  CITIZEN_CONTENT,
-  CITIZEN_UNHAPPY,
-  CITIZEN_ANGRY,
-  CITIZEN_SPECIALIST,          /* assumed to be final category */
-  CITIZEN_LAST
-};
-
-struct citizen_type {
-  enum citizen_category type;
-  Specialist_type_id spec_type;
-};
-
 int get_citydlg_canvas_width(void);
 int get_citydlg_canvas_height(void);
 void generate_citydlg_dimensions(void);
@@ -62,8 +50,8 @@
 void get_city_dialog_pollution_text(const struct city *pcity,
                                    char *buffer, size_t bufsz);
 
-void get_city_citizen_types(struct city *pcity, int index,
-                           struct citizen_type *citizens);
+int get_city_citizen_types(struct city *pcity, enum citizen_feeling index,
+                          enum citizen_category *citizens);
 void city_rotate_specialist(struct city *pcity, int citizen_index);
 
 void activate_all_units(struct tile *ptile);
Index: client/gui-gtk-2.0/citydlg.c
===================================================================
--- client/gui-gtk-2.0/citydlg.c        (revision 13911)
+++ client/gui-gtk-2.0/citydlg.c        (working copy)
@@ -1341,19 +1341,20 @@
 *****************************************************************/
 static void city_dialog_update_citizens(struct city_dialog *pdialog)
 {
+  enum citizen_category citizens[MAX_CITY_SIZE];
   int i, j, width;
   struct city *pcity = pdialog->pcity;
-  struct citizen_type citizens[MAX_CITY_SIZE];
+  int num_citizens = get_city_citizen_types(pcity, FEELING_FINAL, citizens);
 
   /* If there is not enough space we stack the icons. We draw from left to */
   /* right. width is how far we go to the right for each drawn pixmap. The */
   /* last icon is always drawn in full, and so has reserved                */
-  /* tileset_small_sprite_width(tileset) pixels.                               
               */
+  /* tileset_small_sprite_width(tileset) pixels.                           */
 
-  if (pcity->size > 1) {
+  if (num_citizens > 1) {
     width = MIN(tileset_small_sprite_width(tileset),
                ((NUM_CITIZENS_SHOWN - 1) * 
tileset_small_sprite_width(tileset)) /
-               (pcity->size - 1));
+               (num_citizens - 1));
   } else {
     width = tileset_small_sprite_width(tileset);
   }
@@ -1362,8 +1363,6 @@
   gtk_pixcomm_freeze(GTK_PIXCOMM(pdialog->citizen_pixmap));
   gtk_pixcomm_clear(GTK_PIXCOMM(pdialog->citizen_pixmap));
 
-  get_city_citizen_types(pcity, 4, citizens);
-
   i = 0;
   if (can_conn_edit(&aconnection)) {
     gtk_pixcomm_copyto(GTK_PIXCOMM(pdialog->citizen_pixmap),
@@ -1373,7 +1372,7 @@
                        get_arrow_sprite(tileset, ARROW_MINUS),
                       i++ * width, 0);
   }
-  for (j = 0; j < pcity->size; i++, j++) {
+  for (j = 0; j < num_citizens; i++, j++) {
     gtk_pixcomm_copyto(GTK_PIXCOMM(pdialog->citizen_pixmap),
                       get_citizen_sprite(tileset, citizens[j], j, pcity),
                       i * width, 0);
Index: client/gui-gtk-2.0/happiness.c
===================================================================
--- client/gui-gtk-2.0/happiness.c      (revision 13911)
+++ client/gui-gtk-2.0/happiness.c      (working copy)
@@ -156,15 +156,14 @@
 /**************************************************************************
 ...
 **************************************************************************/
-static void refresh_pixcomm(GtkPixcomm *dst, struct city *pcity, int index)
+static void refresh_pixcomm(GtkPixcomm *dst, struct city *pcity,
+                           enum citizen_feeling index)
 {
+  enum citizen_category citizens[MAX_CITY_SIZE];
   int i;
-  struct citizen_type citizens[MAX_CITY_SIZE];
-  int num_citizens = pcity->size;
+  int num_citizens = get_city_citizen_types(pcity, index, citizens);
   int offset = MIN(tileset_small_sprite_width(tileset), PIXCOMM_WIDTH / 
num_citizens);
 
-  get_city_citizen_types(pcity, index, citizens);
-
   gtk_pixcomm_freeze(dst);
   gtk_pixcomm_clear(dst);
 
@@ -182,10 +181,9 @@
 void refresh_happiness_dialog(struct city *pcity)
 {
   int i;
-
   struct happiness_dialog *pdialog = get_happiness_dialog(pcity);
 
-  for (i = 0; i < 5; i++) {
+  for (i = 0; i < FEELING_LAST; i++) {
     refresh_pixcomm(GTK_PIXCOMM(pdialog->hpixmaps[i]), pdialog->pcity, i);
   }
 
Index: client/gui-xaw/citydlg.c
===================================================================
--- client/gui-xaw/citydlg.c    (revision 13911)
+++ client/gui-xaw/citydlg.c    (working copy)
@@ -495,10 +495,7 @@
   Dimension widthNext, borderNext, internalNext, spaceNext;
   Dimension widthPrev, borderPrev, internalPrev, spacePrev;
   Widget relative;
-  struct citizen_type c = {
-    .type = CITIZEN_SPECIALIST,
-    .spec_type = DEFAULT_SPECIALIST
-  };
+  enum citizen_category c = CITIZEN_SPECIALIST + DEFAULT_SPECIALIST;
 
   if (tileset_tile_height(tileset)<45) dummy_improvement_list[5]=0;
 
@@ -1535,13 +1532,12 @@
 *****************************************************************/
 void city_dialog_update_citizens(struct city_dialog *pdialog)
 {
+  enum citizen_category citizens[MAX_CITY_SIZE];
   int i;
   struct city *pcity=pdialog->pcity;
-  struct citizen_type citizens[MAX_CITY_SIZE];
+  int num_citizens = get_city_citizen_types(pcity, FEELING_FINAL, citizens);
 
-  get_city_citizen_types(pcity, 4, citizens);
-
-  for (i = 0; i < pcity->size && i < pdialog->num_citizens_shown; i++) {
+  for (i = 0; i < num_citizens && i < pdialog->num_citizens_shown; i++) {
     xaw_set_bitmap(pdialog->citizen_labels[i],
                   get_citizen_pixmap(citizens[i], i, pcity));
 
@@ -1552,7 +1548,7 @@
     XtSetSensitive(pdialog->citizen_labels[i], TRUE);
   }
 
-  if (i >= pdialog->num_citizens_shown && i < pcity->size) {
+  if (i >= pdialog->num_citizens_shown && i < num_citizens) {
     i = pdialog->num_citizens_shown - 1;
     /* FIXME: what about the mask? */
     xaw_set_bitmap(pdialog->citizen_labels[i],
Index: client/gui-xaw/mapview.c
===================================================================
--- client/gui-xaw/mapview.c    (revision 13911)
+++ client/gui-xaw/mapview.c    (working copy)
@@ -258,7 +258,7 @@
 /**************************************************************************
 ...
 **************************************************************************/
-Pixmap get_citizen_pixmap(struct citizen_type type, int cnum,
+Pixmap get_citizen_pixmap(enum citizen_category type, int cnum,
                          struct city *pcity)
 {
   return get_citizen_sprite(tileset, type, cnum, pcity)->pixmap;
Index: client/gui-xaw/mapview.h
===================================================================
--- client/gui-xaw/mapview.h    (revision 13911)
+++ client/gui-xaw/mapview.h    (working copy)
@@ -24,7 +24,7 @@
 #include "graphics.h"
 
 Pixmap get_thumb_pixmap(int onoff);
-Pixmap get_citizen_pixmap(struct citizen_type type, int cnum,
+Pixmap get_citizen_pixmap(enum citizen_category type, int cnum,
                          struct city *pcity);
 
 void put_unit_pixmap_city_overlays(struct unit *punit, Pixmap pm,
Index: client/gui-win32/citydlg.c
===================================================================
--- client/gui-win32/citydlg.c  (revision 13911)
+++ client/gui-win32/citydlg.c  (working copy)
@@ -515,19 +515,15 @@
 
 void city_dialog_update_citizens(HDC hdc,struct city_dialog *pdialog)
 {
-  HDC hdcsrc;
+  enum citizen_category citizens[MAX_CITY_SIZE];
   int i;
-  struct city *pcity=pdialog->pcity;
   RECT rc;
-  HBITMAP oldbit;
-  struct citizen_type citizens[MAX_CITY_SIZE];
+  struct city *pcity=pdialog->pcity;
+  int num_citizens = get_city_citizen_types(pcity, FEELING_FINAL, citizens);
+  HDC hdcsrc = CreateCompatibleDC(NULL);
+  HBITMAP oldbit = SelectObject(hdcsrc,pdialog->citizen_bmp);
 
-  hdcsrc = CreateCompatibleDC(NULL);
-  oldbit=SelectObject(hdcsrc,pdialog->citizen_bmp);
-
-  get_city_citizen_types(pcity, 4, citizens);
-
-  for (i = 0; i < pcity->size && i < NUM_CITIZENS_SHOWN; i++) {
+  for (i = 0; i < num_citizens && i < NUM_CITIZENS_SHOWN; i++) {
       draw_sprite(get_citizen_sprite(tileset, citizens[i], i, pcity), hdcsrc,
                  tileset_small_sprite_width(tileset) * i, 0);
   }
Index: client/gui-win32/happiness.c
===================================================================
--- client/gui-win32/happiness.c        (revision 13911)
+++ client/gui-win32/happiness.c        (working copy)
@@ -35,7 +35,7 @@
 
 #define HAPPINESS_PIX_WIDTH 23
 
-#define NUM_HAPPINESS_MODIFIERS 5
+#define NUM_HAPPINESS_MODIFIERS FEELING_LAST
 enum { CITIES, LUXURIES, BUILDINGS, UNITS, WONDERS };
 
 struct happiness_dlg {
@@ -57,7 +57,8 @@
 static void happiness_dialog_update_wonders(struct happiness_dlg
                                             *pdialog);
 static void refresh_happiness_bitmap(HBITMAP bmp,
-                                    struct city *pcity, int index);
+                                    struct city *pcity,
+                                    enum citizen_feeling index);
 
 /**************************************************************************
 ...
@@ -286,27 +287,25 @@
 ...
 **************************************************************************/
 static void refresh_happiness_bitmap(HBITMAP bmp,
-                                    struct city *pcity, int index)
+                                    struct city *pcity,
+                                    enum citizen_feeling index)
 {
-  HDC hdc;
-  HBITMAP old;
+  enum citizen_category citizens[MAX_CITY_SIZE];
   RECT rc;
   int i;
-  struct citizen_type citizens[MAX_CITY_SIZE];
-  int num_citizens = pcity->size;
+  int num_citizens = get_city_citizen_types(pcity, index, citizens);
   int pix_width = HAPPINESS_PIX_WIDTH * tileset_small_sprite_width(tileset);
   int offset = MIN(tileset_small_sprite_width(tileset), pix_width / 
num_citizens);
   /* int true_pix_width = (num_citizens - 1) * offset + 
tileset_small_sprite_width(tileset); */
-  hdc=CreateCompatibleDC(NULL);
-  old=SelectObject(hdc,bmp);
+  HDC hdc = CreateCompatibleDC(NULL);
+  HBITMAP old=SelectObject(hdc,bmp);
+
   rc.left=0;
   rc.top=0;
   rc.right=pix_width;
   rc.bottom=tileset_small_sprite_height(tileset);
   FillRect(hdc,&rc,(HBRUSH)GetClassLong(root_window,GCL_HBRBACKGROUND));
 
-  get_city_citizen_types(pcity, index, citizens);
-
   for (i = 0; i < num_citizens; i++) {
     draw_sprite(get_citizen_sprite(tileset, citizens[i], i, pcity),
                hdc, i * offset, 0);
Index: client/packhand.c
===================================================================
--- client/packhand.c   (revision 13911)
+++ client/packhand.c   (working copy)
@@ -406,19 +406,18 @@
 ****************************************************************************/
 void handle_city_info(struct packet_city_info *packet)
 {
-  int i;
   struct universal product;
-  bool city_is_new, city_has_changed_owner = FALSE;
+  int caravan_city_id;
+  int i;
+  bool city_is_new = FALSE;
+  bool city_has_changed_owner = FALSE;
   bool need_units_dialog_update = FALSE;
-  struct city *pcity;
   bool popup, update_descriptions = FALSE, name_changed = FALSE;
   bool shield_stock_changed = FALSE;
   bool production_changed = FALSE;
   struct unit_list *pfocus_units = get_units_in_focus();
-  int caravan_city_id;
+  struct city *pcity = game_find_city_by_number(packet->id);
 
-  pcity=game_find_city_by_number(packet->id);
-
   if (pcity && (player_number(pcity->owner) != packet->owner)) {
     client_remove_city(pcity);
     pcity = NULL;
@@ -451,8 +450,6 @@
     idex_register_city(pcity);
     update_descriptions = TRUE;
   } else {
-    city_is_new = FALSE;
-
     name_changed = (strcmp(pcity->name, packet->name) != 0);
 
     /* Check if city desciptions should be updated */
@@ -477,17 +474,31 @@
   pcity->tile = map_pos_to_tile(packet->x, packet->y);
   sz_strlcpy(pcity->name, packet->name);
   
-  pcity->size = packet->size;
-  for (i = 0; i < 5; i++) {
-    pcity->ppl_happy[i] = packet->ppl_happy[i];
-    pcity->ppl_content[i] = packet->ppl_content[i];
-    pcity->ppl_unhappy[i] = packet->ppl_unhappy[i];
-    pcity->ppl_angry[i] = packet->ppl_angry[i];
+  /* check data */
+  pcity->size = 0;
+  for (i = 0; i < FEELING_LAST; i++) {
+    pcity->feel[CITIZEN_HAPPY][i] = packet->ppl_happy[i];
+    pcity->feel[CITIZEN_CONTENT][i] = packet->ppl_content[i];
+    pcity->feel[CITIZEN_UNHAPPY][i] = packet->ppl_unhappy[i];
+    pcity->feel[CITIZEN_ANGRY][i] = packet->ppl_angry[i];
   }
+  for (i = 0; i < CITIZEN_LAST; i++) {
+    pcity->size += pcity->feel[i][FEELING_FINAL];
+  }
   specialist_type_iterate(sp) {
+    pcity->size +=
     pcity->specialists[sp] = packet->specialists[sp];
   } specialist_type_iterate_end;
 
+  if (pcity->size != packet->size) {
+    freelog(LOG_ERROR, "handle_city_info()"
+            " %d citizens not equal %d city size in \"%s\".",
+            pcity->size,
+            packet->size,
+            pcity->name);
+    pcity->size = packet->size;
+  }
+
   pcity->city_options = packet->city_options;
 
   for (i = 0; i < NUM_TRADEROUTES; i++) {
@@ -709,29 +720,25 @@
 ****************************************************************************/
 void handle_city_short_info(struct packet_city_short_info *packet)
 {
-  struct city *pcity;
-  bool city_is_new, city_has_changed_owner = FALSE;
+  bool city_has_changed_owner = FALSE;
+  bool city_is_new = FALSE;
   bool update_descriptions = FALSE;
+  struct city *pcity = game_find_city_by_number(packet->id);
 
-  pcity=game_find_city_by_number(packet->id);
-
   if (pcity && (player_number(pcity->owner) != packet->owner)) {
     client_remove_city(pcity);
     pcity = NULL;
     city_has_changed_owner = TRUE;
   }
 
-  if(!pcity) {
+  if (!pcity) {
     city_is_new = TRUE;
     pcity = create_city_virtual(player_by_number(packet->owner),
                                map_pos_to_tile(packet->x, packet->y),
                                packet->name);
     pcity->id=packet->id;
     idex_register_city(pcity);
-  }
-  else {
-    city_is_new = FALSE;
-
+  } else {
     /* Check if city desciptions should be updated */
     if (draw_city_names && strcmp(pcity->name, packet->name) != 0) {
       update_descriptions = TRUE;
@@ -741,9 +748,12 @@
     sz_strlcpy(pcity->name, packet->name);
     
     assert(pcity->id == packet->id);
+
+    memset(pcity->feel, 0, sizeof(pcity->feel));
+    memset(pcity->specialists, 0, sizeof(pcity->specialists));
   }
-  
-  pcity->size=packet->size;
+  pcity->specialists[DEFAULT_SPECIALIST] =
+  pcity->size = packet->size;
 
   /* HACK: special case for trade routes */
   pcity->citizen_base[O_TRADE] = packet->tile_trade;
@@ -754,18 +764,6 @@
   pcity->client.happy = packet->happy;
   pcity->client.unhappy = packet->unhappy;
 
-  pcity->ppl_happy[4] = 0;
-  pcity->ppl_content[4] = 0;
-  pcity->ppl_unhappy[4] = 0;
-  pcity->ppl_angry[4] = 0;
-  if (packet->happy) {
-    pcity->ppl_happy[4] = pcity->size;
-  } else if (packet->unhappy) {
-    pcity->ppl_unhappy[4] = pcity->size;
-  } else {
-    pcity->ppl_content[4] = pcity->size;
-  }
-
   if (city_is_new) {
     int i;
 
@@ -785,9 +783,6 @@
     int i;
     int x, y;
 
-    specialist_type_iterate(sp) {
-      pcity->specialists[sp] = 0;
-    } specialist_type_iterate_end;
     for (i = 0; i < NUM_TRADEROUTES; i++) {
       pcity->trade[i] = 0;
       pcity->trade_value[i] = 0;
Index: client/gui-sdl/citydlg.c
===================================================================
--- client/gui-sdl/citydlg.c    (revision 13911)
+++ client/gui-sdl/citydlg.c    (working copy)
@@ -2060,8 +2060,8 @@
   FREESURFACE(pSurf);
   FREESTRING16(pStr);
 
-  count = (pCity->ppl_happy[4] + pCity->ppl_content[4]
-          + pCity->ppl_unhappy[4] + pCity->ppl_angry[4]
+  count = (pCity->feel[CITIZEN_HAPPY][FEELING_FINAL] + 
pCity->feel[CITIZEN_CONTENT][FEELING_FINAL]
+          + pCity->feel[CITIZEN_UNHAPPY][FEELING_FINAL] + 
pCity->feel[CITIZEN_ANGRY][FEELING_FINAL]
           + pCity->specialists[SP_ELVIS] + pCity->specialists[SP_SCIENTIST]
           + pCity->specialists[SP_TAXMAN]);
 
@@ -2071,11 +2071,11 @@
     step = pIcons->pMale_Happy->w;
   }
 
-  for (j = 0; j < 5; j++) {
-    if (j == 0 || pCity->ppl_happy[j - 1] != pCity->ppl_happy[j]
-       || pCity->ppl_content[j - 1] != pCity->ppl_content[j]
-       || pCity->ppl_unhappy[j - 1] != pCity->ppl_unhappy[j]
-       || pCity->ppl_angry[j - 1] != pCity->ppl_angry[j]) {
+  for (j = 0; j < FEELING_LAST; j++) {
+    if (j == 0 || pCity->feel[CITIZEN_HAPPY][j - 1] != 
pCity->feel[CITIZEN_HAPPY][j]
+       || pCity->feel[CITIZEN_CONTENT][j - 1] != 
pCity->feel[CITIZEN_CONTENT][j]
+       || pCity->feel[CITIZEN_UNHAPPY][j - 1] != 
pCity->feel[CITIZEN_UNHAPPY][j]
+       || pCity->feel[CITIZEN_ANGRY][j - 1] != pCity->feel[CITIZEN_ANGRY][j]) {
 
       if (j != 0) {
        putline(pCityWindow->dst->surface, dest.x, dest.y, dest.x + 
adj_size(176), dest.y,
@@ -2083,9 +2083,9 @@
        dest.y += adj_size(5);
       }
 
-      if (pCity->ppl_happy[j]) {
+      if (pCity->feel[CITIZEN_HAPPY][j]) {
        pSurf = pIcons->pMale_Happy;
-       for (i = 0; i < pCity->ppl_happy[j]; i++) {
+       for (i = 0; i < pCity->feel[CITIZEN_HAPPY][j]; i++) {
          alphablit(pSurf, NULL, pCityWindow->dst->surface, &dest);
          dest.x += step;
          if (pSurf == pIcons->pMale_Happy) {
@@ -2096,9 +2096,9 @@
        }
       }
 
-      if (pCity->ppl_content[j]) {
+      if (pCity->feel[CITIZEN_CONTENT][j]) {
        pSurf = pIcons->pMale_Content;
-       for (i = 0; i < pCity->ppl_content[j]; i++) {
+       for (i = 0; i < pCity->feel[CITIZEN_CONTENT][j]; i++) {
          alphablit(pSurf, NULL, pCityWindow->dst->surface, &dest);
          dest.x += step;
          if (pSurf == pIcons->pMale_Content) {
@@ -2109,9 +2109,9 @@
        }
       }
 
-      if (pCity->ppl_unhappy[j]) {
+      if (pCity->feel[CITIZEN_UNHAPPY][j]) {
        pSurf = pIcons->pMale_Unhappy;
-       for (i = 0; i < pCity->ppl_unhappy[j]; i++) {
+       for (i = 0; i < pCity->feel[CITIZEN_UNHAPPY][j]; i++) {
          alphablit(pSurf, NULL, pCityWindow->dst->surface, &dest);
          dest.x += step;
          if (pSurf == pIcons->pMale_Unhappy) {
@@ -2122,9 +2122,9 @@
        }
       }
 
-      if (pCity->ppl_angry[j]) {
+      if (pCity->feel[CITIZEN_ANGRY][j]) {
        pSurf = pIcons->pMale_Angry;
-       for (i = 0; i < pCity->ppl_angry[j]; i++) {
+       for (i = 0; i < pCity->feel[CITIZEN_ANGRY][j]; i++) {
          alphablit(pSurf, NULL, pCityWindow->dst->surface, &dest);
          dest.x += step;
          if (pSurf == pIcons->pMale_Angry) {
@@ -3349,8 +3349,8 @@
   /* count != 0 */
   /* ==================================================== */
   /* Draw Citizens */
-  count = (pCity->ppl_happy[4] + pCity->ppl_content[4]
-          + pCity->ppl_unhappy[4] + pCity->ppl_angry[4]
+  count = (pCity->feel[CITIZEN_HAPPY][FEELING_FINAL] + 
pCity->feel[CITIZEN_CONTENT][FEELING_FINAL]
+          + pCity->feel[CITIZEN_UNHAPPY][FEELING_FINAL] + 
pCity->feel[CITIZEN_ANGRY][FEELING_FINAL]
           + pCity->specialists[SP_ELVIS] + pCity->specialists[SP_SCIENTIST]
           + pCity->specialists[SP_TAXMAN]);
 
@@ -3367,8 +3367,8 @@
 
   FREESURFACE(pBuf);
   
-  if (pCity->ppl_happy[4]) {
-    for (i = 0; i < pCity->ppl_happy[4]; i++) {
+  if (pCity->feel[CITIZEN_HAPPY][FEELING_FINAL]) {
+    for (i = 0; i < pCity->feel[CITIZEN_HAPPY][FEELING_FINAL]; i++) {
       pBuf = adj_surf(get_citizen_surface(CITIZEN_HAPPY, i));
       
       alphablit(pBuf, NULL, pWindow->dst->surface, &dest);
@@ -3377,8 +3377,8 @@
     }
   }
 
-  if (pCity->ppl_content[4]) {
-    for (i = 0; i < pCity->ppl_content[4]; i++) {
+  if (pCity->feel[CITIZEN_CONTENT][FEELING_FINAL]) {
+    for (i = 0; i < pCity->feel[CITIZEN_CONTENT][FEELING_FINAL]; i++) {
       pBuf = adj_surf(get_citizen_surface(CITIZEN_CONTENT, i));
       
       alphablit(pBuf, NULL, pWindow->dst->surface, &dest);
@@ -3387,8 +3387,8 @@
     }
   }
 
-  if (pCity->ppl_unhappy[4]) {
-    for (i = 0; i < pCity->ppl_unhappy[4]; i++) {
+  if (pCity->feel[CITIZEN_UNHAPPY][FEELING_FINAL]) {
+    for (i = 0; i < pCity->feel[CITIZEN_UNHAPPY][FEELING_FINAL]; i++) {
       pBuf = adj_surf(get_citizen_surface(CITIZEN_UNHAPPY, i));
       
       alphablit(pBuf, NULL, pWindow->dst->surface, &dest);
@@ -3397,8 +3397,8 @@
     }
   }
 
-  if (pCity->ppl_angry[4]) {
-    for (i = 0; i < pCity->ppl_angry[4]; i++) {
+  if (pCity->feel[CITIZEN_ANGRY][FEELING_FINAL]) {
+    for (i = 0; i < pCity->feel[CITIZEN_ANGRY][FEELING_FINAL]; i++) {
       pBuf = adj_surf(get_citizen_surface(CITIZEN_ANGRY, i));
       alphablit(pBuf, NULL, pWindow->dst->surface, &dest);
       dest.x += step;
Index: client/gui-sdl/gui_tilespec.h
===================================================================
--- client/gui-sdl/gui_tilespec.h       (revision 13911)
+++ client/gui-sdl/gui_tilespec.h       (working copy)
@@ -216,9 +216,7 @@
 static inline SDL_Surface *get_citizen_surface(enum citizen_category type,
                                                int citizen_index)
 {
-  struct citizen_type ctype = {.type = type};
-
-  return GET_SURF(get_citizen_sprite(tileset, ctype, 0, NULL));
+  return GET_SURF(get_citizen_sprite(tileset, type, 0, NULL));
 }
 
 static inline SDL_Surface *get_nation_flag_surface(const struct nation_type 
*pnation)
Index: client/tilespec.c
===================================================================
--- client/tilespec.c   (revision 13911)
+++ client/tilespec.c   (working copy)
@@ -57,6 +57,7 @@
 #include "options.h"           /* for fill_xxx */
 #include "themes_common.h"
 
+#include "citydlg_common.h"    /* for generate_citydlg_dimensions() */
 #include "tilespec.h"
 
 #define TILESPEC_CAPSTR "+tilespec4+2007.Oct.26 duplicates_ok"
@@ -84,7 +85,6 @@
 #define MAX_INDEX_HALF                  16
 #define MAX_INDEX_VALID                        256
 
-#define NUM_TILES_CITIZEN CITIZEN_LAST
 #define NUM_TILES_HP_BAR 11
 #define NUM_TILES_DIGITS 10
 #define NUM_TILES_SELECT 4
@@ -186,7 +186,7 @@
      * sprites, as defined by the tileset. */
     int count;
     struct sprite *sprite[MAX_NUM_CITIZEN_SPRITES];
-  } citizen[NUM_TILES_CITIZEN], specialist[SP_MAX];
+  } citizen[CITIZEN_LAST], specialist[SP_MAX];
   struct sprite *spaceship[SPACESHIP_COUNT];
   struct {
     int hot_x, hot_y;
@@ -996,7 +996,7 @@
   tileset_changed();
   can_slide = FALSE;
   center_tile_mapcanvas(center_tile);
-  /* update_map_cavnas_visible forces a full redraw.  Otherwise with fast
+  /* update_map_canvas_visible forces a full redraw.  Otherwise with fast
    * drawing we might not get one.  Of course this is slower. */
   update_map_canvas_visible();
   can_slide = TRUE;
@@ -1706,13 +1706,11 @@
 /**********************************************************************
   Returns a text name for the citizen, as used in the tileset.
 ***********************************************************************/
-static const char *get_citizen_name(struct citizen_type citizen)
+static const char *citizen_rule_name(enum citizen_category citizen)
 {
   /* These strings are used in reading the tileset.  Do not
    * translate. */
-  switch (citizen.type) {
-  case CITIZEN_SPECIALIST:
-    return specialist_rule_name(specialist_by_number(citizen.spec_type));
+  switch (citizen) {
   case CITIZEN_HAPPY:
     return "happy";
   case CITIZEN_CONTENT:
@@ -1721,10 +1719,10 @@
     return "unhappy";
   case CITIZEN_ANGRY:
     return "angry";
-  case CITIZEN_LAST:
+  default:
     break;
   }
-  die("unknown citizen type %d", (int) citizen.type);
+  die("unknown citizen type %d", (int) citizen);
   return NULL;
 }
 
@@ -1917,13 +1915,9 @@
   /* Load the specialist sprite graphics. */
   char buffer[512];
   int j;
-  struct citizen_type c = {
-    .type = CITIZEN_SPECIALIST,
-    .spec_type = id
-  };
-  const char *name = get_citizen_name(c);
+  const char *name = specialist_rule_name(specialist_by_number(id));
 
-  for (j = 0; j < NUM_TILES_CITIZEN; j++) {
+  for (j = 0; j < MAX_NUM_CITIZEN_SPRITES; j++) {
     my_snprintf(buffer, sizeof(buffer), "specialist.%s_%d", name, j);
     t->sprites.specialist[id].sprite[j] = load_sprite(t, buffer);
     if (!t->sprites.specialist[id].sprite[j]) {
@@ -1946,11 +1940,10 @@
   char buffer[512];
 
   /* Load the citizen sprite graphics, no specialist. */
-  for (i = 0; i < CITIZEN_SPECIALIST; i++) {
-    struct citizen_type c = {.type = i};
-    const char *name = get_citizen_name(c);
+  for (i = 0; i < CITIZEN_LAST; i++) {
+    const char *name = citizen_rule_name(i);
 
-    for (j = 0; j < NUM_TILES_CITIZEN; j++) {
+    for (j = 0; j < MAX_NUM_CITIZEN_SPRITES; j++) {
       my_snprintf(buffer, sizeof(buffer), "citizen.%s_%d", name, j);
       t->sprites.citizen[i].sprite[j] = load_sprite(t, buffer);
       if (!t->sprites.citizen[i].sprite[j]) {
@@ -4725,16 +4718,18 @@
   used as a picture).
 **************************************************************************/
 struct sprite *get_citizen_sprite(const struct tileset *t,
-                                 struct citizen_type type,
+                                 enum citizen_category type,
                                  int citizen_index,
                                  const struct city *pcity)
 {
   const struct citizen_graphic *graphic;
 
-  if (type.type == CITIZEN_SPECIALIST) {
-    graphic = &t->sprites.specialist[type.spec_type];
+  if (type < CITIZEN_SPECIALIST) {
+    assert(type >= 0);
+    graphic = &t->sprites.citizen[type];
   } else {
-    graphic = &t->sprites.citizen[type.type];
+    assert(type < (CITIZEN_SPECIALIST + SP_MAX));
+    graphic = &t->sprites.specialist[type - CITIZEN_SPECIALIST];
   }
 
   return graphic->sprite[citizen_index % graphic->count];
Index: client/tilespec.h
===================================================================
--- client/tilespec.h   (revision 13911)
+++ client/tilespec.h   (working copy)
@@ -20,7 +20,7 @@
 
 #include "fc_types.h"
 
-#include "citydlg_common.h"    /* enum citizen_type */
+#include "city.h"              /* enum citizen_category */
 #include "options.h"
 
 struct sprite;                 /* opaque; gui-dep */
@@ -215,7 +215,7 @@
 struct sprite *get_spaceship_sprite(const struct tileset *t,
                                    enum spaceship_part part);
 struct sprite *get_citizen_sprite(const struct tileset *t,
-                                 struct citizen_type type,
+                                 enum citizen_category type,
                                  int citizen_index,
                                  const struct city *pcity);
 struct sprite *get_city_flag_sprite(const struct tileset *t,
Index: client/mapview_common.c
===================================================================
--- client/mapview_common.c     (revision 13911)
+++ client/mapview_common.c     (working copy)
@@ -35,6 +35,7 @@
 #include "climap.h"
 #include "control.h"
 #include "goto.h"
+#include "citydlg_common.h"
 #include "mapview_common.h"
 #include "overview_common.h"
 #include "tilespec.h"
Index: client/climisc.c
===================================================================
--- client/climisc.c    (revision 13911)
+++ client/climisc.c    (working copy)
@@ -390,9 +390,7 @@
   } else {
     /* HACK: the UNHAPPY citizen is used for the government
      * when we don't know any better. */
-    struct citizen_type c = {.type = CITIZEN_UNHAPPY};
-
-    return get_citizen_sprite(tileset, c, 0, NULL);
+    return get_citizen_sprite(tileset, CITIZEN_UNHAPPY, 0, NULL);
   }
 }
 
_______________________________________________
Freeciv-dev mailing list
Freeciv-dev@gna.org
https://mail.gna.org/listinfo/freeciv-dev

Reply via email to