Civ1 explained: city disasters

darkpanda

Dark Prince
Joined
Oct 28, 2007
Messages
823
Mize hijacked my interest into the CIV code by asking what's the criteria to trigger an earthquake in a city ? (see here: http://forums.civfanatics.com/showthread.php?p=12718199&post12718199).

Especially, I wanted to know how terrain can affect the occurrence of a city disaster. So I searched a little bit a found the CIV code that handles them, and although there is randomness to it, we can see that terrain does play a role:

City disaster logic

The game logic below is performed TWICE per Civ turn:
  • Select a random city ID between 0 and 127
  • If the corresponding city does not exist -> finish and loop (this counts as an iteration)
  • Else, if the City's owning Civ has only 1 city in total -> finish and loop (this counts as an iteration)
  • Else, if the City size is strictly smaller than 5 -> finish and loop (this counts as an iteration)
  • Else, select a random value between 0 and 9 and apply the corresponding rules below:
    • 0 = EARTHQUAKE
      • If City has nearby Hills AND City has buildings other than Palace
      • -> EARTHQUAKE; destroys a random building (except for Palace)
    • 1 = PLAGUE
      • If Civ owning the City does NOT know Medicine AND City has NO Aqueduct AND Civ knows Construction (and City size is not 0, obviously...)
      • -> PLAGUE; reduces City Size by 25% (CitySize-CitySize/4)
    • 2 = FLOODING
      • If City has RIVER nearby AND City has NO City Walls AND Civ knows Masonry AND City Size strictly above 1
      • -> FLOODING; reduces City Size by 25% (CitySize-CitySize/4)
    • 3 = VOLCANO
      • If City has nearby MOUNTAIN AND City has NO Temple AND Civ knows Ceremonial Burial AND City Size strictly above 1
      • -> VOLCANO; reduces City Size by 33% (CitySize-CitySize/3)
    • 4 = FAMINE
      • If City size larger than 1 AND City has NO Granary AND Civ knows Pottery
      • -> FAMINE; reduces City Size by 33% (CitySize-CitySize/3)
    • 5 = FIRE
      • If City has buildings (other than Palace) AND City has NO Aqueduct AND Civ knows Construction
      • -> FIRE; destroys a random building (except for Palace)
    • 6 = PIRATES
      • If City has nearby OCEAN AND City has NO Barracks
      • -> PIRATES; plunder your FOOD and SHIELD stocks (reset to 0)
    • 7, 8 or 9 = RIOT/SCANDAL/CORRUPTION
      • the business rules in this case are much more complex: they involves citizen demands for Temple, Courthouse, Marketplace or Cathedral, suggestion to lower taxes, and can even lead to a City subverting to another Civ, or declaring its own independence... I'll detail them at a later time
  • Finish and loop

I don't know if any of this was described in the CIV manual, in "Rome on 640K a day" or other official Civ documentation, but wasn't totally aware of those rules, and I must say I like the idea of playing with such thoughts: "if we settle near the sea, we are at the mercy of pirates, but near the river, we may endure floodings".

I tried to hack Civ to increase their frequency (change the "2" iterations to "256"), but it seems that there are so many conditions for an event to happen that it had no impact in my sample test game...
 
Clear and to-the-point, as always, darkpanda! Good job. :goodjob:

