How do Civ 3 calulates corruption

Antal1987

Warlord
Joined
Sep 4, 2013
Messages
160
Location
Karelia, Russia
By request of some forum's participants I'm sharing code of the function which calculates corruption and waste.

First of all, that function is multipurpose. It can compute commercial corruption or production waste depending on 3-rd parameter (Corruption_Type: 0 - Commerce, 1 - Production). 2-nd parameter takes origin value of economic component (total commerce or total production).

It returns an integer value, calculated from the origin value of the same economic type. It represents resulting corruption value of commece/production depending on corruption type.

The function was decompiled by IDA and Hex-Rays plugin. That's why it has many semantic differences and weird schemes

Well, here is the code:

Code:
// Corruption_Type:
// 0 - Commerce Corruption
// 1 - Production Loss
int __thiscall class_City::Calculate_Corruption(class_City *this, int Origin_Value, char Corruption_Type)
{
  int _Origin_Value; // esi@1
  int result; // eax@2
  int v5; // eax@5
  int _CapitalID_1; // ecx@5
  void *v7; // ecx@8
  int _Reduce_Corruption_Value; // ebx@12
  int _ImprovementID; // esi@12
  int v10; // edi@13
  int v11; // eax@16
  int _Reduce_Corruption_Value2; // ebp@19
  int v13; // esi@21
  __int64 v14; // qax@21
  int v15; // esi@21
  int _Max_Distance1; // ebx@24
  int _ImprovementID2; // ebp@24
  struct_Base_List_Item *_Cities; // esi@25
  int _ImprovementType_Offset; // ecx@25
  int _Impr_GovID; // eax@26
  int _CityID1; // eax@30
  void *v22; // eax@33
  class_City *_Wonder_City; // eax@34
  int _CityID2; // eax@36
  void *v25; // eax@39
  int _Small_Wonder_Distance; // eax@45
  int _Corruption_And_Waste2; // ebp@48
  int v28; // eax@48
  int _Reduce_Distance_Value; // esi@48
  int _Capital_City_ID; // eax@51
  int _CivID; // ST0C_4@52
  class_City *_Capital_City_2; // eax@52
  int v33; // eax@54
  int v34; // ecx@65
  __int64 _Cities_Count; // qax@68
  void *v36; // eax@73
  class_City *_City2; // ebp@74
  int v38; // esi@77
  int v39; // eax@77
  int v40; // ecx@77
  int v41; // eax@77
  int v42; // edi@78
  __int64 v43; // qax@80
  int _Distance_2; // ecx@80
  int v45; // eax@82
  int v46; // eax@83
  int v47; // ecx@85
  int v48; // esi@85
  class_City *v49; // edi@87
  int v50; // eax@87
  int v51; // edx@87
  int v52; // ecx@97
  int v53; // esi@97
  int v54; // eax@99
  int v55; // edx@99
  int v56; // ecx@109
  int v57; // eax@111
  int v58; // eax@116
  int v59; // eax@117
  int v60; // ecx@119
  signed int v61; // eax@121
  int v62; // ecx@126
  signed int v63; // eax@128
  int v64; // ecx@133
  signed int v65; // eax@135
  int v66; // eax@141
  int _Citizens_Corruption; // edi@143
  int v68; // ecx@143
  int v69; // eax@143
  int v70; // esi@143
  class_Citizen_Info *v71; // ebx@144
  class_Citizen_Body *v72; // edx@148
  class_Citizen *v73; // edx@149
  int v74; // eax@154
  signed int _Total_Corruption; // ecx@156
  class_City *_City; // [sp+8h] [bp-20h]@1
  int _Reduce_Corruption_Value3; // [sp+Ch] [bp-1Ch]@19
  int v78; // [sp+10h] [bp-18h]@25
  int _Ratio2; // [sp+10h] [bp-18h]@68
  class_City *_Capital1; // [sp+14h] [bp-14h]@9
  int v81; // [sp+18h] [bp-10h]@21
  int _Civ_GovID; // [sp+1Ch] [bp-Ch]@11
  int _Corruption_And_Waste; // [sp+20h] [bp-8h]@11
  int _Ratio1; // [sp+20h] [bp-8h]@67
  int _Distance_Capital; // [sp+24h] [bp-4h]@24
  int Corruption_Typea; // [sp+30h] [bp+8h]@69

  _Origin_Value = Origin_Value;
  _City = this;
  if ( Origin_Value <= 0 )
    return 0;
  if ( Corruption_Type && (this->Body.Status & 1) == 1 )
    return _Origin_Value;
  v5 = LOBYTE(this->Body.CivID);
  _CapitalID_1 = Civilizations[v5].CapitalID;
  if ( !Cities.Items
    || _CapitalID_1 < 0
    || _CapitalID_1 > Cities.LastIndex
    || (v7 = Cities.Items[_CapitalID_1].Object) == 0
    || (_Capital1 = (v7 - 28), v7 == 28) )
    return 0;
  _Civ_GovID = Civilizations[v5].GovenmentType;
  _Corruption_And_Waste = BIC_Data.Governments[_Civ_GovID].CurruptionAndWaste;
  if ( BIC_Data.Governments[_Civ_GovID].CurruptionAndWaste == CWT_Catastrophic )
    return _Origin_Value;
  _Reduce_Corruption_Value = 0;
  _ImprovementID = 0;
  if ( BIC_Data.ImprovementsCount > 0 )
  {
    v10 = 0;
    do
    {
      if ( class_City::Has_Improvement(_City, _ImprovementID, 1) )
      {
        if ( !class_City::Check_Improvement_Obsolete(_City, _ImprovementID) )
        {
          v11 = BIC_Data.Improvements[v10].ImprovementFlags;
          if ( BYTE1(v11) & ITF_B1_Reduces_Corruption )
            ++_Reduce_Corruption_Value;
        }
      }
      ++_ImprovementID;
      ++v10;
    }
    while ( _ImprovementID < BIC_Data.ImprovementsCount );
  }
  _Reduce_Corruption_Value2 = _Reduce_Corruption_Value;
  _Reduce_Corruption_Value3 = _Reduce_Corruption_Value;
  if ( _City->Body.ID == Civilizations[LOBYTE(_City->Body.CivID)].CapitalID )
  {
    _Reduce_Corruption_Value2 = _Reduce_Corruption_Value + 10;
    _Reduce_Corruption_Value3 = _Reduce_Corruption_Value + 10;
  }
  v13 = class_Leader::f3(&Civilizations[LOBYTE(_City->Body.CivID)]);
  v14 = _Reduce_Corruption_Value2 * BIC_Data.WorldSizes[BIC_Data.Map.World.World_Size].OptimalCityCount;
  v15 = (((BYTE4(v14) & 3) + v14) >> 2) + v13;
  v81 = v15;
  if ( Corruption_Type && (_City->Body.Status & 2) == 2 )
    v81 = BIC_Data.WorldSizes[BIC_Data.Map.World.World_Size].OptimalCityCount / 4 + v15;
  _Max_Distance1 = class_Map::Get_Distance(
                     &BIC_Data.Map,
                     _Capital1->Body.X,
                     _Capital1->Body.Y,
                     _City->Body.X,
                     _City->Body.Y);
  _ImprovementID2 = 0;
  _Distance_Capital = _Max_Distance1;
  if ( BIC_Data.ImprovementsCount > 0 )
  {
    _Cities = Cities.Items;
    _ImprovementType_Offset = 0;
    v78 = 0;
    do
    {
      _Impr_GovID = *(&BIC_Data.Improvements->GovernmentID + _ImprovementType_Offset);
      if ( (_Impr_GovID == _Civ_GovID || _Impr_GovID == -1)
        && *(&BIC_Data.Improvements->SmallWonderFlags + _ImprovementType_Offset) & ITSW_Reduces_Corruption )
      {                                         // Check if Impr is Small Wonder
        if ( (*(&BIC_Data.Improvements->Characterictics + _ImprovementType_Offset) & ITC_Small_Wonder) == ITC_Small_Wonder )
        {
          _CityID1 = Civilizations[LOBYTE(_City->Body.CivID)].Small_Wonders[_ImprovementID2];
          if ( _Cities )
          {
            if ( _CityID1 >= 0 )
            {
              if ( _CityID1 <= Cities.LastIndex )
              {
                v22 = _Cities[_CityID1].Object;
                if ( v22 )
                {
                  _Wonder_City = (v22 - 28);
LABEL_42:
                  if ( _Wonder_City )
                  {
                    if ( _Wonder_City == _City )
                      _Reduce_Corruption_Value3 += 7;// Hard coded value for corruption reduring
                    _Small_Wonder_Distance = class_Map::Get_Distance(
                                               &BIC_Data.Map,
                                               _Wonder_City->Body.X,
                                               _Wonder_City->Body.Y,
                                               _City->Body.X,
                                               _City->Body.Y);
                    _Cities = Cities.Items;
                    if ( _Small_Wonder_Distance < _Max_Distance1 )
                      _Max_Distance1 = _Small_Wonder_Distance;
                  }
                  goto LABEL_47;
                }
              }
            }
          }
        }
        else
        {                                       // Check if Impr is Wonder
          if ( (*(&BIC_Data.Improvements->Characterictics + _ImprovementType_Offset) & ITC_Wonder) == ITC_Wonder )
          {
            _CityID2 = class_Game::Get_Wonder_City_ID(&Game, _ImprovementID2);
            _Cities = Cities.Items;
            if ( Cities.Items )
            {
              if ( _CityID2 >= 0 )
              {
                if ( _CityID2 <= Cities.LastIndex )
                {
                  v25 = Cities.Items[_CityID2].Object;
                  if ( v25 )
                  {
                    _Wonder_City = (v25 - 28);
                    if ( _Wonder_City )
                    {
                      if ( LOBYTE(_Wonder_City->Body.CivID) == LOBYTE(_City->Body.CivID) )
                        goto LABEL_42;
                    }
                  }
                }
              }
            }
          }
        }
      }
LABEL_47:
      ++_ImprovementID2;
      _ImprovementType_Offset = v78 + 272;
      v78 += 272;
    }
    while ( _ImprovementID2 < BIC_Data.ImprovementsCount );
  }
  _Corruption_And_Waste2 = _Corruption_And_Waste;
  v28 = (BIC_Data.Map.Width + BIC_Data.Map.Height) / 4;
  _Reduce_Distance_Value = (BIC_Data.Map.Width + BIC_Data.Map.Height) / 4;
  switch ( _Corruption_And_Waste )
  {
    case CWT_Minimal:
      v28 = 3 * _Max_Distance1;
      goto LABEL_50;
    case CWT_Communal:
LABEL_50:
      _Reduce_Distance_Value = v28 / 4;
      break;
    default:
      break;
    case CWT_Nuisance:
    case CWT_Problematic:
      _Reduce_Distance_Value = _Max_Distance1;
      break;
    case CWT_Rampant:
      _Reduce_Distance_Value = 3 * _Max_Distance1 / 2;
      break;
  }
  _Capital_City_ID = Civilizations[LOBYTE(_City->Body.CivID)].CapitalID;
  if ( _Capital_City_ID == -1
    || (_CivID = LOBYTE(_City->Body.CivID),
        _Capital_City_2 = class_Base_List::Get_Item(&Cities, _Capital_City_ID),
        !class_Trade_Net::Check_City_Connected_To_Target_City(&Trade_Net, _City, _Capital_City_2, _CivID)) )
    _Reduce_Distance_Value = 5 * _Reduce_Distance_Value / 4;
  v33 = (BIC_Data.Map.Height + BIC_Data.Map.Width) / 4;
  if ( v33 >= 2 )
  {
    if ( _Reduce_Distance_Value >= 2 )
    {
      if ( _Reduce_Distance_Value > v33 )
        _Reduce_Distance_Value = (BIC_Data.Map.Height + BIC_Data.Map.Width) / 4;
    }
    else
    {
      _Reduce_Distance_Value = 2;
    }
  }
  else
  {
    _Reduce_Distance_Value = 2;
  }
  if ( Corruption_Type && (_City->Body.Status & 2) == 2 )
    _Reduce_Distance_Value = (_Reduce_Distance_Value + 1) / 2;
  v34 = _Reduce_Corruption_Value3;
  if ( _Reduce_Corruption_Value3 > 0 )
  {
    do
    {
      --v34;
      _Reduce_Distance_Value = (_Reduce_Distance_Value + 1) / 2;
    }
    while ( v34 );
  }
  _Ratio1 = Origin_Value * _Reduce_Distance_Value;
  if ( _Corruption_And_Waste2 == CWT_Communal )
  {
    _Cities_Count = Civilizations[LOBYTE(_City->Body.CivID)].Cities_Count;
    _Ratio2 = (_Cities_Count - HIDWORD(_Cities_Count)) >> 1;
    goto LABEL_141;
  }
  _Ratio2 = 0;
  Corruption_Typea = 0;
  if ( Cities.LastIndex >= 0 )
  {
    while ( 1 )
    {
      if ( !Cities.Items
        || Corruption_Typea < 0
        || Corruption_Typea > Cities.LastIndex
        || (v36 = Cities.Items[Corruption_Typea].Object) == 0
        || (_City2 = (v36 - 28), v36 == 28)
        || LOBYTE(_City2->Body.CivID) != LOBYTE(_City->Body.CivID)
        || _City2 == _City )
        goto LABEL_140;
      v38 = abs(class_Map::Get_DX(&BIC_Data.Map, _Capital1->Body.X, _City2->Body.X));
      v39 = class_Map::Get_DY(&BIC_Data.Map, _Capital1->Body.Y, _City2->Body.Y);
      v40 = v38;
      v41 = abs(v39);
      if ( v38 > v41 || (v40 = v41, v42 = v38, v38 >= v41) )
        v42 = v41;
      v43 = (v38 + v41) / 2 - v42 + 1;
      _Distance_2 = v40 - ((v43 - HIDWORD(v43)) >> 1);
      if ( _Distance_2 < _Distance_Capital )
        break;
      if ( _Distance_2 == _Distance_Capital )
      {
        v45 = _City2->Body.Found_Date.BaseTimeUnit;
        if ( v45 )
        {
          v46 = v45 - 1;
          if ( v46 )
          {
            if ( v46 == 1 )
            {
              v47 = _City2->Body.Found_Date.Year;
              v48 = _City2->Body.Found_Date.Year;
              if ( !v47 )
                v48 = 1;
              v49 = _City;
              v50 = _City->Body.Found_Date.Year;
              v51 = _City->Body.Found_Date.Year;
              if ( !v50 )
                v51 = 1;
              if ( v48 == v51 )
                break;
              if ( !v47 )
                v47 = 1;
              if ( !v50 )
                v50 = 1;
              if ( v47 == v50 && _City2->Body.Found_Date.Week + 1 == _City->Body.Found_Date.Week + 1 )
                break;
            }
            else
            {
              v49 = _City;
            }
          }
          else
          {
            v52 = _City2->Body.Found_Date.Year;
            v53 = _City2->Body.Found_Date.Year;
            if ( !v52 )
              v53 = 1;
            v49 = _City;
            v54 = _City->Body.Found_Date.Year;
            v55 = _City->Body.Found_Date.Year;
            if ( !v54 )
              v55 = 1;
            if ( v53 < v55 )
              break;
            if ( !v52 )
              v52 = 1;
            if ( !v54 )
              v54 = 1;
            if ( v52 == v54 && _City2->Body.Found_Date.Month + 1 < _City->Body.Found_Date.Month + 1 )
              break;
          }
        }
        else
        {
          v56 = _City2->Body.Found_Date.Year;
          if ( !_City2->Body.Found_Date.Year )
            v56 = 1;
          v49 = _City;
          v57 = _City->Body.Found_Date.Year;
          if ( !v57 )
            v57 = 1;
          if ( v56 < v57 )
            break;
        }
        v58 = _City2->Body.Found_Date.BaseTimeUnit;
        if ( v58 )
        {
          v59 = v58 - 1;
          if ( v59 )
          {
            if ( v59 != 1 )
              goto LABEL_140;
            v60 = _City2->Body.Found_Date.Year;
            if ( !_City2->Body.Found_Date.Year )
              v60 = 1;
            v61 = v49->Body.Found_Date.Year;
            if ( !v61 )
              v61 = 1;
            if ( v60 != v61 || _City2->Body.Found_Date.Week + 1 != v49->Body.Found_Date.Week + 1 )
              goto LABEL_140;
          }
          else
          {
            v62 = _City2->Body.Found_Date.Year;
            if ( !_City2->Body.Found_Date.Year )
              v62 = 1;
            v63 = v49->Body.Found_Date.Year;
            if ( !v63 )
              v63 = 1;
            if ( v62 != v63 || _City2->Body.Found_Date.Month + 1 != v49->Body.Found_Date.Month + 1 )
              goto LABEL_140;
          }
        }
        else
        {
          v64 = _City2->Body.Found_Date.Year;
          if ( !_City2->Body.Found_Date.Year )
            v64 = 1;
          v65 = v49->Body.Found_Date.Year;
          if ( !v65 )
            v65 = 1;
          if ( !(v64 == v65) )
            goto LABEL_140;
        }
        if ( _City2->Body.ID < v49->Body.ID )
          break;
      }
LABEL_140:
      ++Corruption_Typea;
      if ( Corruption_Typea > Cities.LastIndex )
        goto LABEL_141;
    }
    ++_Ratio2;
    goto LABEL_140;
  }
LABEL_141:
  v66 = _Ratio2;
  if ( _Ratio2 >= v81 )
    v66 = 2 * _Ratio2 - v81;
  _Citizens_Corruption = 0;
  v68 = 0;
  v69 = (v81 * (BIC_Data.Map.Width + BIC_Data.Map.Height) / 4 / 2
       + v81 * _Ratio1
       + (Origin_Value * v66 + 1) / 2 * (BIC_Data.Map.Width + BIC_Data.Map.Height) / 4)
      / (v81
       * (BIC_Data.Map.Width + BIC_Data.Map.Height)
       / 4);
  v70 = _City->Body.Citizens.LastIndex;
  if ( v70 >= 0 )
  {
    v71 = _City->Body.Citizens.Items;
    do
    {
      if ( v71 )
      {
        if ( v68 >= 0 )
        {
          if ( v68 <= _City->Body.Citizens.LastIndex )
          {
            v72 = v71[v68].Body;
            if ( v72 )
            {
              v73 = (v72 - 28);
              if ( v73 )
              {
                if ( !LOBYTE(v73->TileIndex) )
                  _Citizens_Corruption += BIC_Data.CitizenTypes[v73->WorkerType].Corruption;
              }
            }
          }
        }
      }
      ++v68;
    }
    while ( v68 <= v70 );
  }
  if ( _Citizens_Corruption < v69 )
    v74 = v69 - _Citizens_Corruption;
  else
    v74 = 0;
  _Origin_Value = v74
                * BIC_Data.DifficultyLevels[Civilizations[LOBYTE(_City->Body.CivID)].field_30].Corruption_Level
                / 100;
  _Total_Corruption = Origin_Value * (((9 - _Reduce_Corruption_Value3 < 0) - 1) & (9 - _Reduce_Corruption_Value3));
  if ( _Total_Corruption / 10 < 0 )
    return 0;
  if ( _Origin_Value < 0 )
    return 0;
  result = _Total_Corruption / 10;
  if ( _Origin_Value <= _Total_Corruption / 10 )
    return _Origin_Value;
  return result;
}

