Jump to content

Procedurally generated maps - algorithm?


Maurice76

Recommended Posts

I've been deciphering the flow of the game's map generation subroutines. Along the way, I've noted values that may be of interest (for modding, or just for understanding how the game works). I started off with the USO recovery missions and it turned out to be taxing enough to figure out the initialisation alone. In this post I am detailing that initialisation; in later posts, hopefully across the coming days, I hope to detail the generation of all 22 specific map types, starting with the 9 USO recovery mission map types. I tried to be as clear as possible, while also trying to give the necessary information and notes. If something isn't clear, don't hesitate to ask.

 

 

Code legend

SS = Start of subroutine in the executable

ES = End of subroutine in the executable

AC = Attempt Counter for placement of map blocks

CC = Cycle Counter

OF = Offset value for the range

RA = Range for the randomizer (from 0 to the stated value)

ST = Specific Type (see comments)

The location is the hexadecimal offset from the start of the GeoScope.exe file.

The value listed is the value located at that particular offset

The offsets are based on the DOS version of Terror from the Deep, v1.0.

 

Subroutine: USO Recovery missions - generic components

Code Location Value Comment

SS 3F1B0h - -

ES 3F75Bh - -

 

Step 1: dimension determination

Code Location Value Comment

ST 3F1F9h 6E Is 110 decimal. Used to pinpoint a specific byte in the CRAFT.DAT file for this USO. In this case it's byte 0, the Craft type.

ST 3F208h 07 Used to compare against byte 0 of CRAFT.DAT to determine if USO maps need to be 5x5 or 6x6

ST 3F241h 05 Base X and Y dimensions for USO recovery missions. Raised by 1 if the CRAFT.DAT value is 7 or higher

 

Step 2: Original Type, Seabed, or Coral determination

Code Location Value Comment

ST 3F245h 0C Used to check if a check is needed on whether the map is played on the original map type, Seabed or Coral Reef. Note that at this point in the subroutine, the value that's being checked will fall in the range [0 - 8]. Not sure why it checks against 0C, which is 12, as it will always pass the check.

RA 3F24Fh 03 -

ST 3F25Ah 02 If the random value is below this, the original map type will be used. If it's equal to this, it will use the Seabed set instead (so 50% chance on original map type, 25% on Seabed).

ST 3F262h 03 If the random value is equal to this, it will use the Coral Reef set instead (so 25% chance on Coral Reef).

