Procedurally generated maps - algorithm?


  • Please log in to reply
60 replies to this topic

#21 Maurice76

Maurice76

    Sergeant

  • Members
  • PipPipPip
  • 77 posts

Posted 21 August 2015 - 07:45 AM

I guess you are right, NKF, as you have been before ;). I am still in the process of mapping out the subroutines myself. Yesterday evening, just before I stopped for the night, I managed to identify the various branches of the switch-statement I encountered. The switch statement compares a specific value against the decimal value 14, but the code that followed was marked as "data" by IDA 5.0. After converting it to code, I found only 12 cases belonging to the switch statement, so that puzzled me somewhat. But after scanning through the subroutines called within each of those cases, I could identify them and understand why there were only 12 to begin with: it referred to all the random top side maps that the game knows and the final one is the Island Terror site (at value 13, so anything higher would be skipped by the switch statement). But from 0 (Seabed) to 13 (Island Terror), there are two maps that drop out, namely the GRUNGE map (at 10) that follows ALART (at 9) and the Ocean Liner (at 11).

I can understand the omission of the Ocean Liner, as it's a static map, but the omission of the GRUNGE map was a bit surprising. Either it is handled outside the switch statement, or it's not even handled by the GeoScape.exe, but rather by the Tactical.exe. I haven't opened that one yet, so I don't know which subroutines are in there. I do plan to open it, though, if for nothing else than to figure out why pretty much all the aliens in the Alien Colony stage 2 are placed in the top row of the map blocks (except for the 3 Lobsterman Commanders, who are always at the Synomium Device). Somehow, I am suspecting a bug in either the randomizer not covering the entire range of possible spawnspots, or an overflow error on the total number of available spawnspots.

Anyway, now that I've been able to identify the purpose of various subroutines, I'm really starting to make progress in understanding them and identifying the key values within each.

#22 Zombie

Zombie

    Mr. Grognard of X-COM

  • Admin
  • PipPipPipPipPipPip
  • 5,545 posts
  • Gender:Male
  • Location:Wisconsin, USA

Posted 21 August 2015 - 03:29 PM

View PostMaurice76, on 21 August 2015 - 07:45 AM, said:

I can understand the omission of the Ocean Liner, as it's a static map, but the omission of the GRUNGE map was a bit surprising. Either it is handled outside the switch statement, or it's not even handled by the GeoScape.exe, but rather by the Tactical.exe. I haven't opened that one yet, so I don't know which subroutines are in there. I do plan to open it, though, if for nothing else than to figure out why pretty much all the aliens in the Alien Colony stage 2 are placed in the top row of the map blocks (except for the 3 Lobsterman Commanders, who are always at the Synomium Device). Somehow, I am suspecting a bug in either the randomizer not covering the entire range of possible spawnspots, or an overflow error on the total number of available spawnspots.

Not sure I understand you. Do you mean top Level (of the modules) or top Row (of all the modules which make up a stage 2 colony map)? Typically this would be North on the compass rose but it's the upper right in the game. There appears to be plenty of spawn points available for the aliens from what I can see... so I dunno what's wrong there. However, in your first sentence you are talking about the GRUNGE map not showing up, which in the game is the Alien Artifact Site 2nd stage. Which is it: Colony (base) or Artifact?Posted Image

As I said, an alien Colony appears to have plenty of spawn points for the aliens. In the GRUNGE maps, there are few (if any) spawn points in the small 10x10 modules (and their spawn priority is almost always 1 which is rock bottom). However, in the big 20x20 modules there are many. If you are talking about the Artifact site, then perhaps the game is only placing the larger 20x20 map blocks in the top row for some reason. That's the only thing I can come up with at the moment. Posted Image

- Zombie

My X-COM Patch Kit For UFO Defense | Emergency XCOM Meeting spoof on YouTube




JellyfishGreen said:

Zombie: Empirical data's your only man, when formulating a research plan.
A soldier's death is never in vain if it makes the formula more plain.
A few dozen make a better case for refining that third decimal place.
They call me Zombie because I don't sleep, as I slowly struggle to climb this heap,
of corpses, data points, and trials, but from the top - I'll see for miles!

#23 Maurice76

Maurice76

    Sergeant

  • Members
  • PipPipPip
  • 77 posts

Posted 21 August 2015 - 04:57 PM

View PostZombie, on 21 August 2015 - 03:29 PM, said:

Not sure I understand you.

Sorry for the confusion. I was at work when I wrote that and in a bit of a hurry Posted Image.