It also has a call to function class_Leader::f3(class Leader * this). Here is the code:
Code:
int __thiscall class_Leader::f3(class_Leader *this)
{
  class_Leader *_this; // ebx@1
  int _Optimal_City_Count; // ebp@1
  int _Ratio; // edi@1
  int _Ratio2; // eax@9
  signed int _Ratio3; // ecx@15
  int result; // eax@15

  _this = this;
  _Optimal_City_Count = BIC_Data.WorldSizes[BIC_Data.Map.World.World_Size].OptimalCityCount;
  _Ratio = 3
         * BIC_Data.WorldSizes[BIC_Data.Map.World.World_Size].OptimalCityCount
         * class_Leader::Count_Small_Wonders_With_Flag(this, ITSW_Reduces_Corruption, 0)
         / (BIC_Data.Governments[this->GovenmentType].CurruptionAndWaste != CWT_Communal ? 8 : 1)
         + BIC_Data.WorldSizes[BIC_Data.Map.World.World_Size].OptimalCityCount;
  if ( BIC_Data.Races[_this->RaceID].vtable->CheckBonus(&BIC_Data.Races[_this->RaceID], RB_Commercial) )
    _Ratio += _Optimal_City_Count / 4;
  switch ( BIC_Data.Governments[_this->GovenmentType].CurruptionAndWaste )
  {
    case CWT_Minimal:
    case CWT_Nuisance:
      _Ratio += _Optimal_City_Count / 8;
      break;
    case CWT_Problematic:
      _Ratio += _Optimal_City_Count / 16;
      break;
    case CWT_Communal:
      _Ratio += 2 * _Optimal_City_Count;
      break;
    default:
      break;
  }
  if ( !(Global_Civ_Flags2 & (1 << _this->ID)) )
  {
    if ( Game_Difficulty_Level <= 4 )
    {
      if ( Game_Difficulty_Level <= 3 )
      {
        if ( Game_Difficulty_Level <= 2 )
          goto LABEL_15;
        _Ratio2 = _Optimal_City_Count / 8;
      }
      else
      {
        _Ratio2 = _Optimal_City_Count / 4;
      }
    }
    else
    {
      _Ratio2 = _Optimal_City_Count / 2;
    }
    _Ratio += _Ratio2;
  }
LABEL_15:
  _Ratio3 = _Ratio * BIC_Data.DifficultyLevels[_this->field_30].Optimal_Cities;
  result = 1;
  if ( _Ratio3 / 100 >= 1 )
    result = _Ratio3 / 100;
  return result;
}

