City happiness calculation algorithm

FoxAhead

Warlord
Joined
Sep 7, 2017
Messages
192

Foreword​

Again reinventing the wheel, but this time from the decompilation perspective.

Previous reading​

Notes about formulas:​

  • Operator % is Modulo Operator. The modulo division operator produces the remainder of an integer division.
  • All divisions are integer. The result of the division is truncated towards zero before next operation.

Algorithm​

Information about the mood in the city is represented by three values.
H – number of Happy Citizens. Teal color.
U – number of Unhappy Citizens (includes Angry Citizens). Red color.
A – number of Angry Citizens (or Very Unhappy). Is included in Unhappy, so it is how much of Unhappy are additionally unhappy. Black color.
The happiness calculation consists of 5 phases. After each phase, values H, U, A are normalized and recorded in array of phases for later use. The normalization procedure will be described at the end.

Phase 0​

CRF – Cosmic constant “Riot factor based on # cities (higher factor lessens the effect)” defined in RULES.TXT. Default 14.
CSFU – Cosmic constant “City size for first unhappiness at Chieftain level” defined in RULES.TXT. Default 7.
DL – Difficulty Level (0 – Chieftain … 5 – Deity).
So (CSFU - DL) represents city size for first unhappiness at current Difficulty Level.
Note. If (CSFU - DL) is lower than 2 (which is not the case with default values) then if this is the capital and there is no attacking units causing unhappiness and no attacking units in city, then (CSFU - DL) is equal to 2. Again, this rule can be completely ignored with the default cosmic values.
S – City Size.
BU – Base Unhappiness, caused just by the City Size.
BU = S - 1 - ((CSFU – DL) - 2)

1665091303002.png


RF – Riot Factor.
LM – Large Map (2 if Map X Size * Map Y Size >= 6000; 0 otherwise).
RF = CRF - 2 * DL + LM
G – Government (0 – Anarchy … 6 – Democracy).
GF – Government Factor
GF = G / 2 + 2
GFRF – Combined Factor. Represents maximum number of cities which don’t cause additional Unhappiness due to Number of Cities (UDNC).
GFRF = GF * RF / 2

1665091655678.png


NC – Number of Cities.
CI – City Index in cities array. From 0 to 255.
UDNC – Additional Unhappiness due to Number of Cities. Communism doesn’t have UDNC.
UDNC = (NC - GFRF + CI % GFRF) / GFRF
Example for Emperor, Democracy, not Large Map (less than 6000).

1665092734580.png


So as was stated before, GFRF is the maximum number of cities which doesn’t cause additional unhappiness due to number of cities. With GFRF = 15, this can be seen at the row for 15 cities. And at the row for 30 cities (double of 15) all cities have one additional unhappy citizen. Between those, the numbers gradually increase in the triangle-like pattern. And so on. Once you understand this pattern, you can make a table for other combinations of Difficulty Level, Government and Map size.
Now the initial total Unhappiness is equal to:
U = BU + UDNC
By the way, AI’s initial total Unhappiness is always equal to Base Unhappiness at King level:
Uai = S - 1 - (CSFU - 5)
Finally, number of Angry citizens (black) is the surplus of Unhappiness over City Size:
If U > S
A = U - S
U = S

Note. U includes A. This means A is number of Unhappy citizens additionally converted from red to black.
Initial Happiness is zero.

Phase 1​

The luxury causes Happiness. Two cups for one H.
H = Lux / 2

