Modding Civilization I - Patterns for Huts and Special Resources

Gowron

Chieftain
Joined
May 21, 2007
Messages
62
Modifying the map is fun, but it's even more fun if you can also alter the patterns which govern the placement of huts and special resources in the map :)

The following article contains code and data addresses, both of which will only be correct for the uncompressed civ.exe version 1, as described here.


The Special Resources Pattern

As many players will have noticed, the special resources patterns on different maps look similar and often are the same. The pattern is determined by a special number, the so-called resource seed. Depending on the seed, there are 16 different possible patterns.
The resource seed of a running game can be found inside the RAM at position 0x40FB0.

But there's more ingredients. Each square has a horizontal index, the x coordinate, and a vertical index, the y coordinate. Both are used to determine if a square will contain a special resource or not.

And finally, there's a complex formula which reads the seed and the coordinates and tells the game whether a given square is a special resource square or not. Since it's not easy to rewrite the whole formula, I'll propose a relatively easy way to do limited changes by changing two constants inside the formula. This only requires a hex editor.

The first constant is 13 by default (0x0D in hex code) and can be found at position 0x1F016. The second constant, 11 by default (0x0B) is found at position 0x1F024. Changing the constants will change the look of the special resources pattern. The change may be more or less distinct, depending on which values you choose.

Of course, you can also go ahead and rewrite the whole formula, but be aware that some basic knowledge of the x86 assembler language is required.

Here's the relevant code chunk:
Code:
seg012:1828                 push    bp                          
seg012:1829                 mov     bp, sp
seg012:182B                 sub     sp, 2
seg012:182E                 cmp     [bp+arg_4], 1     // check if y coordinate is smaller than 2
seg012:1832                 jle     short loc_2C88A
seg012:1834                 cmp     [bp+arg_4], 30h   // check if y coordinate is greater than 47
seg012:1838                 jl      short loc_2C88E
seg012:183A                 sub     ax, ax
seg012:183C                 jmp     short loc_2C8D2

seg012:183E                 mov     ax, [bp+arg_2]    // load x coordinate
seg012:1841                 sar     ax, 1             // divide by 2
seg012:1843                 sar     ax, 1             // divide by 2
seg012:1845                 mov     cx, 0Dh
seg012:1848                 imul    cx                // multiply by 13
seg012:184A                 mov     cx, ax

seg012:184C                 mov     ax, [bp+arg_4]    // load y coordinate
seg012:184F                 sar     ax, 1             // divide by 2
seg012:1851                 sar     ax, 1             // divide by 2
seg012:1853                 mov     bx, 0Bh           
seg012:1856                 imul    bx                // multiply by 11

seg012:1858                 add     cx, ax            // add both results
seg012:185A                 add     cx, word_40FB0    // add resource seed
seg012:185E                 mov     [bp+var_2], cx    // save result (first value)

seg012:1861                 mov     ax, [bp+arg_2]    // load x coordinate
seg012:1864                 and     ax, 3             // reduce it modulo 4
seg012:1867                 shl     ax, 1             // multiply by 2
seg012:1869                 shl     ax, 1             // multiply by 2
seg012:186B                 mov     cx, [bp+arg_4]    // load y coordinate
seg012:186E                 and     cx, 3             // reduce it modulo 4

seg012:1871                 add     ax, cx            // add both results (second value)

seg012:1873                 mov     cl, byte ptr [bp+var_2]  // load first value
seg012:1876                 sub     ch, ch
seg012:1878                 and     cx, 0Fh           // reduce it modulo 16
seg012:187B                 cmp     ax, cx            // compare the result to the second value
seg012:187D                 jnz     short loc_2C88A   // if both are the same, the square is a special resource square
seg012:187F                 mov     ax, 1
seg012:1882                 mov     sp, bp
seg012:1884                 pop     bp
seg012:1885                 retf



The Huts Pattern

The huts pattern is very similar to the special resources pattern.