In my gameplay experience (I'd say not only mine), disasters always played very minor (if any) role; e.g. I played for years without even realizing/caring that Medicine averts plague. Also, though I knew that barracks could avert pirates plundering a city, the price of keeping barracks is imho higher than the cost of encountering pirates, hence I didn't care either. And that is a pity, actually - disasters could spice the gameplay up quite a bit.

What could be interesting (though practically unimaginable to hack into original civ.exe without major hassle, and I'm not teasing you darkpanda) - an earthquake with an epicenter and much more serious consequences, i.e. destroying not one building in one city, but say 2/3 buildings in city near epicentre, going to 1/5 in cities further away (radius up to say 15), inverse square proportional. Somehow correlate the probability of occurrence with say shields production in that area ("too much mining destabilizes the bedrock"). And courthouses could (dramatically) reduce the probability of/damage caused by such an earthquake; it would make them valuable (along the lines "mining office regulates wild mining"/"organized rescue efforts reduces damage").

Also, the plague could/should last more than one turn + attacking with catapult should increase the possibility of infecting the city (as was common in medieval times - to catapult feces into city e.g.). On the other hand, an infected city could destroy nearby units (with small, but perceivable probability - say 1 in ten per one round) just by infecting them back (spreading infection by air-flies-mosquitoes).

Perhaps hijacking the thread a little bit, but hopefuly on topic.

Anyhow, again, nice work.
 
Seems odd there would be a second set of city size checks after the global condition that they must be greater than 5. It looks like they added in that condition later, probably during testing, due to overzealous decimation of cities, while not bothering to remove the original checks. Maybe despite their best efforts they somehow still ran into divide by zero conditions, which would lead to them choosing 5 in particular since quartering is involved in some of the calculations.

Are there transcription errors for the final three population reduction scenarios? As written, they would reduce population by 75%, 67%, and 67%.
 
Any chances to get the hex-offsets where this formula is defined? I don't know better way to teach myself assembly than trying to hack CivDOS to behave some strange way.
 
Seems odd there would be a second set of city size checks after the global condition that they must be greater than 5. It looks like they added in that condition later, probably during testing, due to overzealous decimation of cities, while not bothering to remove the original checks. Maybe despite their best efforts they somehow still ran into divide by zero conditions, which would lead to them choosing 5 in particular since quartering is involved in some of the calculations.

I agree... But to be honest, browsing CIV code, you can find much evidence of how the code was thought and written, and sometimes rewritten, leaving awkward, convoluted code structures. This is especially true in many places where an absolute value is computed, for a value that can only be positive to begin with...

Are there transcription errors for the final three population reduction scenarios? As written, they would reduce population by 75%, 67%, and 67%.

Indeed: two pairs of eyes are better than one! I corrected the original descriptions.
 
Any chances to get the hex-offsets where this formula is defined? I don't know better way to teach myself assembly than trying to hack CivDOS to behave some strange way.

Well, this whole piece of code takes many bytes, so I am not sure what offsets you would like exactly...
The "2" iterations limit is available as below (in CIV.EXE version EN 47401):

Code:
00002610h: 7E 52 C7 46 FA 00 00 EB 0E FF 76 FA CD 3F 14 3D ;
00002620h: 05 83 C4 02 FF 46 FA 83 7E FA [color=blue][B]02[/B][/color] 7D 37 B8 80 00 ;
00002630h: 50 9A 5B 00 DE 1D 83 C4 02 89 46 FA B8 1C 00 F7 ;

If you're serious about starting the CIV code assembly adventure, I've been thinking of writing a kind of "hands-on" tutorial to look at it with IDA, but I'm not sure it's worth the effort.
 
I tried to hack Civ to increase their frequency (change the "2" iterations to "256"), but it seems that there are so many conditions for an event to happen that it had no impact in my sample test game...
Well I changed the iteration limit to 127 and changed that city must be at least size 1 (which it always is of course) instead of 5 and famines started coming almost every turn. Maybe one could create sort of disaster/armageddon mod to make player angry.:lol:

e: The game with more frequent disaster is very interesting to play actually.
-You want to avoid founding next to hills, since earthquakes can't be prevented by building.
-Early game coastal cities are risky/costly, since they need barracks.
-Granaries are actually useful

I think I really like this hack.

If you're serious about starting the CIV code assembly adventure, I've been thinking of writing a kind of "hands-on" tutorial to look at it with IDA, but I'm not sure it's worth the effort.
I'm not too serious about it, but of course if you shared more about your knowledge about civ.exe,well... at least it would be interesting.
 
This is awesome, darkpanda! Thanks for being so easy to hijack.

When I first fully understood Civ's land creation algorhytms that you supplied, I thought that disaster events were going to be more complex, like maybe using some of the rainfall/wetness calculations for probability of flooding or famine (drought). Or maybe earthquakes would be more probable at the edges of the world's tectonic plates.

Instead, they opted for the much simpler and actually very clever way to hinder players by not only making most of the disasters avoidable, but also making them possible only when you have the technology to actually stop them. And this figures - people living in huts on wooden poles in the jungle around rivers have nothing to fear, but the moment they start building permanent structures, any flood can wash decades of hard work away. So if you commit to masonry, you really have to commit if the conditions are as unforgiving. It seems to me that Civ was originally planned on a much larger scale (tha basic example - population counts in civs and cities) but was shrunk during development, because it would be too cumbersome and, probably, boring. But imagine that you have ten cities along a river and the probability is hacked to be higher (either through more loops per turn or lowering the pop bar). That way flooding would be a constant problem in at least part of your empire.

Anyway, great work! Hope you find out what exactly causes corruption, scandal and riot soon. :rolleyes:
 
Stupid Pottery... it's good only for making famine! It's more worthless than Horseback Riding! (actually I do like it if I capture Hanging Gardens from someone, but if only I didn't have to capture their tech too..)

In that case it would be "Scientists discover Pottery... Scientists discover Future Tech!" ;)

As always it's thoroughly a pleasure to read the work you are doing darkpanda
 
While checking up on this thread again, I remembered something we discussed here a while ago in another topic - http://forums.civfanatics.com/showthread.php?t=457140

Sometimes disasters will wipe out more than one improvement in a city. In my and Valen's case, they were also improvements of the same 'class' (buildings related to shield production).
 
While checking up on this thread again, I remembered something we discussed here a while ago in another topic - http://forums.civfanatics.com/showthread.php?t=457140

Sometimes disasters will wipe out more than one improvement in a city. In my and Valen's case, they were also improvements of the same 'class' (buildings related to shield production).

Very interesting!

It got me really intrigued so I delved back into the detailed instructions associated to an earthquake, and now I can tell you what's happening.

First, just a reminder of the descibred behaviour: when an earthquake occurs and destroys a building (in the example given, it is the Manufacturing Plant), then other buildings may be destroyed as well.

The short story is that, indeed, if CIV selects (randomly) your Mfg Plant to be destroyed by an earthquake, then the following buildings will be wiped out as well, if the city has them: SDI Defense, Recycling Center, Power Plant, Hydro Plant and Nuclear Plant.
Note that:
  • this only occurs if the randomly building is your Mfg Plant
  • if CIV randomly selects any of SDI Defense, Recycling Center, Power Plant, Hydro Plant and Nuclear Plant instead, then actually no building is destroyed at all

The long story is that in the CIV assembly, there seems to be a wrong handling of the city's bytes representing the buildings... It is not very straighforward to explain, but I'll try anyway:
  • CIV is basically a 16-bit program, meaning that the CPU registers are 16-bit wide, and the biggest data element readable from memory is also 16-bit wide; in fact, most data accesses are done by reading 16 bits from memory (rather than 8 bits); those data elements are usually called words, or short integers, etc.
  • As we have seen from the SVE file format analysis, the buildings that a city contains are encoded using 4 bytes
  • In those 4 bytes, each building is represented by 1 bit, set to 1 if the building exists, and to 0 if not; in effect, since there are only 21 buildable buildings, only 21 bits are used among the totally 32 available bits
  • The way CIV handles those 4 bytes is by reading them as 2 separate words (2 * 16-bit values)
  • When CIV decided that an earthquake should occur, it will first randomly select a building to destroy, by randomly selecting a building bit ID between 0 and 23 included; this is already a first -yet impactless- issue, since the highest possible bit ID is 20; 21, 22 and 23 code for ship parts which are not actual city improvements
  • If the city possesses the building that matches the random bit ID, then CIV proceeds to destroy it and popup the message to the player
  • In order to "destroy" a building, what CIV does is basically set its bit to 0, period.
  • Now here's the catch: we've seen that CIV only manipulates buildings data as 16-bit words, so it has to take into account whether the random bit ID was below 16, in which the case the bit to set to 0 is in the first word, or above or equal to 16, in which case the bit to set to 0 in the second word
  • This is where CIV kind of fails: instead of cautiously handling those 2 situations, CIV makes an abusive usage of the instruction CWD:
    • a quick word on CWD: the "CWD" instruction stands for "Convert Word to Double", i.e. convert 16-bit value to 32-bit value
    • to do so, since 16-bit x86 doesn't have 32-bit registers, the CWD instruction "sign extends" the 16-bit register AX into the 16-bit register DX:
    • if the most significant bit of AX is 1 (bit ID 15), then DX is set to value "0xFFFF", i.e. full of "1"s
    • if the most significant bit of AX is 0 (bit ID 15), then DX is set to value "0x0000"
  • Hereunder is a raw copy and paste of the CIV assembly that performs the "destroy building" operation:
    Code:
    0    ...
    1    mov     ax, 1
    2    mov     cl, byte ptr [bp+var_buildingID]
    3    shl     ax, cl          ; Shift Logical Left
    4    not     ax              ; One's Complement Negation
    5    cwd                     ; AX -> DX:AX (with sign)
    6    and     word ptr CityData.Buildings0[bx], ax ; Logical AND
    7    and     word ptr CityData.Buildings2[bx], dx ; Logical AND
    8    ...
  • Instructions 1, 2 and 3 correspond to the C-like code "AX = 0x1 << buildingID", i.e. AX now contains 0 everywhere, except at the bit with position "buildingID", whose value is 1; we can already see here that if buildingID is equal or above 16, the "1" bit is lost, since AX register is only 16-bit wide
  • From instructions 6 and 7, we guess that CIV expects AX register to contain the building bitmask for all buildings with ID below 16, while DX register contains the building bitmask for IDs above and equal to 16
  • Instruction 4 makes a binary negation of ax: all 0's become 1's and the single 1 becomes a 0; this effectively creates a binary mask in AX, for further usage in instruction 6 to keep all bits below 16 unchanged EXCEPT for the bit at position buildingID, which becomes 0, thus destroying the desired buliding
  • Now look at instruction 5: it does a signed extension of AX into DX... If the building bit ID is below or equas to 14, then the bit 15 of AX will remain 0 after instruction 3, then become 1 after instruction 4, then DX will become 0xFFFF and act as a "keep-all" bitmask for buildgs with IDs above 15
  • But what happens if the random bit ID is 15 or above ?
  • If it is 15, the value of AX at instruction 3 will then be "0x8000", or in binary form "0b10000000 00000000"
  • Negating this value, AX becomes "0b01111111 11111111", i.e. a bitmask to keep all buildings except for the build with ID 15
  • On sign extension, since the high order bit of AX is 0, then all bits of DX become 0, i.e. DX = 0b00000000 00000000
  • Consequently, instruction 6 will properly remove the building with ID 15, but instruction 7 will remove all buildings with ID >15!
  • If the bit ID is 16 or more, then AX reaming 0x0 after line 3, then 0xFFFF after line 4, and DX is 0xFFFF as well, thus keeping all buildings
  • To conclude, as you guessed it, the building with ID 15 is the Manufacturing Plant

I can't say for sure why it was done like this, but my guess is that there was an overlook when using C-level data conversion code of the 16-bit words to 32-bit integers, and/or in the handling of such integers... It could even be a compiler issue, not necessarily a programmer's issue, but what's for sure is that this went through testing uncaught.
I guess we'll never know, except that it's one more bug excavated...
 
Anyway, great work! Hope you find out what exactly causes corruption, scandal and riot soon. :rolleyes:
Sure I could! But I just stopped deciphering the code when I was done with all 9 possibilities... It is not dependent on neighbouring terrain, that is for sure. I'll come back to it at a later date.

I am very curious about it :)

Well then here it is! :)