What is _this->field_30 I've no idea...

In summary I can tell the only thing: it's really complicated :)

But it can be fixed in very simple way. I can just make it return 0 right at the begining.
 
Antal,

Thanks for putting this code here. I put all of the code for both functions in notepad++ and I'm analyzing it. I actually understand most of it now.

When char Corruption_Type == 0 (to handle commerce corruption calculations)

Can you find any parts of the application that call the Calculate_Corruption method?
Any statement that calls it has to pass in a value for Origin_Value.

If the value is less than or 0, it returns 0.

if ( Origin_Value <= 0 )
return 0;

And if it is greater than 0, it looks at this statement. And if it is true, it just returns whatever was passed in:

if ( Corruption_Type && (this->Body.Status & 1) == 1 )
return _Origin_Value;

// I'm not sure what Body.Status is so I'm not sure when this would occur

<><><>
But now, if the Origin_Value doesn't meet either of the conditions above,
it gets into the meat of the method ... where it tries to reduce corruption
based on whether there are improvements at the current city that reduces
corruption and how far the city is away from the capital based on the optimal
number of cities for the map size and the civilization ID's government type
(things like that).

Anyway, we can revise this section.

We could easily loop through all the improvements and reduce corruption.
The code there is fairly easy to follow:

do
{
if ( class_City::Has_Improvement(_City, _ImprovementID, 1) )
{
if ( !class_City::Check_Improvement_Obsolete(_City, _ImprovementID) )
{
v11 = BIC_Data.Improvements[v10].ImprovementFlags;
if ( BYTE1(v11) & ITF_B1_Reduces_Corruption )
++_Reduce_Corruption_Value;
}

note: Basically this is looping through the list of all improvements at the city, and it's looking to see
if any improvement has a flag set to reduce corruption, and if it does, as long as the improvement
is not obsolete now, it will calculation a total Reduce_Corruption_Value for all improvements...

Anyway, all of that is pretty straight forward...

And after it does those calculations, it tries to do stuff based on the optimal city size.
This is where things get more complex.

example:
v14 = _Reduce_Corruption_Value2 * BIC_Data.WorldSizes[BIC_Data.Map.World.World_Size].OptimalCityCount;

note: I think we could comment this section out or something and not worry about the map size, etc.
and just make our own custom case/switch to reduce corruption further based on the government type.

Anyway, I can analyze everything better if I can see more of the code.
If you put all the code on subversion, I'll bring it down and look at it myself...


<><><>
Anyway, after thinking about this, I don't think I would like the game to return 0
automatically ... because corruption is part of the game and the game would not
be as fun without some form of corruption.

<><><>
This method is obviously not calculating something right though.

I don't think it is returning 0 very often.

It probably isn't reducing the value very often because there seems to be a high level of corruption everywhere.

So the statement that is probably getting fired the most is the one where it returns the Orig_Value
that was passed in ... which is probably some high number.

Thanks,

Gray Wolf
 
if ( Corruption_Type && (this->Body.Status & 1) == 1 )
return _Origin_Value;

// I'm not sure what Body.Status is so I'm not sure when this would occur

this->Body.Status represents City Status. It's a flag set 32-bit value.
Here are the flags, I've found out

0x01 - Civil Disorder
0x04 - Airlift Used
0x10 - Hurry Production Made
0x20 (Capitalization ???)

So that code part means: if City has Civil Disorders, corruption = origin value.
 
Can you find any parts of the application that call the Calculate_Corruption method?
Any statement that calls it has to pass in a value for Origin_Value.

class_City::Calculate_Corruption has address: 0x004B1220

Here are cross references (in IDA it's called xrefs):
1)
function: int __thiscall class_City::Calculate_Total_Production(class_City *this)
function address: 0x004B0660
call instruction address: 0x004B0670

Here is a part of the code, where the considering function is called
Code:
  _this = this;
  _Wastes = class_City::Calculate_Corruption(this, this->Body.Tiles_Production, 1);
  _Tiles_Production = _this->Body.Tiles_Production;
  _this->Body.ProductionLoss = _Wastes;
  v4 = 0;
  _this->Body.ProductionIncome = _Tiles_Production - _Wastes;

3-rd parameter (1) means, that the function should calculate production wastes instead of commerce corruption

2)
function: int __thiscall class_City::Calculate_Total_Commerce(class_City *this)
function address: 0x004B0850
call instruction address: 0x004B089D

Code:
  _City = this;
  _Improvement_Commerce = 0;
  _ImprovementID = 0;
  for ( _Tiles_Commerce = this->Body.Tiles_Commerce; _ImprovementID < BIC_Data.ImprovementsCount; ++_ImprovementID )
  {
    if ( class_City::Has_Improvement(_City, _ImprovementID, 1) )
      _Improvement_Commerce += class_City::Get_Improvement_Turism_Income(_City, _ImprovementID);
  }
  _Total_Commerce_1 = _Tiles_Commerce + _Improvement_Commerce;
  _Corruption = class_City::Calculate_Corruption(_City, _Total_Commerce_1, 0);
  _City->Body.Corruption = _Corruption;
  _Remain_Budget = _Total_Commerce_1 - _Corruption;
  _CivID = _City->Body.CivID;
  _City->Body.CashIncome = _Remain_Budget;

3-rd parameter (0) means commerce corruption type.

Both of this 2 functions are called in many places. One of the calls is happening when the user clicks on a tile in City Form with setting/unsetting it's worker.
 
Anyway, I can analyze everything better if I can see more of the code.
If you put all the code on subversion, I'll bring it down and look at it myself...

And that's the problem. There is no code itself. IDA has its own database to store information about functions, names, defined structures, processed variables' types and many other, but not the code. I suppose it contains only asembler-code.
I obtain C++ code in runtime when I locate a function and use Hex-Rays plugin and it's main feature of decompiling.

Using stored data decompiler restores C++ code from asm commands as accurate as it can.

It also has a feature to export the code into C-file. It's here: Civ3Conquests_Src_Export.zip

It's the only thing I can obtain globally.
 

Attachments

  • Civ3Conquests_Src_Export.zip
    2.4 MB · Views: 171
And that's the problem. There is no code itself. IDA has its own database to store information about functions, names, defined structures, processed variables' types and many other, but not the code. I suppose it contains only asembler-code. I obtain C++ code in runtime when I locate a function and use Hex-Rays plugin and it's main feature of decompiling.

Ok, wow. It's pretty complicated when you can't see all the code. :scan:

I downloaded the code file you generated and attached. It's pretty complex. That would take
forever to figure out.

I wish it was all in Visual Studio as a C# solution. Then I know I could help.

Thanks for explaining all of that.

And the only easy corruption fix, like you said, is returning 0 all of the time, but I think it's probably best to leave corruption alone
and let it do whatever it is currently doing.
 
And after it does those calculations, it tries to do stuff based on the optimal city size.
This is where things get more complex.

Related function class_Leader::f3 is not better. It's a hell of a mess too.

I met a lot of moments, in which developers just wrote some instead of normal c++ code.

First of all, there are a lot of self-repeatings. DRY concept was not for them.
Second, none of professional developer writes such huge methods like this and I met much more large function like the function, which renders City Form.
And third they used a lot of preprocessor directives which actually transorms in a lot of repeating code fragments.
 
It's pretty complicated when you can't see all the code.
I don't even know how much function were extracted. The result file is huge (its about 15MB).

IDA says there are 10594 methods. I could determine and name about 2350 of them.

Header file is somehow very small. I've created and defined about 300 different structures and enumerations.

I wish it was all in Visual Studio as a C# solution.

Me too :) I also design the trainer in C#.

And the only easy corruption fix, like you said, is returning 0 all of the time, but I think it's probably best to leave corruption alone
and let it do whatever it is currently doing.

I agree.
I haven't touched that method yet. But I've set "Reduce Corruption" flag to all improvements in scenario and now I've got no corruption.
 
I haven't touched that method yet. But I've set "Reduce Corruption" flag to all improvements in scenario and now I've got no corruption.

That is pretty much the approach that I have taken, although not with every improvement. The other factor is making each city as productive as possible, without cramming them in too tightly, so as to keep the number of cities down below the optimum. I should say that I have also boosted resource yields so that highly productive cities are possible, especially if on next to a coast tile or two, along with increasing the town population level to 9 and the city population level to 21 This allows for every tile to be worked within the town and city limits, with an extra citizen to do whatever needs to be done.
 
Top Bottom