ST 3F285h 31 Next byte is F6. The two bytes combined tell the OS to clear the register holding the map type, essentially setting it to 0; 0 is the map type code for the Seabed (so can't be changed easily). The value is used in the code that is addressed when the random value equals 2 (see above).

ST 3F28Ah 08 If the Coral Reef set was selected in the above determination, this value is inserted into the map type instead of the original type; 8 is the Coral Reef map type. The value is used in the code that is addressed when the random value equals 3 (see above).

 

Step 3: Mission type check (only relevant for the Crashed & Sunken Plane map)

I am not sure about the next section; it seems to check on specific mission types, but the code for those missions will never call this subroutine.

Code Location Value Comment

ST 3F2E7h 6E Is 110 decimal. Used to pinpoint a specific byte in the CRAFT.DAT file for this USO.

ST 3F2F1h 68 The byte in the string for the USO in the CRAFT.DAT file to read. Byte 0x68 holds the touchdown depth.

ST 3F308h 09 The subroutine for USO recovery checks the mission type. This value indicates it checks to see if it's an Artefact site, map type 9 (invalid for USO recovery, maybe old redundant code?)

ST 3F313h 0A Similar check, this one against map type 10, the Cargo Ship Terror site. As above, likely old redundant code?

ST 3F31Eh 0B Similar, this one against map type 11, the Port Terror site.

ST 3F329h 0C Similar, this one against map type 12, the Island Terror site.

ST 3F334h 02 Not redundant, this checks if the map is of type 2, the Crashed & Sunken Plane. Important in combination with the next one.

ST 3F338h 05 Used to set the X and Y dimensions of the Crashed & Sunken Plane map to this value.

 

Step 4: Initialize GeoData array (byte [0x0C] through [0x0C + X * Y] set to 'FF', [0x30] through [0x30 + X * Y] set to '01')

Code Location Value Comment

ST 3F343h 04 The height dimension of all maps generated with this subroutine; written to byte 0x04 of the GeoData.dat file.

 

Step 5: Place the first craft on the map

Subroutine: Place the first craft on the map

Code Location Value Comment

SS 42F80h - -

ES 43148h - -

 

ST 430D7h 0A Used to multiply the Y-coordinate of the first craft in map blocks to get the Y-coordinate in tiles

ST 4310Fh 0A Same as above, but then for the X-coordinate

ST 43126h 00 Used to check ownership of craft, to determine where to write the determined coordinates. Checked against the ownership bit of each craft at address [686EAh] through [689E7h]. This range has 59 bytes per craft, where byte 0 is the Y dimension in map blocks, byte 1 is the X dimension, byte 2 the Z dimension and byte 3 is the ownership bit. A 0 indicates the craft is an X-Com craft, 1 indicates an Alien craft. The game also uses the craft dimensions in its placement subroutine, to decrease the relevant range accordingly.

End Subroutine: Place the first craft on the map

 

Step 6: Determining the second craft

Code Location Value Comment

ST 3F3BEh 6E Is 110 decimal. Used to pinpoint a specific byte in the CRAFT.DAT file for the X-Com craft. In this case it's byte 0, the Craft type.

 

Step 7: Place the second craft on the map

Subroutine: Place the second craft on the map

Code Location Value Comment

SS 43150h - -

ES 43487h - -

 

ST 43412h 0A Used to multiply the Y-coordinate of the second craft in map blocks to get the Y-coordinate in tiles

ST 43448h 0A Same as above, but then for the X-coordinate

ST 4345Fh 00 Used to check ownership of craft, to determine where to write the determined coordinates. Checked against the ownership bit of each craft at address [686EAh] through [689E7h]. This range has 59 bytes per craft, where byte 0 is the Y dimension in map blocks, byte 1 is the X dimension, byte 2 the Z dimension and byte 3 is the ownership bit. A 0 indicates the craft is an X-Com craft, 1 indicates an Alien craft. The game also uses the craft dimensions in its placement subroutine, to decrease the relevant range accordingly. Note that the second subroutine will only ever place X-Com craft, since no more than one USO can exist on a given map.

End Subroutine: Place the second craft on the map

 

Step 8: Call the correct subroutine of the map type in question, to place the map blocks

Subroutine: Map type selection

Code Location Value Comment

SS 434C0h - This subroutine is just a switch statement, checking for the proper subroutine to call, based on map type.

ES 435BAh - -

 

ST 434E7h 0D In decimals, this is 13. The map type is checked against this value to determine the correct subroutine to call. The subroutine calls are starting from 434FBh and those instructions are 16 bytes each in length, 12 in total. The data at [43488h - 434BF] are 14 offset values of 4 bytes each, used by the switch statement to find the proper switch case. The first 13 values each point to one of the random map types, including GRUNGE. However, the entry for GRUNGE points to the subroutine for the Port Terror site; apparently, GRUNGE was originally intended to be handled by this subroutine, but eventually placed elsewhere. Entry 14 points to the end of the switch statement, which is the end of the subroutine as well.

End of Subroutine: Map type selection

 

From this point onwards, the Subroutine: USO Recovery missions - generic components continues with other files, like the UNIPOS.DAT and UNIREF.DAT files.

Link to comment
Share on other sites

Below is the map generation code for the Seabed maptype (map type 0):

 

Special blocks: 00, 01 (used as floor below craft)

Filler blocks, 1x1: 02, 03, 04, 05, 06, 07, 08, 09

Filler blocks, 2x2: 10, 11, 12

 

Code legend

AC = Attempt Counter for placement of map blocks

CC = Cycle Counter

SS = Start of subroutine in the executable

ES = End of subroutine in the executable

OF = Offset value for the range

RA = Range for the randomizer (from 0 to the stated value)

ST = Specific Type (see comments)

The location is the hexadecimal offset from the start of the GeoScope.exe file.

The value listed is the value located at that particular offset

The offsets are based on the DOS version of Terror from the Deep, v1.0.

 

Subroutine: Seabed map generation

Code Location Value Comment

SS 435C0h - -

ES 43709h - -

 

Step 1: determining the 2x2 blocks

Code Location Value Comment

ST 435E6h 02 The Y-dimension of the map is lowered by this value (effectively taking out the bottom row of the map for the check to place the top row of a 2x2 block) to find valid Y-coordinates for 2x2 map blocks.

ST 435F2h 02 Same for the X-dimenion.

RA 43667h 02 The range of 2x2 blocks (counting from 0)

OF 43677h 0A The first 2x2 block in the MAPS directory for this map type starts at 10

AC 436B2h 14 The number of attempts to place a 2x2 per cycle, before skipping to the next (in hex)

CC 436C5h 03 The maximum number of 2x2 blocks to place

 

Step 2: filling up the empty blocks with 1x1 blocks

Code Location Value Comment

RA 436DCh 07 There are 8 1x1 blocks in the Seabed set

OF 436F4h 02 The first 1x1 block usable for filler is number 02 (counting from 00)

End of subroutine: Seabed map generation

 

How to read this:

- First, the game will fill out the 2x2 blocks. It will attempt to place 3 of them, each time attempting it either 20 times (breaking from it if it fails after 20), or until it succeeds.

- Once it has found one to place, it will determine the specific block number. It will generate a random number in the range [0 - <value>] where <value> is listed in the table above (so for 2x2 blocks, the range is [0 - 2], 3 blocks total, and for 1x1 blocks, the range is [0 - 7], 8 blocks total).

- To find the final block number, the offset is added to the randomly found value. For 2x2 blocks, possible values for the Seabed map type are therefore 10, 11, and 12, while for the 1x1 blocks, the possible values are in the range [2 - 9].

 

With the offsets and ranges in mind, it's possible to change them. If you wish to add more map blocks to the mix, you have to shift the numbers accordingly. Pay close attention and make sure not to create overlaps in the ranges! If you do, at best some map blocks may show up corrupted ingame, but at worst, your game simply crashes. As always when making changes, save an (original) copy first! Make sure to have map blocks and route nodes in respectively the MAPS and the ROUTES folder, matching the numbers you added.

 

Edit: I just realised that the blocks used for craft have to be added through the craft placement routines. I didn't recognize those values yet, so I will have a closer look tonight. I did see in the logging that it's determined for each block separately (so for instance, for the Dreadnought, the RNG is called 9 times).

Link to comment
Share on other sites

I've made a nice discovery when I was trying to find how the game determined the map block numbers to be placed under craft where relevant, and identified the purpose of a datablock as a result. As it turns out, the map block numbers are hard coded into the GeoScope.exe file. Starting at address 68564h, there are 16 strings of 26 bytes each. I don't know why they numbered them as high as that, since the range is only being addressed while placing crafts on the map. To find the proper string for a map type, multiply the map type number by 26 and add it do the address I mentioned.

 

I also wonder why the range is across 26 bytes, as the game only seems to address the first 10 and only during placement of the crafts. When it places one, it determines a random value in the range of [0-9], and picks the byte at that number for the map type in question (exact byte formula: (26 * map type) + <random value> + 68564h). I assume they resorted to this method, because the switch subroutine that determines map type specifics is only called after placing the crafts. Anyway, the range directly preceeds the X-Com and Alien craft definitions. In table form:

 

Map Name Values

0 Seabed 00 00 00 00 00 01 01 01 01 01 01 02 03 04 05 06 07 08 09 0A 0B 0C 0B 0B 0B 0B

1 Pipes 00 00 00 00 00 00 00 00 00 00 01 02 03 04 05 06 08 09 0A 0B 0B 0B 0B 0B 0B 0B

2 Plane 00 00 00 00 00 00 00 00 00 00 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 07 08

3 Atlantis 00 00 00 00 01 01 01 01 00 01 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 07 08

4 Mayan 00 00 00 00 00 00 00 00 00 00 00 01 02 03 04 05 06 07 08 09 0A 0B 0B 0B 0B 0B

5 Galleon 0D 0E 0F 10 0D 0E 0F 10 0D 0E 0E 0F 10 11 12 03 04 0E 0F 10 11 12 03 03 04 04

6 Sunk Liner 00 00 00 00 00 01 01 01 01 01 01 02 03 04 05 06 07 08 09 0A 0B 0C 0B 0B 0B 0B

7 Volcanic 00 00 00 00 00 00 00 00 00 00 03 04 05 06 07 08 09 0A 0B 0C 03 04 05 06 07 08

8 Coral Reef 00 00 00 00 00 00 00 00 00 00 01 02 03 04 05 06 07 08 09 0A 0B 0B 05 06 07 08

9 Artefact 00 00 00 00 00 00 00 00 00 00 03 04 05 06 07 08 09 0A 0B 02 03 04 05 06 07 08

10 Grunge 01 01 01 01 01 01 01 01 01 01 03 04 05 06 07 08 09 0A 0B 02 03 04 05 06 07 08

11 Cargoship 00 00 00 00 00 00 00 00 00 00 03 04 05 06 07 08 09 0A 0B 02 03 04 05 06 07 08

12 Port 00 01 00 01 00 01 00 01 00 01 0A 0B 0C 0D 0E 0F 10 10 10 10 10 10 10 10 10 10

13 Island 00 01 00 01 00 01 00 01 00 01 0A 0B 0C 0D 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E

14 Colony 1 00 01 00 01 00 01 00 01 00 01 0A 0B 0C 0D 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E

15 T'Leth 1 01 01 01 01 01 01 01 01 01 01 03 04 05 06 07 08 09 0A 0B 02 03 04 05 06 07 08

 

As can be seen for instance with the Sunken Galleon, map blocks 13 and 14 have 30% chance each to be selected as the map block number to put a craft upon, as 15 and 16, which both only have a 20% chance. I've underlined the first 10 entries in each listing, because those are the ones the game randomly picks as map block numbers for crafts. The meaning of the remaining 16 bytes in each string is a mystery.

 

For people feeling adventurous: For the first craft being placed, the address at 4304Ch holds the value '1A', which is the string length used to calculate the offset. The value '09' at 43062h holds the range number used to determine the random value above. For the second one, the addresses are respectively 4338Fh and 43391h.

Link to comment
Share on other sites

Below is the map generation code for the Pipes research station maptype (map type 1):

 

It's pretty similar to the Seabed routine, with just a few differences.

 

Special blocks: 00 (used as floor below craft)

Filler blocks, 1x1: 01, 02, 03, 04, 05, 06, 07

Filler blocks, 2x2: 08, 09, 10, 11

 

Code legend

AC = Attempt Counter for placement of map blocks

CC = Cycle Counter

SS = Start of subroutine in the executable

ES = End of subroutine in the executable

OF = Offset value for the range

RA = Range for the randomizer (from 0 to the stated value)

ST = Specific Type (see comments)

The location is the hexadecimal offset from the start of the GeoScope.exe file.

The value listed is the value located at that particular offset

The offsets are based on the DOS version of Terror from the Deep, v1.0.

 

Subroutine: Pipes research station map generation

Code Location Value Comment

SS 43710h - -

ES 4385Fh - -

 

Step 1: determining the 2x2 blocks

Code Location Value Comment

ST 43735h 02 The Y-dimension of the map is lowered by this value (effectively taking out the bottom row of the map for the check to place the top row of a 2x2 block) to find valid Y-coordinates for 2x2 map blocks.

ST 43743h 02 Same for the X-dimenion.

RA 437BDh 03 -

OF 437CEh 08 -

AC 43807h 14 -

CC 4381Ah 03 -

 

Step 2: filling up the empty blocks with 1x1 blocks

Code Location Value Comment

RA 43832h 06 -

OF - - There is no explicit offset value. The instruction 'FE C0' at 43849h increases the random value by one

End of Subroutine: Pipes research station map generation

Link to comment
Share on other sites

Below is the map generation code for the Crashed & Sunken Plane maptype (map type 2):

 

Craft blocks: 00

Filler blocks 1x1: 01, 02, 03

Cockpit blocks: 04, 05

Middle sections (with wing connectors): 06, 07

Middle sections (without those): 08, 09, 10, 11

Tail section: 12

Large wing (left side): 13, 14

Large wing (right side): 15, 16

Small wing (left side): 17, 18

Small wing (right side): 19, 20

 

Note 1: this subroutine makes some checks against some hard-coded addresses. As it is, to build the map block section of the GeoData array, the game addresses a specific block, starting at memory address '1632Ch'. That address is the start of the entire 92-byte array of the GeoData.dat file. Those hardcoded addresses are used throughout the subroutines building the GeoData.dat file.

Note 2: I encounted what appears to be a bug in the code. I have never seen it in effect, but I realise the chances are fairly small. Still, interesting to see that it's clearly a bug even if it's likely a very rare one.

 

Code legend

AC = Attempt Counter for placement of map blocks

CC = Cycle Counter

SS = Start of subroutine in the executable

ES = End of subroutine in the executable

OF = Offset value for the range

RA = Range for the randomizer (from 0 to the stated value)

ST = Specific Type (see comments)

The location is the hexadecimal offset from the start of the GeoScope.exe file.

The value listed is the value located at that particular offset

The offsets are based on the DOS version of Terror from the Deep, v1.0.

 

Subroutine:Sunken Plane map generation

Code Location Value Comment

SS 439B0h - -

ES 43CDDh - -

 

Step 1: Placing a left wing (top side of the map)

The game starts by trying to place a large left wing on the top row at column 02.

If it fails, it tries the column next to it, column 03.

Code Location Value Comment

ST 439BFh 01 Used to check if a large left wing could be placed succesfully. This value is set to 0 when it has succeeded. Regardless of this value, at least one attempt will be made.

ST 439EDh 02 The middle column is column 02 (starting from 00)

ST 43A00h 02 Number of columns it tries to match before moving to the next row

ST 43A08h 02 Number of rows it tries to match before breaking from the placement attempt

Essentially, it tries to place a large wing on (2, 0), (3, 0), (2, 1) or (3, 1), in that order. If it still fails to do so, it skips out and moves on to try to place a small wing instead.

If it succeeds in placing a large wing section, it will make 1 determination to find which:

RA 43A18h 01 There are only 2 wing map blocks for the left wing: 13 and 14

OF 43A28h 0D Map block offset for those 2 wing map blocks

 

Step 1a: Placing a small left wing (top side of the map)

If it failed to place the large left wing, it will make 1 attempt to place a small left wing: at (3, 0)

Code Location Value Comment

ST 43A5Ah 3B Together with the next two bytes reads as '3B 63 01'. Reversing the byte order yields a hard-coded address '1633Bh' - this is the hard-coded position in the byte array for the GeoData.dat file, corresponding to coordinate (3, 0). This one is used as validity check.

ST 43A5Eh FF The value the above array element is being checked against. If it's equal, the small wing can be placed.

RA 43A62h 01 There are only 2 small wing map blocks for the left wing: 17 and 18

OF 43A6Ch 11 Map block offset for those 2 wing map blocks

ST 43A6Eh 3B Like the 3B value above, when combined with the next two bytes yields the hardcoded offset where the determined map block number has to be written. Logically, it's identical to the address at the start of the step 1a section.

 

Step 2: Placing a right wing (bottom side of the map)

Pretty similar to the left wing, except at the bottom side of the map.

Code Location Value Comment

ST 43A86h 01 Used to check if a large right wing could be placed succesfully. This value is set to 0 when it has succeeded. Regardless of this value, at least one attempt will be made.

ST 43A94h 03 The number of rows to skip from the very top to start attempts to place a large right wing section.

ST 43A9Ah 02 The middle column is column 02 (starting from 00)

ST 43AADh 02 Number of columns it tries to match before moving to the next row

ST 43AB5h 02 Number of rows it tries to match before breaking from the placement attempt (note that increasing this value pushes it out of the default map dimensions!)

Essentially, it tries to place a large wing on (2, 2), (3, 2), (2, 3) or (3, 3), in that order. If it still fails to do so, it skips out and moves on to try to place a small wing instead.

If it succeeds, it will determine a map block number to place at that section.

RA 43AC5h 01 There are only 2 wing map blocks for the right wing: 15 and 16

OF 43AD4h 0F Map block offset for those 2 wing map blocks

 

Step 2a: Placing a small right wing (bottom side of the map)

Like with the left wing, if it failed to place a right wing, it will make one attempt to place a small wing instead: at (3, 3)

Code Location Value Comment

ST 43B06h 3E Together with the next two bytes reads as '3E 63 01'. Reversing the byte order yields a hard-coded address '1633Eh' - this is the hard-coded position in the byte array for the GeoData.dat file, corresponding to coordinate (1, 1). This one is used as validity check.

ST 43B0Ah FF The value the above array element is being checked against. If it's equal, the small wing can be placed.

RA 43B0Eh 01 There are only 2 small wing map blocks for the left wing: 19 and 20

OF 43B18h 13 Map block offset for those 2 wing map blocks

ST 43B1Ah 4A Like the 3E value above, when combined with the next two bytes yields the hardcoded offset where the determined map block number has to be written. This one, 4A 63 01 translates to 1634Ah and refers to (3, 3) on the map.

NOTE THE BUG HERE! The validity check is made against (1, 1)! If there is nothing yet at (1, 1) during the validity check, it will overwrite whatever is located at (3, 3)!

 

Step 3: placing the tail section

Basically, this step randomly determines a map block along the left map border. It uses the maps' dimensions (determined before entering this subroutine) to find those specific blocks.

Code Location Value Comment

ST 43B46h 0C Value of the tail section map block

ST 43B48h 04 Written to a specific register when the tail is placed succesfully. The register is used to check how many placement attempts have been made. The register was set to 0 just before starting this step.

ST 43B56h 04 The value against which the register is being checked; hence, the game will make 5 attempts to place the tail section.

 

Step 4: placing the first middle section

This step aims to place a middle section on the central column of the map, randomizing the row on which it appears. It derives the central column by adding 2 to the column number of the left side of the map (hardcoded check).

Code Location Value Comment

RA 43BB0h 03 It has 4 map blocks available, from which it picks one.

OF 43BE8h 08 The four available middle section map blocks are 08, 09, 10 and 11

ST 43BF0h 04 Written to a specific register when the middle section is placed succesfully. The register is used to check how many placement attempts have been made. The register was set to 0 just before starting this step.

ST 43BF8h 04 The value against which the register is being checked; hence, the game will make 5 attempts to place the middle section.

 

Step 5: placing the wing connector section

Like step 4, this attempts to place one of the map blocks with a middle section, but with wing connectors attached to it. The attempt is made on column 03.

Code Location Value Comment

RA 43C03h 01 There are two such map blocks; one is picked at random

ST 43C1Ch 03 Used to determine the blocks in column 03, by adding this value to the left side border of the map. This one is used to validate placement.

ST 43C36h 03 Same value, except this time to actually place the map block.

OF 43C39h 06 Offset for the two middle sections with wing connectors among the map blocks. Found at 06 and 07.

ST 43C3Eh 04 Written to a specific register when this section is placed succesfully. The register is used to check how many placement attempts have been made. The register was set to 0 just before starting this step.

ST 43C4Ch 04 The value against which the register is being checked; hence, the game will make 5 attempts to place this section.

 

Step 6: placing the cockpit

Code Location Value Comment

RA 43C57h 01 There are two cockpit map blocks; one is picked at random

ST 43C70h 04 Used to determine the blocks in column 04, by adding this value to the left side border of the map. This one is used to validate placement.

ST 43C83h 04 Same value, except this time to actually place the map block.

OF 43C8Dh 04 The cockpit modules are 04 and 05.

ST 43C95h 04 Written to a specific register when the cockpit is placed succesfully. The register is used to check how many placement attempts have been made. The register was set to 0 just before starting this step.

ST 43C9Dh 04 The value against which the register is being checked; hence, the game will make 5 attempts to place the cockpit

 

Step 7: filling up the empty blocks with 1x1 blocks

Code Location Value Comment

RA 43CAFh 02 There are 3 random 1x1 blocks available for filling

ST 43CC6h FE There is no hard value of 1 added to the offset. Instead, this byte combined with the next one, C0, forms an instruction to raise the value in a specific register by 1. The register was set to 0 by another instruction just before.

End of Subroutine: Sunken Plane map generation

 

As a final note, analysing this one was both fun (getting a look at the internal workings) and taxing (given the somewhat complex approach they made). But I think this was the most complex of all the maps, the rest should be (a lot) easier.

Link to comment
Share on other sites

Good catch. It's such a trivial error that no one would really notice it, but that is most certainly a bug. Must chronicle on wiki!

 

For what it's worth, the check for the small left wing is done at geodata map 0 + 3, which is all right. However, the check for the small right wing is at geodata map 3 + 3.

 

The mistake here is they should have multiplied the first 3 with the map width (5) to get 18, which is where 3,3 would be located in the Geodata map array. 3+3 would resolve to 6. So col 0 row 1?

 

Also the final placement of the small wing is explicitly specified as at map location 18. In retrospect, probably not a good idea had they suddenly decided to let the map have variable dimensions. But there you go.

 

Indeed, of the regular maps, the sunken plane certainly is slightly more complex than the usual: fit planes, fit 20x20's, fill gaps. wink.png

 

- NKF

Link to comment
Share on other sites

Good catch. It's such a trivial error that no one would really notice it, but that is most certainly a bug. Must chronicle on wiki!

 

I remember encountering it once ingame, many years ago. The game placed the wing and then put the X-Com craft right on top of it, replacing the wing tiles with the X-Com craft tiles where relevant. Looked quite odd, but I didn't stop to think about it back then. Still, the odds of it happening are small. During craft placement (USO and X-Com craft), it has to occupy (3, 3) and leave (1, 1) open as it is. Then when it tries to place the large right wing, it has to fail, so it skips to placing a small right wing instead.

 

For what it's worth, the check for the small left wing is done at geodata map 0 + 3, which is all right. However, the check for the small right wing is at geodata map 3 + 3.

 

The mistake here is they should have multiplied the first 3 with the map width (5) to get 18, which is where 3,3 would be located in the Geodata map array. 3+3 would resolve to 6. So col 0 row 1?

 

Also the final placement of the small wing is explicitly specified as at map location 18. In retrospect, probably not a good idea had they suddenly decided to let the map have variable dimensions. But there you go.

 

The check is made against the hardcoded addresses starting at 1632Ch (in the code anyway, once it's loaded in memory it alters it to 1F032Ch instead). The game stores the GeoData.dat array (all 92 bytes) from that location onwards, filling it with the relevant values, before passing it to the subroutine that writes the array to file (to be precise: it copies 92 bytes starting from address 1F032Ch to another buffer and that buffer gets written to file).

 

For the small left wing, the placement check is made against the byte located at 1633Bh (which is (3, 0)) and if it finds that it can place it there (the byte at 1633Bh equals 'FF'), it writes the map block number to the byte at 1633Bh, overwriting the 'FF' there. This is hard coded, it doesn't translate map coordinates or anything. Same for the small right wing, except it checks (incorrectly) against the byte at 1633Eh (which is (1, 1) for a 5 x 5 map) and if it finds that placement is valid, it will write the map block number to the byte located at 1664Ah (which is (3, 3) for a 5 x 5 map). Again, hard coded addresses, no map coordinate calculations or anything. I admit that I was surprised they addressed those bytes with direct address, rather than calculating offsets from the 1632Ch start address like they do with virtually all other map blocks.

 

But yes, I also noticed that they pass along the map dimensions for USO recovery missions between the subroutines, making explicit distinction between X and Y coordinates. After all, all maps are squares in their dimensions, except for Ship Terror sites - but those are static maps. The funny thing is that if you are able to define dimensions where X and Y are not equal, the subroutines will all likely still work. I guess we'll never know whether they did that to make the subroutines internally robust, or whether they were toying with the idea of making random rectangular maps as well.

Link to comment
Share on other sites

Good catch. It's such a trivial error that no one would really notice it, but that is most certainly a bug. Must chronicle on wiki!

 

I've seen that you updated it indeed wink.png. But I don't think your assumption is correct - of course, I don't know which source you are using wink.png. I've derived it directly from the code of the game itself, after disassembling it in IDA 5.0. The two relevant sections in the .exe that place the two small wings:

 

For the small left wing: 80 3D 3B 63 01 00 FF 75 11 B8 01 00 00 00 E8 15 BE FE FF 04 11 A2 3B 63 01 00 (address range 43A58h - 43A71h)

For the small right wing: 80 3D 3E 63 01 00 FF 75 11 B8 01 00 00 00 E8 69 BD FE FF 04 13 A2 4A 63 01 00 (address range 43B04h - 43B1Dh)

 

Disecting it and disassembling the pieces:

 

For the small left wing:

80 3D 3B 63 01 00 FF = cmp byte ptr ds:1633Bh, 0FFh. It compares the byte located at address 1633Bh against the value 'FF'

75 11 = jnz short loc_510DA. In other words, if the value is unequal to 'FF', skip this section and jump to the address at loc_510DA; this means placement of a small wing section failed.

B8 01 00 00 00 = mov eax, 1. If the value was equal to 'FF', the placement will occur. First step is to set the value of the EAX register to 1. As I indicated in my Plane map construction post a bit above, this is the range the game uses for the next call, the random number generator.

E8 15 BE FE FF = call Randomizer. The name 'Randomizer' is one that I assigned to the subroutine being called. It will take the value from the previous instruction to generate a random number in the range from 0 to the number specified. In this case, the result will be either 0 or 1.

04 11 = al, 11h. The randomizer returned the result into the 'AL' register (part of the EAX register) and this instruction subsequently raises it by 11h. The value 11h is the offset for the small left wing section.

A2 3B 63 01 00 = mov ds:1633Bh, al. The resulting value in the 'AL' byte register is copied directly into the address at 1633Bh. This is the same address as was being checked against.

 

Note that both the validation check as well as the actual writing of the map block number happen based on a direct address, not on a dimension / position calculation.

 

For the small right wing, we have the dissection in a similar fashion:

80 3D 3E 63 01 00 FF = cmp byte ptr ds:1633Eh, 0FFh. See above, except the check address is 1633Eh instead.

75 11 = jnz short loc_51186. Same as above, except the effective jump location is different. The value '11' is relative to the location from where the jump is made.

B8 01 00 00 00 = mov eax, 1.

E8 69 BD FE FF = call Randomizer

04 13 = add al, 13h. The value 13h is the offset for the small right wing among the map blocks for this map type.

A2 4A 63 01 00 = mov ds:1634Ah, al. The map block number is written to address 1634Ah. This value differs from the one against which the check was made.

 

As I said above, the whole map generation has code that writes bytes to an address range ranging from 1632Ch to 16387h, 92 bytes in total. This is a memory range, not an address offset within the GeoScope.exe of course. When the game is run, the actual address range is shifted to 1F032Ch and higher, but the code explicitely refers to the previous range as you can see from the dissection.

 

The order of these bytes corresponds exactly to the byte order of the GeoData.dat file as detailed on the UfoPedia page. Bytes 0Ch through 2Fh detail the map block numbers, according to that page. Adding the offset to the address of the start byte, the start address of the map block numbers is 16338h, ending at 1635Bh. For a 5x5 map, only the first 25 of those 36 bytes are used. Placing them in a diagram as if it's the minimap of the mission yields:

 

Row 0: 16338h 16339h 1633Ah 1633Bh 1633Ch

Row1: 1633Dh 1633Eh 1633Fh 16340h 16341h

Row2: 16342h 16343h 16344h 16345h 16346h

Row3: 16347h 16348h 16349h 1634Ah 1634Bh

Row4: 1634Ch 1634Dh 1634Eh 1634Fh 16350h

 

I've highlighted the three blocks relevant for the placement of the small wings. The one in the top row is for the small left wing. The one in the first row is the position being checked against for the small right wing, while the one in the third row is where the wing is actually getting placed.

 

With some nudging on my side (slight modification to the GeoData.dat file before it was being passed to the Tactical.exe file), I've managed to force a reproduction of the bugs' result. The image can be seen below:

CrashedPlaneBug.jpg

The wing tiles where the X-Com craft is being placed, are simply replaced by those of the X-Com craft in question. The other tiles are left untouched. As you can see, the bug is mostly cosmetical, it just looks weird.

 

For the record, what you stated with your relative location calculations is indeed used for the plane's middle sections, as well as the cockpit. It's just not being used for the small wings: those use direct address pointers.

Link to comment
Share on other sites

Below is the map generation code for the Atlantis maptype (map type 3):

 

Special blocks: 00, 01 (used as floor below craft)

Filler blocks, 1x1: 01, 02, 03, 04, 05, 06, 07, 08, 09

Filler blocks, 2x2: 10, 11, 12

 

Code legend

SS = Start of subroutine in the executable

ES = End of subroutine in the executable

AC = Attempt Counter for placement of map blocks

CC = Cycle Counter

OF = Offset value for the range

RA = Range for the randomizer (from 0 to the stated value)

ST = Specific Type (see comments)

The location is the hexadecimal offset from the start of the GeoScope.exe file.

The value listed is the value located at that particular offset.

The offsets are based on the DOS version of Terror from the Deep, v1.0.

 

Subroutine: Atlantis map generation

Code Location Value Comment

SS 43860h - -

ES 439ABh - -

 

Step 1: determining the 2x2 blocks

Code Location Value Comment

ST 43887h 02 The Y-dimension of the map is lowered by this value (effectively taking out the bottom row of the map for the check to place the top row of a 2x2 block) to find valid Y-coordinates for 2x2 map blocks.

ST 43894h 02 Same for the X-dimenion.

RA 4390Ah 01 -

OF 4391Ah 10 -

AC 43953h 14 -

CC 43966h 02 -

 

Step 2: filling up the empty blocks with 1x1 blocks

Code Location Value Comment

RA 4397Eh 08 -

OF There is no explicit offset value. The instruction 'FE C0' at 40FFDh increases the random value by one.

End of Subroutine: Atlantis map generation

 

Note that the RNG yields a number in the range [0 - 8] for step 2, which is then raised to [1 - 9]. This means block 01 is also used as filler.

Link to comment
Share on other sites

Below is the map generation code for the Mayan Temple maptype (map type 4):

 

Special blocks: 00 (used as floor below craft)

Filler blocks, 1x1: 01, 02, 03, 04, 05, 06, 07, 08

Filler blocks, 2x2: 09, 10, 11

Unused blocks: 12, 13, 14

 

Code legend

SS = Start of subroutine in the executable

ES = End of subroutine in the executable

AC = Attempt Counter for placement of map blocks

CC = Cycle Counter

OF = Offset value for the range

RA = Range for the randomizer (from 0 to the stated value)

ST = Specific Type (see comments)

The location is the hexadecimal offset from the start of the GeoScope.exe file.

The value listed is the value located at that particular offset.

The offsets are based on the DOS version of Terror from the Deep, v1.0.

 

Subroutine: Mayan temple map generation

Code Location Value Comment

SS 43CE0h - -

ES 43E2Bh - -

 

Step 1: determining the 2x2 blocks

Code Location Value Comment

ST 43D07h 02 The Y-dimension of the map is lowered by this value (effectively taking out the bottom row of the map for the check to place the top row of a 2x2 block) to find valid Y-coordinates for 2x2 map blocks.

ST 43D14h 02 Same for the X-dimenion.

RA 43D8Ah 02 -

OF 43D9Ah 09 -

AC 43DD3h 14 -

CC 43DE6h 02 -

 

Step 2: filling up the empty blocks with 1x1 blocks

Code Location Value Comment

RA 43DFEh 07 -

OF There is no explicit offset value. The instruction 'FE C0' at 43E15h increases the random value by one.

End of Subroutine: Mayan temple map generation

 

Note: Map blocks 12, 13 and 14 are unused. They are variations on respectively map blocks 09, 10 and 11.

Link to comment
Share on other sites

Below is the map generation code for the Sunken Galleon maptype (map type 5):

 

Special blocks: 13, 14, 15, 16 (used as floor below craft)

Filler blocks, 1x1: 04, 05, 06, 07, 08, 09, 10, 11, 12

Filler blocks, 2x2: 01, 02, 03

 

Code legend

SS = Start of subroutine in the executable

ES = End of subroutine in the executable

AC = Attempt Counter for placement of map blocks

CC = Cycle Counter

OF = Offset value for the range

RA = Range for the randomizer (from 0 to the stated value)

ST = Specific Type (see comments)

The location is the hexadecimal offset from the start of the GeoScope.exe file.

The value listed is the value located at that particular offset.

The offsets are based on the DOS version of Terror from the Deep, v1.0.

 

Subroutine: Sunken Galleon map generation

Code Location Value Comment

SS 43E30h - -

ES 43F7Bh - -

 

Step 1: determining the 2x2 blocks

ST 43E57h 02 The Y-dimension of the map is lowered by this value (effectively taking out the bottom row of the map for the check to place the top row of a 2x2 block) to find valid Y-coordinates for 2x2 map blocks.

ST 43E64h 02 Same for the X-dimenion.

Code Location Value Comment

RA 43EDAh 02 -

OF There is no explicit offset value. The instruction 'FE C0' at 43EE9h increases the random value by one.

AC 43F23h 14 -

CC 43F36h 02 -

 

Step 2: filling up the empty blocks with 1x1 blocks

Code Location Value Comment

RA 43F4Eh 08 -

OF 43F66h 04 -

End of Subroutine: Sunken Galleon map generation

Link to comment
Share on other sites

Below is the map generation code for the Sunken Liner maptype (map type 6):

 

Special blocks: 00, 01 (used as floor below craft)

Filler blocks, 1x1: 07, 08, 09, 10, 11, 12, 13

Filler blocks, 2x2: 02, 03, 04, 05, 06

 

Code legend

SS = Start of subroutine in the executable

ES = End of subroutine in the executable

AC = Attempt Counter for placement of map blocks

CC = Cycle Counter

OF = Offset value for the range

RA = Range for the randomizer (from 0 to the stated value)

ST = Specific Type (see comments)

The location is the hexadecimal offset from the start of the GeoScope.exe file.

The value listed is the value located at that particular offset.

The offsets are based on the DOS version of Terror from the Deep, v1.0.

 

Subroutine: Sunken Liner map generation

Code Location Value Comment

SS 43F80h - -

ES 440C9h - -

 

Step 1: determining the 2x2 blocks

Code Location Value Comment

ST 43FA6h 02 The Y-dimension of the map is lowered by this value (effectively taking out the bottom row of the map for the check to place the top row of a 2x2 block) to find valid Y-coordinates for 2x2 map blocks.

ST 43FB2h 02 Same for the X-dimenion.

RA 44027h 04 -

OF 44037h 02 -

AC 44072h 14 -

CC 44085h 03 -

 

Step 2: filling up the empty blocks with 1x1 blocks

Code Location Value Comment

RA 4409Ch 06 -

OF 440B4h 07 -

End of Subroutine: Sunken Liner map generation

Link to comment
Share on other sites

Below is the map generation code for the Volcanic maptype (map type 7):

 

Special blocks: 00 (used as floor below craft)

Filler blocks, 1x1: 01, 02, 03, 04, 05, 06, 07, 08

Filler blocks, 2x2: 09, 10, 11

 

Code legend

SS = Start of subroutine in the executable

ES = End of subroutine in the executable

AC = Attempt Counter for placement of map blocks

CC = Cycle Counter

OF = Offset value for the range

RA = Range for the randomizer (from 0 to the stated value)

ST = Specific Type (see comments)

The location is the hexadecimal offset from the start of the GeoScope.exe file.

The value listed is the value located at that particular offset.

The offsets are based on the DOS version of Terror from the Deep, v1.0.

 

Subroutine: Volcanic map generation

Code Location Value Comment

SS 440D0h - -

ES 4421Bh - -

 

Step 1: determining the 2x2 blocks

Code Location Value Comment

ST 440F7h 02 The Y-dimension of the map is lowered by this value (effectively taking out the bottom row of the map for the check to place the top row of a 2x2 block) to find valid Y-coordinates for 2x2 map blocks.

ST 44104h 02 Same for the X-dimenion.

RA 4417Ah 02 -

OF 4418Ah 09 -

AC 441C3h 14 -

CC 441D6h 02 -

 

Step 2: filling up the empty blocks with 1x1 blocks

Code Location Value Comment

RA 441EEh 07 -

OF There is no explicit offset value. The instruction 'FE C0' at 44205h increases the random value by one.

End of Subroutine: Volcanic map generation

Link to comment
Share on other sites

Below is the map generation code for the Coral maptype (map type 8):

 

Special blocks: 00 (used as floor below craft)

Filler blocks, 1x1: 01, 02, 03, 04, 05, 06, 07, 08

Filler blocks, 2x2: 09, 10, 11, 12

 

Code legend

SS = Start of subroutine in the executable

ES = End of subroutine in the executable

AC = Attempt Counter for placement of map blocks

CC = Cycle Counter

OF = Offset value for the range

RA = Range for the randomizer (from 0 to the stated value)

ST = Specific Type (see comments)

The location is the hexadecimal offset from the start of the GeoScope.exe file.

The value listed is the value located at that particular offset.

The offsets are based on the DOS version of Terror from the Deep, v1.0.

 

Subroutine: Coral map generation

Code Location Value Comment

SS 44220h - -

ES 4436Fh - -

 

Step 1: determining the 2x2 blocks

Code Location Value Comment

ST 44247h 02 The Y-dimension of the map is lowered by this value (effectively taking out the bottom row of the map for the check to place the top row of a 2x2 block) to find valid Y-coordinates for 2x2 map blocks.

ST 44253h 02 Same for the X-dimenion.

RA 442CDh 02 -

OF 442DEh 09 -

AC 44317h 14 -

CC 4432Ah 03 -

 

Step 2: filling up the empty blocks with 1x1 blocks

Code Location Value Comment

RA 44342h 07 -

OF There is no explicit offset value. The instruction 'FE C0' at 44359h increases the random value by one.

End of Subroutine: Coral map generation

Link to comment
Share on other sites

For the record, what you stated with your relative location calculations is indeed used for the plane's middle sections, as well as the cockpit. It's just not being used for the small wings: those use direct address pointers.

 

You're not incorrect here. But at the same time, the offsets 0+3 and 3+3 that I mentioned are also correct as I was paraphrasing. My guess that they had left out the multiplier for the map width was my own.

 

What you are seeing is the optimized machine code. The Watcom compiler would have recognised the offset formula as consisting of a couple of constant values being added together. To help speed things up it stored the result rather than the full formula.

 

Where I think I went wrong was forgetting that arrays start with 0 ,so I was thinking the top row of the map was numbered 1, 2, 3, 4, 5 instead of 0, 1, 2, 3, 4. So (1,1)'s right. I started off with the right mindset, but once I got to the next row I was back to counting from 1. Honestly it's been too long since I did any programming. wink.png

 

- NKF

Link to comment
Share on other sites

What you are seeing is the optimized machine code. The Watcom compiler would have recognised the offset formula as consisting of a couple of constant values being added together. To help speed things up it stored the result rather than the full formula.

 

Hmm, possibly. I must say I never really examined the world of compilers :P. But it seems logical anyway, as it's a specific map spot. The location for the middle sections and the cockpits do change over their respective columns, depending on the RNG output, so it can't set a static value there.

 

However, the game code is the only thing I have to work with - and the only place where I can fix bugs :P. It would be absolutely amazing to have the (full) source code in C, but I guess that's a dreamers' dream. For now, I will settle with hex editing the .exe file and changing the byte it checks.

 

By the way, I ran into another bug yesterday. I started to get really curious about the unit placement in Alien Colonies, when I came across the 5th one in my current game. After reaching the second stage, I decided to take a look at the ROUTES.DAT file in the savegame directory. Even though the map was composed of 24 different map blocks (4 were 2x2, 20 were 1x1 -> total 36), the ROUTES.DAT only held information up to and including the block numbered 13h, which is 19 in decimals. It had a 2x2 block, which was counted as block 0Fh and it covered 2 of the blocks on the bottom row . As such, block 13h was located at (6, 5). I've verified the node locations in that block and can confirm they were indeed located in the block at (6, 5) (which is row 04 and column 05). So at least the last 4 blocks had no defined routes and nodes within the map as a whole. Somehow I suspect that when it determines the number of blocks present, it fails to account for the large blocks, or that it fails to account for the bottom row. Tonight I will generate a few more stage 2 maps and see if I can pinpoint the most likely cause.

 

The number of nodes was 241, by the way. This pretty much rules out an overflow issue; differentiating between the various types means that there are (far) less than 241 of any given type of node. The value fits within a byte and I assume they feed their RNG with nothing less than a byte. I'll see if I can make an analysis of alien ranks vs. occupied nodes. I'm beginning to suspect it simply doesn't randomize the spawn node selection for the aliens at all and just starts counting from spawn node 0 onward.

Link to comment
Share on other sites

Below is the map generation code for the Artefact stage 1 maptype (map type 9):

It is performed in two subroutines; first it sets the dimensions and the X-Com craft, then it fills the map blocks.

 

Special blocks: 00 (used as floor below craft), 09 (exit to stage 2)

Filler blocks, 1x1: 01, 02, 03, 04, 05, 06, 07, 08

Filler blocks, 2x2: 10, 11

 

Code legend

SS = Start of subroutine in the executable

ES = End of subroutine in the executable

AC = Attempt Counter for placement of map blocks

CC = Cycle Counter

OF = Offset value for the range

RA = Range for the randomizer (from 0 to the stated value)

ST = Specific Type (see comments)

The location is the hexadecimal offset from the start of the GeoScope.exe file.

The value listed is the value located at that particular offset.

The offsets are based on the DOS version of Terror from the Deep, v1.0.

 

Subroutine: Artefact stage 1 map initialisation

Code Location Value Comment

SS 42470h - -

ES 42854h - -

 

Step 1: initialisation of variables

Code Location Value Comment

ST 4248Eh 04 Z-dimension of the map.

ST 4249Dh 05 X and Y dimension of the map (so the map is always a square).

ST 424CBh FF Initialisation value for the map blocks.

ST 424CFh 01 Initialisation value for the RMP blocks.

ST 424F2h 19 Number of bytes to initialise for the map blocks and RMP blocks.

 

Step 2: placing the X-Com craft

Code Location Value Comment

ST 42512h 6E Is 110 in decimals and refers to the length of the CRAFT.DAT file.

ST 4251Eh 44 The byte to read from the CRAFT.DAT file for this craft. 44h refers to byte 68, the touchdown depth.

ST 42534h 09 The map type, which is used to call the next subroutine to generate the rest of the map.

End of subroutine: Artefact stage 1 map initialisation

 

Subroutine: Artefact stage 1 map generation

Code Location Value Comment

SS 44370h - -

ES 44567h - -

 

Step 1: placing the stage 2 exit block

Code Location Value Comment

ST 4440Bh 09 -

 

Step 2: determining the 2x2 blocks

Code Location Value Comment

ST 44384h 02 The Y-dimension of the map is lowered by this value (effectively taking out the bottom row of the map for the check to place the top row of a 2x2 block) to find valid Y-coordinates for 2x2 map blocks.

ST 44394h 02 Same for the X-dimenion.

RA 444CBh 01 -

OF 444DAh 0A -

AC 44513h 14 -

CC 44526h 02 -

 

Step 3: filling up the empty blocks with 1x1 blocks

Code Location Value Comment

RA 4453Ah 07 -

OF There is no explicit offset value. The instruction 'FE C0' at 44551h increases the random value by one.

End of Subroutine: Artefact stage 1 map generation

Link to comment
Share on other sites

Below is the map generation code for the Artefact stage 2 maptype (map type 10):

 

Special blocks: 04 (used as entrance zone blocks), 15 (map block with the Synomium Device)

Filler blocks, 1x1: 05, 06, 07, 08, 09, 10

Filler blocks, 2x2: 11, 12, 13, 14

Unused blocks: 00, 01, 02, 03, 16

 

Code legend

SS = Start of subroutine in the executable

ES = End of subroutine in the executable

AC = Attempt Counter for placement of map blocks

CC = Cycle Counter

OF = Offset value for the range

RA = Range for the randomizer (from 0 to the stated value)

ST = Specific Type (see comments)

The location is the hexadecimal offset from the start of the GeoScope.exe file.

The value listed is the value located at that particular offset.

The offsets are based on the DOS version of Terror from the Deep, v1.0.

 

Subroutine: Artefact stage 2 map generation

Code Location Value Comment

SS 42860h - -

ES 42E37h - -

 

Step 1: Initialising variables

Code Location Value Comment

ST 42870h 04 Map dimensions for all three coordinates: X, Y and Z

ST 42893h 0A The map type.

ST 428CFh FC Initialisation value for the map blocks.

ST 428D3h 01 Initialisation value for the RMP blocks.

ST 42902h 10 Number of bytes to initialise for the map blocks and RMP blocks.

 

Step 2: determining the location of the Synomium Device block

Code Location Value Comment

ST 4290Ch 02 The Y-dimension of the map is lowered by this value (effectively taking out the bottom row of the map for the check to place the top row of a 2x2 block) to find valid Y-coordinates for 2x2 map blocks. The same value is used for the X-coordinate as well.

ST 42926h 0F The map block that holds the Synomium Device

ST 42932h FF The map block value for the top right corner of that map block

ST 42934h FE The map block value for the bottom left corner of that map block

ST 42943h FD The map block value for the bottom right corner of that map block

 

Step 3: placing two entrance zones

Code Location Value Comment

ST 42945h 04 The value for the entrance zone map block

ST 42898h 10 Maximum position on the map block string for the entrance zones

ST 42972h 02 Number of entrance zone map blocks to place

 

Step 4: determining the 2x2 blocks

Code Location Value Comment

RA 429DAh 03 -

OF 429E6h 0B -

AC 42A09h 0A -

ST 42A22h 02 There is no absolute cycle value. Instead, the map dimension is lowered by this value to find the amount of cycles to try and place 2x2 map blocks.

ST 429DFh FF The map block value for the top right corner of that map block

ST 429EEh FE The map block value for the bottom left corner of that map block

ST 429F6h FD The map block value for the bottom right corner of that map block

Note: Contrary to other similar sections, the Y and X coordinate for this section are actually lowered by the number of entrance zones that have been placed. I assume that it's a "loan" of another variable that also holds the value 2, even if it's unrelated. A side effect is that if you would increase the number of entrance zones (or decrease!), the locations where 2x2 blocks get placed will change as well.

 

Step 5: filling up the empty blocks with 1x1 blocks

Code Location Value Comment

RA 42A37h 05 -

OF 42A56h 05 -

End ofSubroutine: Artefact stage 2 map generation

 

Personal note: one of the two main reasons I started this whole exploration was to see if I could increase the size of the Artefact Stage 2 missions; I feel they're too small, at 4x4 with only 2 levels for most of the map blocks. However, the fact that it assigns one and the same value to both X, Y and Z pretty much shatters that idea. I tried setting it to 5, but that completely messes up the map to unplayable result. The only way that I can see now is if I can somehow add a new function (in machine language tongue.png) to the .exe and refer to it in the spot where it assigns the variable to the Z-dimension. Then, in the called subroutine, I can simply assign a different value to the Z-dimension. There's a section filled with 0's between the code and the data embedded within the exe, so hopefully it will take on ... but I have no idea on the odds of succeeding there.

 

Update: Adding the subroutine didn't work, got a black screen as soon as I finished Stage 1. Then I tried to see if I could abuse some of the registers during the initialisation, to have one of them carry the Z-dimension; notably a register that would be overwritten shortly after anyway, so assigning it another value wouldn't affect anything else. As it is, the one I picked moves 2 bytes into a 1-byte address, so only one byte would be copied. Instead of assigning a 2-byte register CX to one byte address, I tried to see if I could assign CH to it instead and assign CL to the Z-dimension byte. However, the machine code to assign the CX byte is 7 bytes in length and the code to assign CH or CL is only 6 bytes - leaving me 2 bytes short in the .exe. I tried leaving them to 00, hoping they would simply be ignored, but that didn't work. I changed them to '40' (which means to increase the EAX register by 1) and lowered the starting count of EAX accordingly. This resulted in error messages that it couldn't allocate memory for the map, leaving me at a black screen. Looking at the GeoData.dat file, I see the Z-dimension is 0 ...

 

Edit: After putting some thought to it, I am starting to suspect it has something to do with setting some CPU flags, which happens when the code performs an increase of a register by 1. Maybe I can use a PUSH/POP combination on a register instead, as that leaves the flags untouched. Also, while it's hard due to the sound driver moving through CPU cycles like crazy, I may be able to use the DOSBox Debugger to check what it is actually doing.

 

Edit2: I've read up a bit in Intel's x86 programming guide and seen that opcode 00 actually means something. And rather than pushing and popping a value to and off the stack, it might be better to use opcode 90h, which is basically "do nothing" (except move the instruction pointer ahead to the next address).

Link to comment
Share on other sites

banana.gifbanana.gif YES! banana.gifbanana.gif

 

I actually managed to extend Artefact Stage 2 to a 5x5 map! But 6x6 should be possible just as well, although I didn't test that.

 

Below is the "hack" you need to perform to make it happen (offsets based on the DOS version of TFTD, v1.0):

 

Address 42870h holds the value '04h'. Change to '05h' for a 5x5 map, or '06h' for a 6x6 map.

Address 42902h holds the value '10h'. Change to '19h' for a 5x5 map, or '24h' for a 6x6 map.

Address 42898h holds the value '10h'. Change to '19h' for a 5x5 map, or '24h' for a 6x6 map.

 

I already listed those above, with the note that the address 42870h was also used to determine the Z value. The trick to extending X and Y to either 5 or 6 is to get Z defined by another register. For that purpose, I turned to the register ECX; it holds an unused byte during initialisation and shortly after initialisation (without getting used any further), it gets assigned some other values. The instructions below tell the game to make use of that unused byte and assign it to the value of Z:

 

Address 42885h holds the value '00h'. This is the unused byte to use for this purpose. Change it to '04h'.

(For the techies among us: this byte is assigned to the CH register, part of the ECX register.)

 

The address range 428AAh - 428B7h holds the following 14 bytes:

 

66 89 35 30 63 01 00 | 66 89 0D 9A 69 01 00

 

Change it to the following:

 

90 88 2D 30 63 01 00 | 90 88 0D 9A 69 01 00

 

The first three bytes of each of these two command instructions have changed. Save the changes and run the game for increased map size of the Artefact Stage 2 missions! smile.png

 

You don't have to worry about changing the value at 42885h. It gets overwritten with new values a few instructions further down in the subroutine.

 

For the techies among us:

I've separated the two commands these bytes represent by a |. I've also underlined the address reference of the two instructions. Reverse the byte order to find the addresses 16330h and 1699Ah. The first one is the byte reference to the Z dimension, the second one is irrelevant for this purpose, but it's the address that receives the 2-byte register CX (part of ECX, composed itself of CH and CL). The addresses have remained the same, but the 3 bytes preceding each have been changed. The OPCode '90h' basically tells the program to do "nothing". The instruction 88 2D tells the game to move the value held in register CH to the address that follows it in the instruction. The instruction 88 0D does the same, except then for the value in register CL.

 

Initially I tried to put the value '90h' behind the move command, but that royally botched the game. I suspect it has something to do with Word (2 bytes) and Double Word (4 bytes) boundaries, which were mis aligned when the 'NOP' OPCode was behind the move command. When I moved it in front of it, it worked like a charm smile.png.

Link to comment
Share on other sites

Below is the map generation code for the Ship Stage 1 maptype (map type 11 and 19):

 

Note: this subroutine generates the map for both ship types, even though the map types differ. Type is determined on map generation.

 

Special blocks: 00 (Cargo Ship top level), 00 and 01 (Passenger Liner)

Unused blocks: 02 (Passenger Liner). This is a variety of 01, with just a few changes to some of the stairs on the upper decks: they have handrails, preventing direct access to the stairs from the tiles to the left of those stairs. Also, 3 floor tiles in the casino room, hidden from view by a wall, have a different floor tile type.

 

Code legend

SS = Start of subroutine in the executable

ES = End of subroutine in the executable

AC = Attempt Counter for placement of map blocks

CC = Cycle Counter

OF = Offset value for the range

RA = Range for the randomizer (from 0 to the stated value)

ST = Specific Type (see comments)

The location is the hexadecimal offset from the start of the GeoScope.exe file.

The value listed is the value located at that particular offset.

The offsets are based on the DOS version of Terror from the Deep, v1.0.

 

Subroutine: Ship terror map generation

Code Location Value Comment

SS 41670h - -

ES 41B65h - -

 

Step 1: Initialisation

Code Location Value Comment

ST 41690h 04 The Z-dimension of the ship maps

ST 4169Ah 07 The Y-dimension of the ship maps

ST 4169Fh 03 The X-dimension of the ship maps

ST 416A4h 01 Range of the randomizer to determine the specific ship type. If the random value is 0, it's a Passenger Liner. Any other value yields the Cargo Ship.

ST 416CEh 0B Map type used for the Cargo Ship version of the Ship Terror site.

ST 416D9h 13 Map type used for the Passenger Liner version of the Ship Terror site.

ST 4170Eh FF Filler value for the map block section of the GeoData.dat file.

ST 41710h 01 Filler value for the RMP block section of the GeoData.dat file.

Note: to fill the GeoData.dat map and RMP sections, it computes the number of bytes required by multiplying the X and Y coordinates of the map.

 

Step 2: filling up the GeoData.dat file with relevant information

Code Location Value Comment

ST 41743h 6E The length of the CRAFT.DAT file, used to determine the craft type of the X-Com craft for this mission.

ST 4177Ch 01 X-coordinate of the X-Com craft in map block size; the Y-coordinate is 0, achieved by clearing a specific register elsewhere in the code.

ST 417CCh 0A Multiplier for the Y-coordinate of the X-Com craft to get the tile location instead of map block location. Since Y is always 0, they probably just copied the placement part from elsewhere, where it could be random, and did not alter it.

ST 417FCh 0A Multiplier for the X-coordinate of the X-Com craft, to get the tile location instead of map block location. Since X is always 1, they probably just copied the placement part from elsewhere, where it could be random, and did not alter it.

Note: a further hint that they copied the placement code is the fact that there's a conditional check in this part of the code to see if an X-Com craft is being placed or a USO.

ST 4183Ch FE Filler used to fill up the map block section in the GeoData.dat file.

ST 4185Ch 15 Number of times to put the value into the map block section. Covers the entire ship (7x3 = 21 map blocks).

ST 41867h 0B Map type is being checked against this value, to determine which map block sections to fill in.

ST 4186Ah 30 Combined with the next byte, 'ED', this instruction sets a specific register to 0, for the Cargo Ship. This is then being written to byte 13 of the GeoData.dat file.

ST 41875h 01 Map block number for the aft section of the Passenger Liner (composed of 2 different map blocks). This is being written to byte 13 of the GeoData.dat file.

ST 41876h 30 Combined with the next byte, '09', this instruction sets a specific register to 0, for the bow section of the Passenger Liner. This is then being written to byte 28 of the GeoData.dat file.

End ofSubroutine: Ship terror map generation

Link to comment
Share on other sites

Below is the map generation code for the Ship Stage 2 maptype (map type 11 and 20):

 

Note: this subroutine generates the map for both ship types, even though the map types differ. Type is passed down from the previous mission. Two checks differentiate between specific values for both Ship types.

 

Special blocks: 01 and 02 (Cargo Ship) or 02 and 03 (Passenger Liner)

 

Code legend

SS = Start of subroutine in the executable

ES = End of subroutine in the executable

AC = Attempt Counter for placement of map blocks

CC = Cycle Counter

OF = Offset value for the range

RA = Range for the randomizer (from 0 to the stated value)

ST = Specific Type (see comments)

The location is the hexadecimal offset from the start of the GeoScope.exe file.

The value listed is the value located at that particular offset.

The offsets are based on the DOS version of Terror from the Deep, v1.0.

 

Subroutine: Ship terror map generation

Code Location Value Comment

SS 41B70h - -

ES 4201Eh - -

 

Step 1: Initialisation

Code Location Value Comment

ST 41B8Fh 04 The Z-dimension of the ship maps

ST 41B99h 07 The Y-dimension of the ship maps

ST 41B9Eh 03 The X-dimension of the ship maps

ST 41BC1h 0B Value against which the map type is being checked (0B is the value for the Cargo Ship).

ST 41BCBh 14 If it's the Passenger Liner, this value is assigned to the map type instead.

ST 41C09h FE Filler value for the map block section of the GeoData.dat file.

ST 41C0Bh 01 Filler value for the RMP block section of the GeoData.dat file.

Note: to fill the GeoData.dat map and RMP sections, it computes the number of bytes required by multiplying the X and Y coordinates of the map.

 

Step 2: filling up the GeoData.dat file with relevant information

Code Location Value Comment

ST 41C28h 0B Map type is being checked against this value, to determine which map block sections to fill in.

ST 41C31h 01 If it's the Cargo Ship, this value is written to byte 13 of the GeoData.dat file.

ST 41C38h 02 If it's the Cargo Ship, this value is written to byte 28 of the GeoData.dat file.

ST 41C41h 02 If it's the Passenger Liner, this value is written to byte 13 of the GeoData.dat file.

ST 41C48h 03 If it's the Passenger Liner, this value is written to byte 28 of the GeoData.dat file.

End ofSubroutine: Ship terror map generation

 

Concluding note: The development of the two Ship types is somewhat strange, despite having identical sizes:

- The Cargo Ship stage 1 consists of a single map block, whereas the Passenger Liner stage 1 is split across 2 map blocks;

- The Cargo Ship stage 2 is defined within the same map type, whereas the Passenger Liner stage 2 is split across 2 map types;

- Despite these two differences, they did try to combine the map creation within the same two subroutines.

Whatever transpired when they designed these two ship missions is something we may never know, but something changed during development, that's for sure.

Link to comment
Share on other sites

Below is the map generation code for the Port maptype (map type 12):

 

Special blocks: 00, 01 (used as floor below craft), 20 (2x2 map block with rails, can only go into the top row)

Filler blocks, 1x1: 10, 11, 12, 13, 14, 15, 16

Filler blocks, 2x2: 17, 18, 19

Coastal blocks: 02, 03, 04, 05, 06, 07, 08, 09

 

Code legend

SS = Start of subroutine in the executable

ES = End of subroutine in the executable

AC = Attempt Counter for placement of map blocks

CC = Cycle Counter

OF = Offset value for the range

RA = Range for the randomizer (from 0 to the stated value)

ST = Specific Type (see comments)

The location is the hexadecimal offset from the start of the GeoScope.exe file.

The value listed is the value located at that particular offset.

The offsets are based on the DOS version of Terror from the Deep, v1.0.

 

Subroutine: Port Terror map generation (initialisation)

Code Location Value Comment

SS 42020h - -

ES 42460h - -

 

Step 1: Initialising variables

Code Location Value Comment

ST 42040h 04 The Z-dimension of the map

ST 42051h 05 The X- and Y-dimension of the map.

ST 42072h FF Filler value for the map block section of the GeoData.dat file.

ST 4208Ah 01 Filler value for the RMP block section of the GeoData.dat file.

ST 420B2h 19 Number of bytes to overwrite with the two filler values.

ST 420D9h 6E Size of the CRAFT.DAT file. Used to find the craft type of the X-Com craft.

Note 1: at this point, the game places the X-Com craft, by calling the subroutine that places the first craft on the map.

Note 2: there is some interesting code following it. After placing it, it checks to see if the placement was on column 04, the one that holds the coastal tiles. If so, it will call the placement subroutine again, after which the coastal line will be checked again. The code will only move on once it finds the X-Com craft on another column than column 04.

Note 3: the coastal line check and possible call to the placement subroutine also has some code to initialise the GeoData.dat map block and RMP block array. However, a conditional evaluation that always evaluates to true skips over it. Possible leftover from copied code from elsewhere.

Note 4: the way the game determines the coastal line blocks is pretty much hard-coded. The instruction '8D 14 92' at address 420FBh-420FDh tells the CPU to shift the byte register being checked by 5. With a map dimension of 5 in the X-direction, this simply moves to the next block below the one that was checked. It starts at block (4, 0), which is the coastal block in the top right corner, but that block is pointed to by performing the shift once already.

ST 42142h 6E Size of the CRAFT.DAT file. Used to find the craft type of the X-Com craft (used in possible relocation of the X-Com craft).

Note: after this, the game calls the placement routine again for the first craft on the map.

 

Once it succeeds in placing the X-Com craft (on a column other than the coastal line), the code calls another subroutine to build the rest of the map.

End ofSubroutine: Port Terror map generation (initialisation)

 

Subroutine: Port Terror map generation (filling map blocks)

Code Location Value Comment

SS 44570h - -

ES 44774h - -

 

Step 1: filling the coastal blocks in column 04

Code Location Value Comment

ST 44579h 01 Row counter. The top row is row 1 for this purpose. Used to find the coastal blocks in the map grid.

RA 4458Fh 07 There are 8 different coastal map blocks.

OF 445A7h 02 -

ST 445B2h 06 The game loops 5 times through the code, each time shifting down one row. Each loop it randomly picks one of the Coastal map blocks to put on that row. The loop counter starts at 1, so after 5 loops, it equals 6. The loop counter is checked against this value to see if it needs to break from it.

 

Step 2: Filling in block 20 in the top row

Code Location Value Comment

ST 445C6h 02 Range on the X-coordinate where map block 20 may be positioned.

Note: after finding the random value in the range [0-2], it determines to see if the 2x2 block on the map is still unoccupied. Note that only the X-Com craft can block it at this point. If the location is available, it will proceed to place the map block. Only 1 check is made.

ST 44617h 14 The map block number to be placed (14h = 20 in decimals)

ST 44626h FE Value for the three other corners of map block 20 for the GeoData.dat bytes.

 

Step 3: determining the 2x2 blocks

Code Location Value Comment

ST 44650h 02 The Y-dimension of the map is lowered by this value (effectively taking out the bottom row of the map for the check to place the top row of a 2x2 block) to find valid Y-coordinates for 2x2 map blocks.

ST 4465Dh 02 Same for the X-dimenion. Note that at this point, another instruction has already lowered the effective range of X by one, to account for the Coastal line on the right hand side of the map.

RA 446D9h 02 -

OF 446E9h 11 -

AC 44721h 14 -

CC 44733h 02 -

 

Step 4: filling up the empty blocks with 1x1 blocks

Code Location Value Comment

RA 44747h 06 -

OF 4475Fh 0A -

End ofSubroutine: Port Terror map generation (filling map blocks)

Link to comment
Share on other sites

Below is the map generation code for the Island Terror maptype (map type 13):

 

Special blocks: 00, 01 (used as floor below craft)

Filler blocks, 1x1: 08, 09, 10, 11, 12, 13, 14

Filler blocks, 2x2: 02, 03

Coastal blocks: 04, 05 (both 2x2), 07 (1x1)

Unused block: 06

 

Code legend

SS = Start of subroutine in the executable

ES = End of subroutine in the executable

AC = Attempt Counter for placement of map blocks

CC = Cycle Counter

OF = Offset value for the range

RA = Range for the randomizer (from 0 to the stated value)

ST = Specific Type (see comments)

The location is the hexadecimal offset from the start of the GeoScope.exe file.

The value listed is the value located at that particular offset.

The offsets are based on the DOS version of Terror from the Deep, v1.0.

 

Subroutine: Island Terror map generation (initialisation)

Code Location Value Comment

SS 41290h - -

ES 41662h - -

 

Step 1: Initialising variables

Code Location Value Comment

ST 412ADh 04 The Z-dimension of the map

ST 412B9h 05 The X- and Y-dimension of the map.

ST 412BEh FF Filler value for the map block section of the GeoData.dat file.

ST 412D7h 01 Filler value for the RMP block section of the GeoData.dat file.

ST 41312h 19 Number of bytes to overwrite with the two filler values.

ST 41332h 6E Size of the CRAFT.DAT file. Used to find the craft type of the X-Com craft.

ST 4133Ah 03 Re-dimension of the Y-coordinate. This makes sure the X-Com craft is not getting placed on the two bottom rows, which hold the coastline map blocks. Note that this also explicitely nullifies (2, 2) as a valid placement for the aft section of the X-Com craft, as it would put the nose at (2, 3), which is outside the map boundaries with this re-dimension, even though (2, 3) is the randomly selected block held between the two 2x2 coastline blocks. In fact, this makes sure the X-Com craft will always have either its aft section or its nose on the second row of the map.

ST 4133Fh 05 Rather than storing the original Y-coordinate in a variable, it's redefined again by this value. It replaces the re-dimensioned Y-coordinate after the X-Com craft has been placed.

Note: at this point, the game places the X-Com craft, by calling the subroutine that places the first craft on the map.

After replacing the Y-coordinate, setting it to 5 again, the subroutine to build the rest of the map is called.

End of Subroutine: Island Terror map generation (initialisation)

 

Subroutine: Island Terror map generation (filling map blocks)

Code Location Value Comment

SS 44780h - -

ES 44900h - -

 

Step 1: filling the coastal blocks in rows 03 and 04

Code Location Value Comment

ST 4479Dh 01 Used to determine the map block in the bottom left corner of the map. The random number generator uses this to determine a value of either 0 or 1.

ST 447ACh 04 The outcome is raised by this value (effective range is [04 - 05] as a result). This is written to byte 28 of the GeoData.dat array (which is position (0, 3) on the map).

ST 447AEh FE Filler value for the 3 off-corners of both coastal 2x2 map blocks, which is then written to the 6 corresponding bytes of the GeoData.dat array.

ST 447DBh 01 The value of the map block found for the bottom left corner is "XOR'ed" against the value 1. Essentially, this flips the least significant bit from 0 to 1 or from 1 to 0 - which causes 4 to turn into 5 and vice versa. In other words, if map block 04 is in the bottom left corner, it will place 05 in the bottom right corner. If 05 is bottom left, 04 will be put in the bottom right. The value is written to byte 31 of the GeoData.dat array (which is position (3, 3) on the map).

Important note: should you want to increase the random number range, make sure the value is odd, not even, so the outcome of the RNG is in a range with an even amount of values. Corners will always be paired this way, with even numbers always appearing together with the odd numbers directly following it, due to the XOR operation on the least significant bit.

RA 447DDh 06 Range of the 1x1 blocks, used in this case to determine the map block number of the block on row 03 (map coordinate (2, 3)) between the two 2x2 coastal corners.

OF 447EDh 08 Once the block has been determined, it is written to byte 30 of the GeoData.dat array.

ST 447FCh 07 Map block number that's between the two 2x2 coastal corner blocks on row 04 (map coordinate (2, 4)). It is written to byte 35 of the GeoData.dat file.

 

Step 2: determining the 2x2 blocks

Code Location Value Comment

ST 4480Bh 01 Range in the Y-direction to place 2x2 blocks. Since the coastal blocks already take off 2 rows and the bottom row of the 3 remaining is invalid for the top row of 2x2 blocks, only row 0 and row 1 remain. This value refers to row 1.

ST 447FFh 02 Not really an offset, but rather the value by which the X-coordinate is effectively lowered to find valid X-coordinates for the topleft section of 2x2 blocks.

RA 4486Eh 01 -

OF 4487Dh 02 -

AC 448BBh 14 -

Note: there is no cycle counter. Effectively, this means that the subroutine only attempts (20 times) to place one additional 2x2 block on the map, besides the two corners along the coast. The rest will be filled up with 1x1 blocks.

 

Step 3: filling up the empty blocks with 1x1 blocks

Code Location Value Comment

RA 448D3h 06 -

OF 448EBh 08 -

End of Subroutine: Island Terror map generation (filling map blocks)

Link to comment
Share on other sites

Below is the map generation code for the Alien Colony stage 1 maptype (map type 21):

 

Special blocks: 00, 01, 02, 03, 04, 05, 06, 07, 08 (map is static)

 

Code legend

SS = Start of subroutine in the executable

ES = End of subroutine in the executable

AC = Attempt Counter for placement of map blocks

CC = Cycle Counter

OF = Offset value for the range

RA = Range for the randomizer (from 0 to the stated value)

ST = Specific Type (see comments)

The location is the hexadecimal offset from the start of the GeoScope.exe file.

The value listed is the value located at that particular offset.

The offsets are based on the DOS version of Terror from the Deep, v1.0.

 

Subroutine: Alien Colony stage 1 map generation

Code Location Value Comment

SS 3FAF0h - -

ES 3FF40h - -

 

Step 1: Initialising variables

Code Location Value Comment

ST 3FB11h 04 The Z-dimension of the map

ST 3FB16h 15 Map type for Alien Colony stage 1.

ST 3FB25h 06 X- and Y-dimensions of the map

ST 3FB2Ah FE Filler value for the map block section of the GeoData.dat file.

ST 3FB4Ah 01 Filler value for the RMP block section of the GeoData.dat file.

ST 3FB82h 24 Number of bytes to overwrite with the two filler values.

ST 3FBA4h 6E Size of the CRAFT.DAT file. Used to find the craft type of the X-Com craft.

ST 3FBD5h 03 Used to determine a random value in the range [0 - 3]. The outcome is used to place the X-Com craft in one of four possible locations on the map.

ST 3FBECh 01 It performs a check on the random value. If it equals 0, the value for the X-offset is set to this value. The Y-offset is set to the random value. This is the top left quadrant of the map.

ST 3FBFCh 01 Random value is checked against this.

ST 3FC03h 04 If the random value is 1, the X-offset is set to this value. The value for the Y-offset is set equal to a cleared register (i.e. holds the value 0). This is the top right quadrant of the map.

ST 3FC13h 02 Random value is checked against this.

ST 3FC18h 04 If the random value is 2, the Y-offset is set to this value.

ST 3FC1Dh 01 If the random value is 2, the X-offset is set to this value. This puts the X-Com craft in the bottom left quadrant of the map.

ST 3FC2Dh 03 Random value is checked against this.

ST 3FC32h 04 If the random value is 3, both the Y-offset and the X-offset are set to this value. This is the bottom right quadrant of the map.

ST 3FC8Dh 0A Multiplier for the Y-offset to find the exact Y-coordinate of the X-Com craft in tiles. The multiplied value is written to byte 86 of the GeoData.dat array.

ST 3FCBCh 0A Same, but then for the X-offset; written to byte 87 of the GeoData.dat array.

 

Step 2: building the map

Code Location Value Comment

Note: the game will then loop through some code to write the Colony map blocks to the GeoData.dat array. Three counters have been set to zero by clearing the corresponding registers at this point: EAX, EDX and ESI.

ST 3FCF8h 02 The first one, EAX, keeps track of the offset in the GeoData.dat array. Everytime it writes a map block to it, it is then raised by this value.

ST 3FD06h 03 The second one, EDX, counts how many blocks have been placed on a given row. Since the map is 6x6 and filled with 2x2 blocks, it has to move to the next set of rows once 3 such blocks have been placed. This value is compared to the value of EDX.

ST 3FD0Bh 06 Once EDX reaches the value of 3, it is reset to 0, ESI is increased by 1 (see below) and EAX is increased by this value. This is because placing the 2x2 blocks automatically fills 2 rows, not 1. It has to skip the second of those rows. Adding this value does just that.

ST 3FD12h 03 The third counter, ESI, counts the number of rows that have been filled with 2x2 blocks. Since there are 3 rows of 2x2 blocks possible in a 6x6 map, the subroutine has to break from placing them once the third row of 2x2 blocks has been filled.

Note: the actual value assigned to the GeoData.dat array is coming from a fourth counter. However, no hard values are assigned to it; instead, it is reset to 0 at the start by having its register cleared and then increased by 1 through an operand. As such, the map block values are hard coded by the loop number.

End of Subroutine: Alien Colony stage 1 map generation

Link to comment
Share on other sites

Below is the map generation code for the Alien Colony stage 2 maptype (map type 14):

 

Special blocks: 00 (used as entrance zone blocks), 15 (map block with the Synomium Device)

Filler blocks, 1x1: 01, 02, 03, 04, 05, 06, 07, 08, 09

Filler blocks, 2x2: 11, 12, 13, 14

Unused block: 10

Note: map block 10 holds an exit zone. Could this mean they originally intended Colony assaults to cover 3 stages? Or is it a left-over from a time when T'Leth stage 2 was possibly built with the same subroutine?

 

Code legend

SS = Start of subroutine in the executable

ES = End of subroutine in the executable

AC = Attempt Counter for placement of map blocks

CC = Cycle Counter

OF = Offset value for the range

RA = Range for the randomizer (from 0 to the stated value)

ST = Specific Type (see comments)

The location is the hexadecimal offset from the start of the GeoScope.exe file.

The value listed is the value located at that particular offset.

The offsets are based on the DOS version of Terror from the Deep, v1.0.

 

Subroutine: Alien Colony stage 2 map generation

Code Location Value Comment

SS 3FF50h - -

ES 404D9h - -

 

Step 1: Initialising variables

Code Location Value Comment

ST 3FF60h 06 X- and Y- dimension of the map

ST 3FF74h 04 Z-dimension of the map

ST 3FF79h 0E The map type.

ST 3FFBBh FC Initialisation value for the map blocks.

ST 3FFC2h 01 Initialisation value for the RMP blocks.

ST 3FFE2h 24 Number of bytes to initialise for the map blocks and RMP blocks.

 

Step 2: determining the location of the Synomium Device block

Code Location Value Comment

ST 40006h 0F The map block that holds the Synomium Device

ST 40012h FF The map block value for the top right corner of that map block

ST 40014h FE The map block value for the bottom left corner of that map block

ST 40029h FD The map block value for the bottom right corner of that map block

 

Step 3: placing the two entrance zones

Code Location Value Comment

ST 4003Bh FC Value being used to check the random position against

ST 3FF88h 24 Maximum position on the map block string for the entrance zones

ST 40050h 02 Number of entrance zone map blocks to place

Note: the entrance zones map block (00) is not referred to explicitely. Instead, the value 0 is achieved by clearing a register. As such, the map block number for entrance zones is hardcoded.

 

Step 4: determining the 2x2 blocks

Code Location Value Comment

RA 400B4h 03 -

OF 400C0h 0B -

AC 400E3h 0A -

CC 400F7h 03 -

ST 400AAh FF The map block value for the top right corner of that map block

ST 400C8h FE The map block value for the bottom left corner of that map block

ST 400D0h FD The map block value for the bottom right corner of that map block

Note: Contrary to other similar sections, the Y and X coordinate for this section are actually lowered by the number of entrance zones that have been placed. I assume that it's a "loan" of another variable that also holds the value 2, even if it's unrelated. A side effect is that if you would increase the number of entrance zones (or decrease!), the locations where 2x2 blocks get placed will change as well.

 

Step 5: filling up the empty blocks with 1x1 blocks

Code Location Value Comment

ST 4010Fh 64 An interesting section, that gives a specific map block a special treatment. See the note below for the details. The value 64h equals 100 in decimals.

ST 40133h 3C This value is used for that purpose too. The value 3Ch equals 60 in decimals.

ST 4013Dh 07 Map block number that receives this special treatment.

RA 4010Ah 08 -

OF There is no explicit offset value. The instruction 'FE C0' at 40147h increases the random value by one.

Note: Map block 07 gets a special treatment here! The game performs a random number generation with a range of [0 - 100]. It then compares it against the second value, 60. If the random value is equal to or below this number, the game will determine a random 1x1 block in the range of [0 - 8] to be placed on the spot in question. However, if the random value is higher than 60, it will place map block 07 there. Since the range [0 - 100] effectively covers 101 values, the chance to find a map block 07 on any of the 1x1 map block positions is 39.6%. When it randomly determines the map block number, 07 is in that range too, so the total value is higher. Across a range of 9 possible blocks, 07 takes up 11.11% of the selection. Taking 11.11% from 61 gives another 6.8%, raising the total odds for any of the 1x1 blocks to be of type 07 to 46.4%. This special treatment explains why Alien Colony stage 2 missions have so many of those 07 map blocks. Apparently, the developers felt that Alien Colonies should have a lot of Ion Beam Accelerators in their basement.

End of Subroutine: Alien Colony stage 2 map generation

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
  • Create New...