As said before, if the random "event" value is 7, 8 or 9, CIV executes a "discontent" related disaster, with the following logic:
  • First it checks if the selected City has more unhappy citizens (black) than happy citizens (white); if not, the routine terminates here
  • Else, it selects a random cause for the city riot among "riot", "corruption" or "scandal"; this is really for game experience only, as it doesn't impact the rest
  • Then it selects what the unhappy citizens' demands will be:
    • A Temple, if the city does not have one yet
    • Else a Courthouse, if the city does not have on yet
    • Else a Marketplace, if the city does not have on yet
    • Else a Cathedral, if the city does not have on yet
    • Else -and finally- lower taxes :)
  • Next CIV depletes food and shield stocks to 0
  • The next and final step is a more complex one, that will check whether the conditions are met for the city to self-subvert to another Civ:
    • First, the city should not be the Civ's capital
    • Second, the city's owning Civ should have strictly more than 3 cities totally (4 or more)
    • Third, CIV loops among all Cities that have a lower ID (basically, cities that were built earlier) up to the first one that does not exist (or, incidentally, that was destroyed, but I believe this to be an undesired side-effect...), and checks the following:
      • Compute the city's appeal with the formula: appeal = (iterated city's happy - unhappy people) * 32 / (distance from rioting city to iterated city); I know that Gowron already described the distance formula but I am too lazy to dig it out right now...
      • If this appeal is strictly above 4 AND above any other city's appeal, then mark this city as admired
    • Fourth and finally: if there is an admired city, and it's owning Civ is different from the rioting city's owning Civ, then the rioting city subverts to the admired city's Civ: "Residents of <rioting city> admire the prosperity of <admired city>" -> <admired civ> capture <rioting city>...