Phase 2​

  1. Colosseum
    -3 U (-4 with Electronics).
  2. Monotheism and Cathedral (or Michelangelo's Chapel)
    -3 U
    +1 U with Communism
    -1 U with Theology
  3. Temple
    U = U - dU
    +1 dU with Mysticism
    +1 dU with Ceremonial Burial
    x2 dU with Oracle
  4. Courthouse (or Palace) with Democracy
    +1 H

Phase 3​

  1. Fundamentalism
    A = 0
    U = 0
  2. Republic or Democracy
    k = 1 without Women's Suffrage and Police Station
    +1 k for Democracy
    Uu = unhappiness caused by units far from city. If non-zero, then for Republic it is reduced by 1.
    U = U + Uu * k
  3. Other – Martial Law
    Up to 3 units with non-zero Attack reduce unhappiness.
    Each of these units lowers U by 1 (2 for Communism), but not below 0.

Phase 4​

  1. Hanging Gardens
    +1 H (+3 for city with this wonder)
  2. Cure for Cancer
    +1 H
  3. Shakespeare's Theatre
    U = 0 for city with this wonder
  4. J. S. Bach's Cathedral
    -2 U

Normalization after each phase​

F – number of Free Citizens (those who do not work on the map, i.e. - specialists).
a) Balance values
C-like:
// Step 1
H = clamp(H, 0, S);
// Step 2
while A && A > U
{
  --A;
  ++U;
}
// Step 3
for (U=clamp(U,0,S); clamp(S-F,0,99)<H+U; U=clamp(U,0,S))
{
  if A {
    --A;
  }
  else
  {
    --H;
    H = clamp(H, 0, S);
  }
  --U;
}
// Step 4
while A && S-F > H+U
{
  --A;
  ++U;
}
Note. Clamp function ensures that the first argument is between second and third.

b) Store H, U, A in arrays at respective index for current phase. It is used, for example, in city window in Happiness Analysis tab.

Comments​

As you can see, after each phase, before normalization, sum H + U can become larger than available places in city (S - F). The normalization procedure just balances values and puts them in the boundaries, before finalizing the current phase.

However, the underlying algorithm may raise some questions. For example, after phase 1, the value of H appears (brought by luxury). If after that, H and U do not fit in the city in total, then if A is present, the number of A and U simply decreases, creating the feeling that H will replace A. Actually, this is how the well-known A to H conversion glitch (or "happiness quirk") appears. When A is no more, U decreases with H, as if they cancel each other out.

Examples​

Let’s examine some extreme cases. Saves attached.

Example 1​

This simplified example demonstrates happiness quirk when happy citizen displaces angry citizen.
Difficulty: Deity.
Government: Despotism.
Not large map: 50x80 = 4000 squares.
Number of cities NC = 24.
No wonders.
City Bapedi with index CI = 2, and size S = 10, without “phase 2” buildings, no specialists (F = 0), with 3 luxury cups.
No units in the city.

Phase 0​

BU = 9
GFRF = 4
With NC of 24 all cities have the same UDNC regardless of CI.
UDNC = 5
U = BU + UDNC = 14
UUUUUUUUUUUUUU
Because U > S, then
A = U - S = 4
U = S = 10
H = 0
UUUUUUUUUU
......AAAA

Normalization after phase 0​

No effect

Phase 1​

H = Lux / 2 = 3 / 2 = 1 (integer division)
HUUUUUUUUUU
.......AAAA

Normalization after phase 1​

Step 1
No effect. H = 1 is between 0 and S, so it stays 1.
Step 2
No effect, because A < U.
Step 3
Because clamp(S-F,0,99)<H+U (10 < 1 + 10), then there goes one iteration.
Because A <> 0, then:
A = A - 1 = 4 - 1 = 3
Also, each iteration:
U = U - 1 = 10 - 1 = 9
HUUUUUUUUU
.......AAA

Because now condition clamp(S-F,0,99)<H+U is not true, then no more iterations.
Step 4
No effect, because condition S-F > H+U is not true.

At this simplified example further phases have no effect. So, the final picture is:
HUUUUUUAAA
This is what you see on the city screen. Remember that U includes A. Here A = 3 and U = 9 (not 6!). Think of A as a second row of U, as if A were "shading" U standing in front of it. This is important because in most cases A is not taken into account. For example, when deciding whether to put a city into disorder or into WLTKD mode, the A is not taken into account at all.

Example 2​