Anyway, I tried to say two things:
- First of all, I encountered a switch statement, which seems to take care of random maps. I've found subroutines for the USO sites (0 through 8), Artefact top side, Port Terror and Island Terror. If you keep the listing as given on the UfoPedia in mind (bytes 10 and 11 in the GeoData.dat file), you'll notice that Cargo Ship is missing, but so is Grunge. That the Cargo Ship is missing from that list is logical, as it's a set map, but Grunge in random. So whatever subroutine is used to build the Grunge map, it's not passing through the switch statement.

In the mean time I've also found the subroutine that builds T'Leth top level, which has the Grunge looks. That one, too, is random. It first places mab block 00 in each of the four corners, then randomizes the location for the exit block (block 03), after which it randomizes the location for both entrance blocks (01 and 02, both appear once). After that, it will make 20 attempts to place 2x2 blocks, breaking from that loop if it managed to place 3 of them. Then it fills the remainder with 1x1 blocks.

- The second thing I tried to say is that I usually find the aliens on Colony stage two mission to be in map blocks at the north of the alien base. When you look on the minimap, it's the map blocks in the top left corner. Usually all aliens are spread out across a given map, but in that particular mission, I always find them in the northern row, usually in the first 3 to 4 blocks when counting from the left side of the minimap. The presence of a 2x2 block in that top row usually spreads them a little further. The only exception are the Commanders, which are always near the Synomium Device - probably by virtue of the spawnpoints there being the only ones where they can spawn.

I am wondering if that clustering of aliens in the northern row of map blocks (especially towards the left side of that row) is caused by a typo in the randomizer that determines the spawnspots that will hold an actual alien, or if the value it feeds to the randomizer is affected by an overflow. I suspect that if the number of spawnpoints exceeds 255, it will omit those spawnpoints. Let's say a typical Colony stage two mission has 300 spawnpoints if you count them across all the used mapblocks; if it discards 256 due to byte overflow, the effective number the game would use to spawn aliens is the remainder, which is some ~45 spawnpoints. Counting from left to right and top to bottom, it finds those all in the top row, towards the left, unable to even use the other ones - which results in a virtually empty base, except for those few blocks. I haven't done any checking on what's going on there, but I intend to once I feel I am done with the map generation Posted Image.

By the way, I've found the determination for the Seabed, Coral Reef or base type selection. It performs a random generation of a number between 0 and 3, which it then checks. If it's less than 2 (i.e., 0 or 1) it will use the type assigned to it based on the location of the worldmap, if it's equal to 2 it will use the Seabed maptype regardless of what was there before, and if it's 3, it will use Coral Reef instead of what was there before.

So, for any given maptype used for USO sites, there is a 50% chance to use the maptype based on geographical location, 25% chance on Seabed and 25% chance on Coral Reef. If the basetype was already Seabed or Coral Reef, odds for them increases to 75% and 25% for the other one.

#24 Maurice76

Maurice76

    Sergeant

  • Members
  • PipPipPip
  • 77 posts

Posted 21 August 2015 - 09:43 PM

View PostMaurice76, on 20 August 2015 - 08:17 PM, said:

--- It will break out of its attempts to place such a 2x2 block when it meets either of the following two requirements: it has placed two of those 2x2 blocks (excluding the exit block), and/or it has attempted to place them 20 times.

Slight alteration to the above:

The game will cycle 2 times through attempts to place a 2x2 block on the Artefact site. Each cycle will run until either it succeeded, or until 20 attempts have been made, whichever comes first. At the start of the next cycle, the number of attempts is reset to 0 again, so in theory the game could make 40 attempts across 2 cycles if it keeps failing. After 2 cycles, the game moves on to placing the 1x1 tiles on the yet unoccupied tiles.

I've also found Colony Stage 2 in the GeoScape.exe file; it runs a slightly different loop. After placing the block with the Synomium Device and two entrance blocks, it will make 3 cycles of at most 10 attempts each to place a 2x2 block on the map.

#25 Maurice76

Maurice76

    Sergeant

  • Members
  • PipPipPip
  • 77 posts

Posted 23 August 2015 - 11:56 AM

View PostNKF, on 21 August 2015 - 06:47 AM, said:

It's certainly the case with most of the unique missions like the port, island and artefact sites. The more general everyday missions however has a separate routine just for the troop transport that essentially does the same thing but also attempts to set the troop transport away from the alien sub.

- NKF

I've been going over this, but I am finding something different. In every case, the X-Com craft is placed first. On USO recovery mission, the USO is placed next, but the placement routine loads up the location of the X-Com craft and keeps it in mind when trying to place the USO. The strange thing is that both subroutines end with an IF-statement that selects between the craft being placed to be an X-Com craft or a USO, even though it's pretty clear that the first subroutine only handles the X-Com craft, and the second one handles the USO. Seeing also how the source code of both subroutines share a lot of the same operations (with the second one having a bit more in virtue of loading up the X-Com craft position as well, plus a few deviations from the first subroutine at spots), I suspect they originally designed it all to be handled within one subroutine, but ran into some complexities with regards to the USO placement. Rather than to create a difficult construction, they decided to simply split it into two subroutines, by simply copying the code and altering the second one to accomodate knowledge of the X-Com craft. They left some redundant code, like the IF-statement at the end that selected between the two types.