Personally, I find the "city subversion" code above too restrictive... I am very keen on hacking it to increase the likelihood of cities subverting to another Civ, as I would love to be able to conduct a "Cultural conquest" win in CIV 1 :)
 
I already experienced such a subvertion in Civ 1 some years ago, an AI city subverted to my side. I was really surprised. Now I know, why the city did this (resp. if I figure out your formula I will :D).

Thank you darkpanda :)
 
I don't understand some things:
Well then here it is! :)

As said before, if the random "event" value is 7, 8 or 9, CIV executes a "discontent" related disaster, with the following logic:
[...]
[*] Else, it selects a random cause for the city riot among "riot", "corruption" or "scandal"; this is really for game experience only, as it doesn't impact the rest
Why CIV decides again about the cause for the city riot? Is not the value 7, 8 or 9 decisive for the choice?
And: Haven't 7, 8 and 9 the same probability as the other numbers? So the likelihood to get 7, 8 or 9 is roughly 1/3??

[*] Then it selects what the unhappy citizens' demands will be:
  • A Temple, if the city does not have one yet
  • Else a Courthouse, if the city does not have on yet
  • Else a Marketplace, if the city does not have on yet
  • Else a Cathedral, if the city does not have on yet
  • Else -and finally- lower taxes :)
This is just to give the official reason for the riot, right? For example you can not prevent a riot, if you build a temple (unless its makes enough people content)?
[*] Third, CIV loops among all Cities that have a lower ID (basically, cities that were built earlier) up to the first one that does not exist (or, incidentally, that was destroyed, but I believe this to be an undesired side-effect...), and checks the following:
  • Compute the city's appeal with the formula: appeal = (iterated city's happy - unhappy people) * 32 / (distance from rioting city to iterated city); I know that Gowron already described the distance formula but I am too lazy to dig it out right now...

  • I don´t understand: How CIV is able to compute any appeal of a city, which doesn't exist? Or takes CIV the last existing city before/after this?
    What is, if there is no none-existing city with a lower ID?
 