Again, there are two constants which allow for limited changes (it's even the same constants), the first one (13=0x0D by default) at position 0x1F08B, and the second one (11=0x0B by default) at position 0x1F099.

Also, the same resource seed is used.

And finally, here's the relevant code chunk for hut locations:
Code:
seg012:1886                 push    bp
seg012:1887                 mov     bp, sp
seg012:1889                 sub     sp, 2
seg012:188C                 push    si
seg012:188D                 cmp     [bp+arg_0], 0Ah
seg012:1891                 jz      short loc_2C8FF
seg012:1893                 mov     ax, 32h
seg012:1896                 imul    [bp+arg_2]
seg012:1899                 mov     si, ax
seg012:189B                 mov     bx, [bp+arg_4]    // load y coordinate
seg012:189E                 test    byte ptr [bx+si+7FF8h], 1
seg012:18A3                 jnz     short loc_2C8FF
seg012:18A5                 cmp     bx, 1             // check if it's smaller than 2
seg012:18A8                 jle     short loc_2C8FF
seg012:18AA                 cmp     bx, 30h           // check if it's greater than 47
seg012:18AD                 jl      short loc_2C903
seg012:18AF                 sub     ax, ax
seg012:18B1                 jmp     short loc_2C95B

seg012:18B3                 mov     ax, [bp+arg_2]    // load x coordinate
seg012:18B6                 sar     ax, 1             // divide by 2
seg012:18B8                 sar     ax, 1             // divide by 2
seg012:18BA                 mov     cx, 0Dh
seg012:18BD                 imul    cx                // multiply by 13
seg012:18BF                 mov     cx, ax

seg012:18C1                 mov     ax, [bp+arg_4]    // load y coordinate
seg012:18C4                 sar     ax, 1             // divide by 2
seg012:18C6                 sar     ax, 1             // divide by 2
seg012:18C8                 mov     bx, 0Bh
seg012:18CB                 imul    bx                // multiply by 11

seg012:18CD                 add     cx, ax            // add both results
seg012:18CF                 add     cx, word_40FB0    // add resource seed
seg012:18D3                 add     cx, 8             // add 8
seg012:18D6                 mov     [bp+var_2], cx    // save result

seg012:18D9                 mov     ax, [bp+arg_2]    // load x coordinate
seg012:18DC                 and     ax, 3             // reduce it modulo 4
seg012:18DF                 shl     ax, 1             // multiply by 2
seg012:18E1                 shl     ax, 1             // multiply by 2
seg012:18E3                 mov     cx, [bp+arg_4]    // load y coordinate
seg012:18E6                 and     cx, 3             // reduce it modulo 4

seg012:18E9                 add     ax, cx            // add both results

seg012:18EB                 mov     cl, byte ptr [bp+var_2]  // load first value
seg012:18EE                 sub     ch, ch            
seg012:18F0                 and     cx, 1Fh           // reduce it modulo 32
seg012:18F3                 cmp     ax, cx            // compare the result to the second value
seg012:18F5                 jnz     short loc_2C8FF   // if both are the same, a hut is placed on the square
seg012:18F7                 push    [bp+arg_4]
seg012:18FA                 push    [bp+arg_2]
seg012:18FD                 push    cs
seg012:18FE                 call    near ptr sub_2C5C7
seg012:1901                 add     sp, 4
seg012:1904                 test    al, 1
seg012:1906                 jnz     short loc_2C8FF
seg012:1908                 mov     ax, 1
seg012:190B                 pop     si
seg012:190C                 mov     sp, bp
seg012:190E                 pop     bp
seg012:190F                 ret


Note that not much has changed in Civilization II, see here:
http://apolyton.net/forums/showthread.php?t=68481
 
Modifying ...
I found your research into this fascinating. In my own program TerraForm I could not deduce the formula for the Minor Tribes or as commonly known Huts. I was left with the alternative of producing tables for the location of the huts.
Terraform post 117
Terraform post 120

Using your posted code; if the following are equal then there you will have a hut.
[((x \ 4) * &HD) + ((y \ 4) * &HB) + TerrainMasterWord + 8] and &H1F&
= (x And 3) * 4 + (y And 3)

I haven’t incorporated this into my code but with a test program I see that I have a few errors in my hut location table.

A few questions:
What are you using to de-compile the CIV code?
Have you tried code view or something similar to step thru the running code?

I’m wondering if the layout on the SVE file is the same in memory as appears in it’s file.

Getting people to tinker with the EXE using a hex editor( Freeware Hex Editor XVI32 ) might be difficult. I assume that most CIV DOS users would prefer a program that would manipulate the data or in this case the EXE. Then of course what version of CIV one should support. In my opinion support for version .01 and .05 are definite choices. Version .01 because of the cheat factor (Shift-56). Some like it but for me the bugs outweigh the advantages. The lost civilization bug makes version .01 worthless for me.

Anyway to the point of all this rambling. I have modified CIVMAP to work within DOSBOX ( CIVMAP21 revisited ). It seems a perfect platform for your idea for modification or manipulation of the placement of the huts and special resource squares. CIVMAP is already coded to allow map, terrain improvements and special resource squares placement.

One could zap those pesky barbarians.
 
A few questions:
What are you using to de-compile the CIV code?
Have you tried code view or something similar to step thru the running code?
I did not decompile the code, I merely disassembled it using IDA. I did not use any debuggers either.

I’m wondering if the layout on the SVE file is the same in memory as appears in it’s file.
I doubt that. At least the earth map looked totally different in the RAM than it looked in the file. In the RAM, it was easy to recognize, while in the file it looked very cryptic.
 
While the Resources form an easy-to-recognize pattern on the map (to the point that when you find a group of them you can deduce the locations of all hidden ones, particularly those concealed by Grassland which you can turn into Forest), it seems to me the Huts have nothing of the sort. No visible pattern.


One could zap those pesky barbarians.

Are barbs more likely to spawn on a landmass with a Hut?
 
... it seems to me the Huts have nothing of the sort. No visible pattern.

To quote myself
if the following is equal then there you will have a hut.
[((x \ 4) * &HD) + ((y \ 4) * &HB) + TerrainMasterWord + 8] and &H1F& = (x And 3) * 4 + (y And 3)


Yes there is a pattern but not readily apparent.
 
To quote myself
if the following is equal then there you will have a hut.
[((x \ 4) * &HD) + ((y \ 4) * &HB) + TerrainMasterWord + 8] and &H1F& = (x And 3) * 4 + (y And 3)


Yes there is a pattern but not readily apparent.

Yes, I did read the thread, and I realize that I sound like an idiot barging in here and saying that there's "no visible pattern" after you gave the algorithm in x86. I was just saying that the huts are too difficult for me to sight out, whereas the resources are extremely easy.
 
realize that I sound like an idiot barging in here and saying that there's "no visible pattern" after you gave the algorithm in x86. I was just saying that the huts are too difficult for me to sight out, whereas the resources are extremely easy.


Myself an idiot also. I could never figure it out and hard coded all of the possibilities into TerraForm. "no visible pattern” is certainly the appropriate statement.
 
i played with the formula, made an simple simulation in excel, my suggestion:

seg012:1828 push bp
seg012:1829 mov bp, sp
seg012:182B sub sp, 2
seg012:182E cmp [bp+arg_4], 1 // check if y coordinate is smaller than 2
seg012:1832 jle short loc_2C88A
seg012:1834 cmp [bp+arg_4], 30h // check if y coordinate is greater than 47
seg012:1838 jl short loc_2C88E
seg012:183A sub ax, ax
seg012:183C jmp short loc_2C8D2

seg012:183E mov ax, [bp+arg_2] // load x coordinate
seg012:1841 sar ax, 1 // divide by 2
seg012:1843 sar ax, 1 // divide by 2
sar ax, 1 // CHANGE INSERTED
seg012:1845 mov cx, 03h // CHANGE VALUE
seg012:1848 imul cx // multiply by 3
seg012:184A mov cx, ax

seg012:184C mov ax, [bp+arg_4] // load y coordinate
seg012:184F sar ax, 1 // divide by 2
seg012:1851 sar ax, 1 // divide by 2
seg012:1853 mov bx, 01h // CHANGE VALUE
seg012:1856 imul bx // multiply by 1

seg012:1858 add cx, ax // add both results
seg012:185A add cx, word_40FB0 // add resource seed
seg012:185E mov [bp+var_2], cx // save result (first value)

seg012:1861 mov ax, [bp+arg_2] // load x coordinate
seg012:1864 and ax, 3 // reduce it modulo 4
seg012:1867 shl ax, 1 // multiply by 2
// CHANGE REMOVED multiply by 2
seg012:186B mov cx, [bp+arg_4] // load y coordinate
seg012:186E and cx, 3 // reduce it modulo 4

seg012:1871 add ax, cx // add both results (second value)

seg012:1873 mov cl, byte ptr [bp+var_2] // load first value
seg012:1876 sub ch, ch
seg012:1878 and cx, 07h // CHANGE VALUE reduce it modulo 8
seg012:187B cmp ax, cx // compare the result to the second value
seg012:187D jnz short loc_2C88A // if both are the same, the square is a special resource square
seg012:187F mov ax, 1
seg012:1882 mov sp, bp
seg012:1884 pop bp
seg012:1885 retf</pre>

you can see in excel file a sample of pattern

i can't test it, cause i uncompressed exe file i am not able to run. I somebody wants to break a law and send me 475.01 version which works for Gowron, you are welcomed at Adam.Podstavka@gmail.com . (and if this note is against rules of forum, please edit it, or let me know, thx)

hell, Gowron, how did you inserted that Code window? with uniwidth text...
 

Attachments

  • SRpattern.zip
    12.1 KB · Views: 214
well, i made it :) My pattern works quite well i think. It should work on every version of CIV DOS.
Just open CIV.EXE in some hexa editor, find this chunk of code:

D1F8 D1F8 B90D 00F7 E98B C88B 460A D1F8 D1F8 BB0B
00F7 EB03 C803 0E80 6E89 4EFE 8B46 0825 0300 D1E0
D1E0 8B4E 0A83 E103 03C1 8A4E FE2A ED83 E10F 3BC1

and replace by this:
D1F8 D1F8 D1F8 B903 00F7 E98B C88B 460A D1F8 D1F8
BB01 00F7 EB03 C803 0E80 6E89 4EFE 8B46 0825 0300
D1E0 8B4E 0A83 E103 03C1 8A4E FE2A ED83 E108 3BC1

formulas, which decides, whether a square content a special resource - if sides are equal:
constants (bolded in code above in their order)
A = first constatn value (orig 13, my 3)
B = second constant value (orig 11, my 1 or 2)

original formula:
((x div 4)*A + (y div 4)*B + Seed) mod 16 = (x mod 4)*4 + (y mod 4)

my new formula is:
((x div 8)*A + (y div 4)*B + Seed) mod 8 = (x mod 4)*2 + (y mod 4)

there are just 5 changes, now i find it more "randomous behaving", which was my goal :)

you cannot see the differencies in TerraForm, cause it calculates positions of special resources with original formula. So i place screenshots from testing in random new game here. First is with B=1, second with B=2. It affects also saved games, cause .sve nor .map file does't contain information abou specials.
 

Attachments

  • SR_new_pattern.png
    SR_new_pattern.png
    29.1 KB · Views: 277
  • SR_new_pattern2.png
    SR_new_pattern2.png
    35.8 KB · Views: 380
  • Like
Reactions: GPR
WOW!!!
:woohoo:

That something I dreamed about 17 years!!!

Can You make the formula and parameters in a way, that one combination leads to world with damn meny resurce&huts, but anoder - for very rarely resurce&huts?
 
I once reversed the formula so that most of the tiles were special resources.Game experience was crazy as you can imagine:lol: I don't have the exe anymore but you only have to change this instruction
jnz short loc_2C88A // if both are the same, the square is a special resource square
to other jump instruction that will set the tile to bonus resource tile. Maybe JMP instruction would make every tile bonus tile, I'm not sure.
 
A storm of posts
threads erupt from long-forgotten strata
as white glass washed on the night-shrouded shores

A cowherd will cull yearlings
shouting "Haw!" just so
 
excavated from the depths
years in running, problems remain
Mar someone a piece of the task,
which requires knowing binary arithmetic?
 
Top Bottom