The subroutine to place the X-Com craft is called by four subroutines: one that handles the generation of Port Terror sites, one that handles the Island Terror sites, one that handles the Artefact sites and the fourth one handles USO recovery missions (map type is not considered at that point for those missions). The USO recovery mission then calls on a subroutine to place the USO on the map. Each of those four subroutines then call on a further subroutine that distinguishes between the 12 Stage 1 random map types (9 USO recovery, Port, Island, Artefact) to call the specific subroutine for that map type, to fill out the map with the various map blocks.

Edit: I stand corrected! The code was quite hard to read, but now I see what it does. There are two subroutines that place a craft on the map. The first one will place a USO if present, or the X-Com craft otherwise. The second one is only called upon when a USO is present and will then place an X-Com craft as the second craft on the map. I did identify that the second subroutine keeps the location of the craft placed with the first subroutine in mind, I just didn't see the switch-over on craft type when it handles a USO recovery mission. This means the IF-statement I mentioned above makes sense for the first subroutine. Not sure why it's present as well in the second one, other than that it was a leftover from copying the subroutine.

#26 Maurice76

Maurice76

    Sergeant

  • Members
  • PipPipPip
  • 77 posts

Posted 23 August 2015 - 08:17 PM

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.


#27 Maurice76

Maurice76

    Sergeant

  • Members
  • PipPipPip
  • 77 posts

Posted 23 August 2015 - 09:15 PM

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).

#28 Maurice76

Maurice76

    Sergeant

  • Members
  • PipPipPip
  • 77 posts

Posted 24 August 2015 - 06:00 PM

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.

#29 Maurice76

Maurice76

    Sergeant

  • Members
  • PipPipPip
  • 77 posts

Posted 24 August 2015 - 06:42 PM

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


#30 Maurice76

Maurice76

    Sergeant

  • Members
  • PipPipPip
  • 77 posts

Posted 24 August 2015 - 09:03 PM

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.

#31 NKF

NKF

    Mr. Badger in disguise

  • Site Staff
  • PipPipPipPipPip
  • 4,426 posts
  • Gender:Male
  • Location:In my mind

Posted 25 August 2015 - 07:02 AM

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. Posted Image

- NKF
NKF, narrow minded fuddy duddy who refuses to let go of the past and will not accept anything newer than 1979.

#32 Maurice76

Maurice76

    Sergeant

  • Members
  • PipPipPip
  • 77 posts

Posted 25 August 2015 - 09:54 AM

View PostNKF, on 25 August 2015 - 07:02 AM, said:

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.

Quote

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.

#33 Maurice76

Maurice76

    Sergeant

  • Members
  • PipPipPip
  • 77 posts

Posted 25 August 2015 - 05:23 PM

View PostNKF, on 25 August 2015 - 07:02 AM, said:

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 Posted Image. But I don't think your assumption is correct - of course, I don't know which source you are using Posted Image. 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:
Posted Image
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.

#34 Maurice76

Maurice76

    Sergeant

  • Members
  • PipPipPip
  • 77 posts

Posted 25 August 2015 - 06:07 PM

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.

#35 Maurice76

Maurice76

    Sergeant

  • Members
  • PipPipPip
  • 77 posts

Posted 25 August 2015 - 06:23 PM

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.

#36 Maurice76

Maurice76

    Sergeant

  • Members
  • PipPipPip
  • 77 posts

Posted 25 August 2015 - 06:38 PM

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


#37 Maurice76

Maurice76

    Sergeant

  • Members
  • PipPipPip
  • 77 posts

Posted 25 August 2015 - 06:50 PM

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


#38 Maurice76

Maurice76

    Sergeant

  • Members
  • PipPipPip
  • 77 posts

Posted 25 August 2015 - 06:57 PM

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


#39 Maurice76

Maurice76

    Sergeant

  • Members
  • PipPipPip
  • 77 posts

Posted 25 August 2015 - 07:09 PM

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


#40 NKF

NKF

    Mr. Badger in disguise

  • Site Staff
  • PipPipPipPipPip
  • 4,426 posts
  • Gender:Male
  • Location:In my mind

Posted 26 August 2015 - 05:32 AM

Quote

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. Posted Image

- NKF
NKF, narrow minded fuddy duddy who refuses to let go of the past and will not accept anything newer than 1979.




0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users