Why CIV decides again about the cause for the city riot? Is not the value 7, 8 or 9 decisive for the choice?
And: Haven't 7, 8 and 9 the same probability as the other numbers? So the likelihood to get 7, 8 or 9 is roughly 1/3??

Basically you're right: 7, 8 and 9 have the same probability as 0, 1, 2, ... 6. So to be mathematically exact, the likelihood to go down the "unhappiness" route is 3/10, which is slightly lower than 1/3 ...

As to why CIV decides again instead of just re-using the original variable, you should ask those Civilized Guys at MPS! It's them who wrote the code, I am just the messenger :)

This is just to give the official reason for the riot, right? For example you can not prevent a riot, if you build a temple (unless its makes enough people content)?

Indeed: the first criteria is to check that unhappy citizens outnumber happy citizens, which is the basic logic for any city to fall into civil disorder... Only in this case is there any chance for a city to be subject to this city "disaster" logic. In a way, you can see it as "possible disaster in case a city is already in riot".

I don´t understand: How CIV is able to compute any appeal of a city, which doesn't exist? Or takes CIV the last existing city before/after this?
What is, if there is no none-existing city with a lower ID?

You should read "up to and excluding the first non-existing city".
To understand this, you need to know a little bit a about city logic in CIV:
  • in memory (and this can be seen in SVE files), there are 128 available data "slots" for Cities
  • those slots are allocated to newly created cities starting from slot with ID up to slot with ID 127.
  • when a city is destroyed, its slot is "de-allocated" and -possibly- a new city can re-use it (not sure about the exact logic for re-using slots though...)
  • what the loop above does is iterate over city slots starting from slot 0; then you have a loop-ending condition which is: (1) iterated city same as rioting city OR (2) iterated city does not exist OR (3) reached slot 127
    • part (1) of the condition effectively means that when reaching the current city, the loop ends; so this is the same as saying the the loop iterates only on cities with a strictly lower ID
    • part (2) of the condition means that if CIV stumbles on an un-allocated city slot (technically: city status = 0xFF), then the loop ends; this is the same as saying that the loop iterates up to the first non-existing city
    • part (3) is the obvious condition to end the loop when all possible cities have been iterated over

I hope this clarifies the matter... Again you must understand that my process is to reverse-engineer CIV's assembly code, so in general all I am doing in translating code into english words for everyone to understand. But since I don't always understand the logic that was in the programmer's minds, this translation sometimes remains very low-level in terms of algorithmics...
 
Again, awesome work, darkpanda! So, what I get from your (very thorough) explanations is, that in the case of an earthquake/fire, civ basically regards the structures with ID => as the end of the chalkboard and does a clean sweep of everything instead of carefully erasing one of them. Weirdly overlooked bug/programming error.

But I think you might be missing something from the formula for unhappiness related disasters though. I've gotten improvements destroyed during riots. I remember a particular case of a nuclear plant that blew up just as one of my cities revolted and I got the 'riot' message. Maybe it is a logic exclusive fore nuke plants, or just a coincidence. Unfortunately I can't remember if I've ever gotten another improvement, let's say a factory, knocked down during a riot.
 
Top Bottom