Now let’s extend previous example and add Shakespeare's Theatre to this city.
Because of angry citizens before phase 4, this wonder will not eliminate the discontent completely. Let’s show this.
Before phase 4 we have the same picture:
HUUUUUUUUU
.......AAA

Phase 4​

Because of Shakespeare's Theatre in this city, U = 0.
H.........
.......AAA

Normalization after phase 4​

Step 2
While A <> 0 and A > U (3 > 0), convert A to U sequentially. This happens two times.
H.......UU
.........A

Step 4
While A <> 0 and S - F > H + U (10 - 0 > 1 + 2), convert A to U sequentially. This converts the rest of A to U, because there is enough room for H + U.
H......UUU

Now we have the city with unhappy citizens even with Shakespeare's Theatre:
HCCCCCCUUU

Complete code​

Just in case, I will provide the complete decompiled code of the procedures.
Spoiler CalcCityEconomics :
C-like:
void __cdecl Q_CalcCityEconomics_sub_4EA8E4(signed int aCity)
{
  int v1; // eax
  int v2; // eax
  int v3; // esi
  int v4; // eax
  int vOwner; // [esp+Ch] [ebp-28h]
  int j; // [esp+10h] [ebp-24h]
  int vPartner; // [esp+14h] [ebp-20h]
  int v8; // [esp+18h] [ebp-1Ch]
  int v9; // [esp+1Ch] [ebp-18h]
  int v10; // [esp+1Ch] [ebp-18h]
  int HasTech_sub_4BD9F0; // [esp+1Ch] [ebp-18h]
  int v12; // [esp+1Ch] [ebp-18h]
  int AttUnitsOfDiscontent; // [esp+1Ch] [ebp-18h]
  int vTrade; // [esp+20h] [ebp-14h]
  __int16 v15; // [esp+20h] [ebp-14h]
  signed int v16; // [esp+24h] [ebp-10h]
  int v17; // [esp+28h] [ebp-Ch]
  int vCommodity; // [esp+2Ch] [ebp-8h]
  int i; // [esp+30h] [ebp-4h]

  vOwner = V_Cities_stru_64F340[aCity].Owner;
  V_CityGlobals_stru_6A6528.TradeCorruption = j_Q_CalcCorruption_sub_4E989A(
                                                aCity,
                                                V_CityGlobals_stru_6A6528.TotalRes[2],
                                                0,
                                                0);
  if ( V_Civs_stru_64C6A0[vOwner].Government == 4 )
  {
    V_CityGlobals_stru_6A6528.TradeCorruption = 0;
  }
  if ( V_Civs_stru_64C6A0[vOwner].Government == 6 )
  {
    V_CityGlobals_stru_6A6528.TradeCorruption = 0;
  }
  V_Cities_stru_64F340[aCity].BaseTrade = LOWORD(V_CityGlobals_stru_6A6528.TotalRes[2])
                                        - LOWORD(V_CityGlobals_stru_6A6528.TradeCorruption);
  sub_402414(aCity);
  for ( i = 0; V_Cities_stru_64F340[aCity].TradeRoutes > i; ++i )
  {
    vPartner = V_Cities_stru_64F340[aCity].TradePartner[i];
    vTrade = (V_Cities_stru_64F340[vPartner].BaseTrade + V_Cities_stru_64F340[aCity].BaseTrade + 4) >> 3;
    vCommodity = V_Cities_stru_64F340[aCity].CommodityTraded[i];
    v16 = j_Q_PFFindConnection_sub_488A45(
            V_Cities_stru_64F340[aCity].Owner,
            V_Cities_stru_64F340[aCity].X,
            V_Cities_stru_64F340[aCity].Y,
            V_Cities_stru_64F340[vPartner].X,
            V_Cities_stru_64F340[vPartner].Y);
    if ( j_Q_CityHasImprovement_sub_43D20A(aCity, 0x20) )// Airport
    {
      if ( j_Q_CityHasImprovement_sub_43D20A(vPartner, 0x20) )// Airport
      {
        v1 = v16;
        if ( v16 <= 1 )
        {
          v1 = 1;
        }
        v16 = v1;
      }
    }
    if ( j_Q_CityHasImprovement_sub_43D20A(aCity, 0x19) )// Superhighways
    {
      ++v16;
    }
    if ( v16 )
    {
      vTrade += (vTrade * v16) >> 1;
    }
    if ( vCommodity < 0 )                       // Food supplies
    {
      vTrade = 0;
    }
    if ( V_Cities_stru_64F340[vPartner].Owner == vOwner )
    {
      vTrade >>= 1;
    }
    V_CityGlobals_stru_6A6528.TradeRevenue[i] = vTrade;
    V_CityGlobals_stru_6A6528.TotalRes[2] += vTrade;
  }
  V_CityGlobals_stru_6A6528.TradeCorruption = j_Q_CalcCorruption_sub_4E989A(
                                                aCity,
                                                V_CityGlobals_stru_6A6528.TotalRes[2],
                                                0,
                                                1);
  if ( V_Civs_stru_64C6A0[vOwner].Government == 4 )
  {
    V_CityGlobals_stru_6A6528.TradeCorruption = 0;
  }
  if ( V_Civs_stru_64C6A0[vOwner].Government == 6 )
  {
    V_CityGlobals_stru_6A6528.TradeCorruption = 0;
  }
  v15 = LOWORD(V_CityGlobals_stru_6A6528.TotalRes[2]) - LOWORD(V_CityGlobals_stru_6A6528.TradeCorruption);
  sub_403620(aCity, V_CityGlobals_stru_6A6528.TotalRes[2] - V_CityGlobals_stru_6A6528.TradeCorruption, 0, 0);
  V_CityGlobals_stru_6A6528.HappyCitizens = 0;
  if ( (V_GameParameters_stru_655AE8.HumanPlayersBits & (1 << vOwner)) != 0 )
  {
    v9 = V_Cosmic_stru_64BCC8.RiotFactor - 2 * V_GameParameters_stru_655AE8.DifficultyLevel;
    if ( (V_GameParameters_stru_655AE8.MapFlags & 4) != 0 )
    {
      v9 += 2;
    }
    v2 = v9 * (((int)V_Civs_stru_64C6A0[vOwner].Government >> 1) + 2) / 2;
    if ( v2 <= 1 )
    {
      v2 = 1;
    }
    v10 = v2;
    v8 = V_Cosmic_stru_64BCC8.CitySizeFirstUnhappinessChieftain - V_GameParameters_stru_655AE8.DifficultyLevel;
    if ( v8 <= 2 )
    {
      if ( j_Q_CityHasImprovement_sub_43D20A(aCity, 1) )// Palace
      {
        if ( !V_CityGlobals_stru_6A6528.AttUnitsOfDiscontent && !V_CityGlobals_stru_6A6528.AttUnitsInCity )
        {
          v8 = 2;
        }
      }
    }
    V_CityGlobals_stru_6A6528.UnhappyCitizens = V_Cities_stru_64F340[aCity].Size - 1 - (v8 - 2);
    if ( V_Civs_stru_64C6A0[vOwner].Government != 3 )
    {
      V_CityGlobals_stru_6A6528.UnhappyCitizens += (V_Civs_stru_64C6A0[vOwner].Cities - v10 + aCity % v10) / v10;
    }
  }
  else
  {
    V_CityGlobals_stru_6A6528.UnhappyCitizens = V_Cities_stru_64F340[aCity].Size
                                              - 1
                                              - (V_Cosmic_stru_64BCC8.CitySizeFirstUnhappinessChieftain
                                               - 5);
  }
  V_CityGlobals_stru_6A6528.AngryCitizens = 0;
  if ( V_Cities_stru_64F340[aCity].Size < V_CityGlobals_stru_6A6528.UnhappyCitizens )
  {
    V_CityGlobals_stru_6A6528.AngryCitizens = V_CityGlobals_stru_6A6528.UnhappyCitizens
                                            - V_Cities_stru_64F340[aCity].Size;
    V_CityGlobals_stru_6A6528.UnhappyCitizens = V_Cities_stru_64F340[aCity].Size;
  }
  j_Q_NormalizeHappinessPhase_sub_4EA031(aCity, 0);
  V_CityGlobals_stru_6A6528.HappyCitizens = V_CityGlobals_stru_6A6528.Lux >> 1;
  j_Q_NormalizeHappinessPhase_sub_4EA031(aCity, 1);
  if ( j_Q_CityHasImprovement_sub_43D20A(aCity, 0xE) )// Colosseum
  {
    V_CityGlobals_stru_6A6528.UnhappyCitizens -= 3;
    if ( j_Q_CivHasTech_sub_4BD9F0(vOwner, 0x18) )// Electronics
    {
      --V_CityGlobals_stru_6A6528.UnhappyCitizens;
    }
  }
  if ( j_Q_CivHasTech_sub_4BD9F0(vOwner, 0x37) )// Monotheism
  {
    if ( j_Q_CityHasImprovement_sub_43D20A(aCity, 0xB) || j_Q_CivHasWonder_sub_453E51(vOwner, 0xA) )// Cathedral || Michelangelo's Chapel
    {
      v3 = !j_Q_CivHasTech_sub_4BD9F0(vOwner, 0xF) + 2;// Communism
      V_CityGlobals_stru_6A6528.UnhappyCitizens -= j_Q_CivHasTech_sub_4BD9F0(vOwner, 0x52) + v3;// Theology
    }
  }
  if ( j_Q_CityHasImprovement_sub_43D20A(aCity, 4) )// Temple
  {
    HasTech_sub_4BD9F0 = j_Q_CivHasTech_sub_4BD9F0(vOwner, 0x38);// Mysticism
    if ( j_Q_CivHasTech_sub_4BD9F0(vOwner, 9) ) // Ceremonial Burial
    {
      ++HasTech_sub_4BD9F0;
    }
    if ( j_Q_CivHasWonder_sub_453E51(vOwner, 5) )// Oracle
    {
      HasTech_sub_4BD9F0 *= 2;
    }
    V_CityGlobals_stru_6A6528.UnhappyCitizens -= HasTech_sub_4BD9F0;
  }
  if ( (j_Q_CityHasImprovement_sub_43D20A(aCity, 7) || j_Q_CityHasImprovement_sub_43D20A(aCity, 1))// Courthouse || Palace
    && V_Civs_stru_64C6A0[vOwner].Government == 6 )
  {
    ++V_CityGlobals_stru_6A6528.HappyCitizens;
  }
  j_Q_NormalizeHappinessPhase_sub_4EA031(aCity, 2);
  if ( V_Civs_stru_64C6A0[vOwner].Government == 4 )
  {
    V_CityGlobals_stru_6A6528.AngryCitizens = 0;
    V_CityGlobals_stru_6A6528.UnhappyCitizens = 0;
  }
  else if ( V_Civs_stru_64C6A0[vOwner].Government >= 5u )
  {
    v17 = !j_Q_CivHasWonder_sub_453E51(vOwner, 0x15) && !j_Q_CityHasImprovement_sub_43D20A(aCity, 0x21);// Women's Suffrage || Police Station
    if ( V_Civs_stru_64C6A0[vOwner].Government == 6 )
    {
      ++v17;
    }
    if ( v17 )
    {
      AttUnitsOfDiscontent = V_CityGlobals_stru_6A6528.AttUnitsOfDiscontent;
      if ( V_CityGlobals_stru_6A6528.AttUnitsOfDiscontent )
      {
        if ( V_Civs_stru_64C6A0[vOwner].Government == 5 )
        {
          AttUnitsOfDiscontent = V_CityGlobals_stru_6A6528.AttUnitsOfDiscontent - 1;
        }
      }
      V_CityGlobals_stru_6A6528.UnhappyCitizens += AttUnitsOfDiscontent * v17;
    }
  }
  else
  {
    V_CityGlobals_stru_6A6528.field_3C = 0;
    for ( j = j_Q_MapGetTopUnit_sub_5B2E69(V_Cities_stru_64F340[aCity].X, V_Cities_stru_64F340[aCity].Y);
          j >= 0;
          j = j_Q_GetNextUnitInStack_sub_5B2C82(j) )
    {
      if ( V_UnitTypes_stru_64B1B8[stru_6560F0[j].UnitType].Att )
      {
        ++V_CityGlobals_stru_6A6528.field_3C;
        if ( V_Civs_stru_64C6A0[vOwner].Government == 3 )
        {
          ++V_CityGlobals_stru_6A6528.field_3C;
        }
      }
    }
    v12 = 3;
    if ( V_Civs_stru_64C6A0[vOwner].Government == 3 )
    {
      v12 = 6;
    }
    v4 = V_CityGlobals_stru_6A6528.field_3C;
    if ( V_CityGlobals_stru_6A6528.field_3C >= v12 )
    {
      v4 = v12;
    }
    V_CityGlobals_stru_6A6528.field_3C = v4;
    V_CityGlobals_stru_6A6528.UnhappyCitizens -= j_Q_Clamp_sub_5ADFA0(v4, 0, V_CityGlobals_stru_6A6528.UnhappyCitizens);
  }
  j_Q_NormalizeHappinessPhase_sub_4EA031(aCity, 3);
  if ( j_Q_CivHasWonder_sub_453E51(vOwner, 1) ) // Hanging Gardens
  {
    ++V_CityGlobals_stru_6A6528.HappyCitizens;
    if ( j_Q_ActiveWonderCity_sub_453E18(1) == aCity )
    {
      V_CityGlobals_stru_6A6528.HappyCitizens += 2;
    }
  }
  if ( j_Q_CivHasWonder_sub_453E51(vOwner, 0x1B) )// Cure for Cancer
  {
    ++V_CityGlobals_stru_6A6528.HappyCitizens;
  }
  if ( j_Q_ActiveWonderCity_sub_453E18(0xD) == aCity )
  {
    V_CityGlobals_stru_6A6528.UnhappyCitizens = 0;
  }
  if ( j_Q_CivHasWonder_sub_453E51(vOwner, 0xF) )// J. S. Bach's Cathedral
  {
    V_CityGlobals_stru_6A6528.UnhappyCitizens -= 2;
  }
  j_Q_NormalizeHappinessPhase_sub_4EA031(aCity, 4);
  V_Cities_stru_64F340[aCity].Science = V_CityGlobals_stru_6A6528.field_50;
  V_Cities_stru_64F340[aCity].Tax = V_CityGlobals_stru_6A6528.Tax;
  V_Cities_stru_64F340[aCity].Trade = v15;
  V_Cities_stru_64F340[aCity].TotalFood = V_CityGlobals_stru_6A6528.TotalRes[0];
  V_Cities_stru_64F340[aCity].TotalShield = V_CityGlobals_stru_6A6528.TotalRes[1];
  V_Cities_stru_64F340[aCity].HappyCitizens = V_CityGlobals_stru_6A6528.HappyCitizens;
  V_Cities_stru_64F340[aCity].UnHappyCitizens = V_CityGlobals_stru_6A6528.UnhappyCitizens;
}
Spoiler NormalizeHappinessPhase :
C-like:
void __cdecl Q_NormalizeHappinessPhase_sub_4EA031(int aCity, int aPhase)
{
  V_CityGlobals_stru_6A6528.HappyCitizens = j_Q_Clamp_sub_5ADFA0(
                                              V_CityGlobals_stru_6A6528.HappyCitizens,
                                              0,
                                              V_Cities_stru_64F340[aCity].Size);
  while ( V_CityGlobals_stru_6A6528.AngryCitizens
       && V_CityGlobals_stru_6A6528.AngryCitizens > V_CityGlobals_stru_6A6528.UnhappyCitizens )
  {
    --V_CityGlobals_stru_6A6528.AngryCitizens;
    ++V_CityGlobals_stru_6A6528.UnhappyCitizens;
  }
  for ( V_CityGlobals_stru_6A6528.UnhappyCitizens = j_Q_Clamp_sub_5ADFA0(
                                                      V_CityGlobals_stru_6A6528.UnhappyCitizens,
                                                      0,
                                                      V_Cities_stru_64F340[aCity].Size);
        j_Q_Clamp_sub_5ADFA0(V_Cities_stru_64F340[aCity].Size - V_CityGlobals_stru_6A6528.CitizensNotWorking, 0, 0x63) < V_CityGlobals_stru_6A6528.HappyCitizens + V_CityGlobals_stru_6A6528.UnhappyCitizens;
        V_CityGlobals_stru_6A6528.UnhappyCitizens = j_Q_Clamp_sub_5ADFA0(
                                                      V_CityGlobals_stru_6A6528.UnhappyCitizens,
                                                      0,
                                                      V_Cities_stru_64F340[aCity].Size) )
  {
    if ( V_CityGlobals_stru_6A6528.AngryCitizens )
    {
      --V_CityGlobals_stru_6A6528.AngryCitizens;
    }
    else
    {
      --V_CityGlobals_stru_6A6528.HappyCitizens;
      V_CityGlobals_stru_6A6528.HappyCitizens = j_Q_Clamp_sub_5ADFA0(
                                                  V_CityGlobals_stru_6A6528.HappyCitizens,
                                                  0,
                                                  V_Cities_stru_64F340[aCity].Size);
    }
    --V_CityGlobals_stru_6A6528.UnhappyCitizens;
  }
  while ( V_CityGlobals_stru_6A6528.AngryCitizens
       && V_Cities_stru_64F340[aCity].Size - V_CityGlobals_stru_6A6528.CitizensNotWorking > V_CityGlobals_stru_6A6528.HappyCitizens
                                                                                          + V_CityGlobals_stru_6A6528.UnhappyCitizens )
  {
    --V_CityGlobals_stru_6A6528.AngryCitizens;
    ++V_CityGlobals_stru_6A6528.UnhappyCitizens;
  }
  if ( aPhase >= 0 )
  {
    V_CityGlobals_stru_6A6528.HappyArray[aPhase] = V_CityGlobals_stru_6A6528.HappyCitizens;
    V_CityGlobals_stru_6A6528.UnhappyArray[aPhase] = V_CityGlobals_stru_6A6528.UnhappyCitizens;
    V_CityGlobals_stru_6A6528.AngryArray[aPhase] = V_CityGlobals_stru_6A6528.AngryCitizens;
  }
}
 

Attachments

  • Happiness.xlsx
    19.4 KB · Views: 30
  • HappinessAlgorithmExample1.sav
    70.3 KB · Views: 30
  • HappinessAlgorithmExample2.sav
    70.3 KB · Views: 28
Last edited:
This is...very complicated. I had no idea that it wasn't just some simple arithmetic. No wonder there could be some weird effects.
 
@FoxAhead - I was marveled that you described the function and I am even more amazed that you took time to detail the description and add examples. Chapeau bas!

From my perspective step 3 is bugged - I think the programmers forgot to finish the part "if A" - I imagine the function should look something like:
for (U=clamp(U,0,S); clamp(S-F,0,99)<H+U; U=clamp(U,0,S))
{
if A && H {
--A;
}
else
{
--H;
H = clamp(H, 0, S);
--U;
}
--H;
H = clamp(H, 0, S);

--U;
}
Alternatively, the "else" clause should be deleted and the code should be executed in every iteration.

Does anyone see a reason why there is a bonus for having angry citizens?
 
Top Bottom