darkpanda
Dark Prince
- Joined
- Oct 28, 2007
- Messages
- 844
Although I never put it at the center of my focus, stories about the "pollution bug" have been kept roaming at the back of my mind ever since I became active on those forums, and read people mentioning it here and there.
Being more or less done with the City Process routine (I'm not done yet, in fact, but a bi tired
), I thought it would be more fun to investigate this so-called pollution bug one way or another.
So, first thing, I looked up the forums for related threads, and here is what I found:
Some of the above links contain detailed game logic, extracted from "Rome on 640K a Day" regarding pollution, namely how known Techs influence pollution, how a city's power source influence chimneys, how population influence pollution, as well as shields production (or did it?).
Anyhow, I had already identified a very small routine in CIV.EXE, that I bluntly called "AddPollution(x, y)". It seems this routine is used anytime pollution should be added to the map (nuke, city process, ...).
Back-tracking the call from the City Process routine, I could finally dig out what I believe to be the cause of this famous "bug".
Here the raw piece of assembly, for the die-hards out there:
This code block is one of the first condition to be assessed when checking whether pollution should be spawned.
Re-writing the condition in a single formula, it gives this:
IF ( 2 * CityPollution > Random(256 - CityOwnerTechCount * difficultyLevel / 2) ) THEN AddPollution
In the above, CityPollution is computed as explained in other threads:
CityPollution = CityShields / CityPowerType - 20 + CitySize * PollutionFactor
CityPowerType depends on the power infrastructure in the city (default/power plant = 1, hydro/nuke plant = 2, recycling center = 3)
PollutionFactor depends on the "polluting" techs known by the player (INDUSTRIALIZATION=1, AUTOMOBILE=2, MASS PRODUCTION=3 and PLASTICS=4), and whether the city has a MASS TRANSIT (= 0).
So we can see that multiple combination are possible, but the key to this bug lies in the RANDOM() call: what if CityOwnerTechCount * difficultyLevel / 2 is more than 256?
Then the RANDOM() call has a negative argument, for which it will always return zero.
In other words, as soon as one gets (256*2/difficultyLevel) Techs, pollution starts to get triggered at every turn.
Let's make a list:
The theoretical numbers seem to more or less match what people have reported in the various threads above, although some other unknown conditions may add variations.
Anyhow, for me, the riddle is solved.
The riddle in the riddle that isn't solved, though, is: was this done on purpose? I guess we'll never know...
Now, how would you guys out there suggest to patch it?
Being more or less done with the City Process routine (I'm not done yet, in fact, but a bi tired

So, first thing, I looked up the forums for related threads, and here is what I found:
- Pollution bug - Jan 2003
- Pollution - Jan 2003
- Did anyone try to remove the pollution? - Jun 2008
- Pollution "Bug" - Nov 2007
- Weird pollution bug? - Dec 2011
Some of the above links contain detailed game logic, extracted from "Rome on 640K a Day" regarding pollution, namely how known Techs influence pollution, how a city's power source influence chimneys, how population influence pollution, as well as shields production (or did it?).
Anyhow, I had already identified a very small routine in CIV.EXE, that I bluntly called "AddPollution(x, y)". It seems this routine is used anytime pollution should be added to the map (nuke, city process, ...).
Back-tracking the call from the City Process routine, I could finally dig out what I believe to be the cause of this famous "bug".
Here the raw piece of assembly, for the die-hards out there:
Spoiler :
Code:
seg007:633F
seg007:633F seg007_633F: ; CODE XREF: cityProcess_cityID_mode_?1_?2_?3+62F7j
seg007:633F 10C mov ax, cityShieldsProd_dseg_705C
seg007:6342 10C cwd ; AX -> DX:AX (with sign)
seg007:6343 10C mov cx, dseg_6C18_CityPowerType
seg007:6347 10C idiv cx ; Signed Divide
seg007:6349 10C sub ax, 14h ; Integer Subtraction
seg007:634C 10C mov [bp+var_pollutionProd], ax
seg007:6350 10C mov ax, 1Ch
seg007:6353 10C imul [bp+arg_cityID] ; Signed Multiply
seg007:6356 10C mov bx, ax
seg007:6358 10C mov al, CityData.ActualSize[bx]
seg007:635C 10C cbw ; AL -> AX (with sign)
seg007:635D 10C imul pollutionFactor_dseg_C7A2 ; Signed Multiply
seg007:6361 10C cwd ; AX -> DX:AX (with sign)
seg007:6362 10C xor ax, dx ; Logical Exclusive OR
seg007:6364 10C sub ax, dx ; Integer Subtraction
seg007:6366 10C mov cx, 2
seg007:6369 10C sar ax, cl ; Shift Arithmetic Right
seg007:636B 10C xor ax, dx ; Logical Exclusive OR
seg007:636D 10C sub ax, dx ; Integer Subtraction
seg007:636F 10C add [bp+var_pollutionProd], ax ; Add
seg007:6373 10C mov bx, cityOwner
seg007:6377 10C shl bx, 1 ; Shift Logical Left
seg007:6379 10C mov ax, perCivTechCount[bx]
seg007:637D 10C imul difficultyLevel ; Signed Multiply
seg007:6381 10C cwd ; AX -> DX:AX (with sign)
seg007:6382 10C sub ax, dx ; Integer Subtraction
seg007:6384 10C sar ax, 1 ; Shift Arithmetic Right
seg007:6386 10C sub ax, 100h ; Integer Subtraction
seg007:6389 10C neg ax ; Two's Complement Negation
seg007:638B 10C push ax
seg007:638C 10E call random_N ; Call Procedure
seg007:638C
seg007:6391 10E add sp, 2 ; Add
seg007:6394 10C mov cx, [bp+var_pollutionProd]
seg007:6398 10C shl cx, 1 ; Shift Logical Left
seg007:639A 10C cmp cx, ax ; Compare Two Operands
seg007:639C 10C jg short seg007_63A1 ;
This code block is one of the first condition to be assessed when checking whether pollution should be spawned.
Re-writing the condition in a single formula, it gives this:
IF ( 2 * CityPollution > Random(256 - CityOwnerTechCount * difficultyLevel / 2) ) THEN AddPollution
In the above, CityPollution is computed as explained in other threads:
CityPollution = CityShields / CityPowerType - 20 + CitySize * PollutionFactor
CityPowerType depends on the power infrastructure in the city (default/power plant = 1, hydro/nuke plant = 2, recycling center = 3)
PollutionFactor depends on the "polluting" techs known by the player (INDUSTRIALIZATION=1, AUTOMOBILE=2, MASS PRODUCTION=3 and PLASTICS=4), and whether the city has a MASS TRANSIT (= 0).
So we can see that multiple combination are possible, but the key to this bug lies in the RANDOM() call: what if CityOwnerTechCount * difficultyLevel / 2 is more than 256?
Then the RANDOM() call has a negative argument, for which it will always return zero.
In other words, as soon as one gets (256*2/difficultyLevel) Techs, pollution starts to get triggered at every turn.
Let's make a list:
- Chieftain = 0: (256*2/0) = +infinity -> pollution bug never triggered
- Warlord = 1: (256*2/1) = 512 -> pollution bug triggered after Future Tech. 444 (512 - 68 non-future techs)
- Prince = 2: (256*2/2) = 256 -> pollution bug triggered after Future Tech. 188 (256 - 68 non-future techs)
- King = 3: (256*2/3) = 170 -> pollution bug triggered after Future Tech. 102 (170 - 68 non-future techs)
- Emperor = 4: (256*2/4) = 128 -> pollution bug triggered after Future Tech. 60 (128 - 68 non-future techs)
The theoretical numbers seem to more or less match what people have reported in the various threads above, although some other unknown conditions may add variations.
Anyhow, for me, the riddle is solved.
The riddle in the riddle that isn't solved, though, is: was this done on purpose? I guess we'll never know...

Now, how would you guys out there suggest to patch it?