Mikelan98, AdAstra/LD3005
Target games:
With the arrival of Pokémon Diamond and Pearl, maps were introduced a second layer for move permissions that -potentially- allowed a more flexible collision editing for maps, since it allowed combining the hit properties with every other collision type in the map. In practice, however, cases where an unusual hit property is needed for a collision type in a tile are very rare, rendering the two-layer system pointless. Furthermore, this system may suppose an additional effort to the ROM hacker when it comes to mapping.
DS Pokémon ROM Editor (DSPRE) showing the two collision layers of a Pokémon Platinum map.
In Pokémon HeartGold and SoulSilver, a third layer was added for triggering sounds when the player steps over a tile. In this case, though, the system is useful since the combinations of the different sounds with different collision types are very frequent, allowing plenty of graphical effects (shallow water, footprints) followed by sound effects, and thus enriching the game experience.
In this tutorial we will research the game code that handles the hit collision, so the second move permissions layer from the game maps can be removed. The default hit collision will be implemented at collision type 01, and a method for assigning hittable/walkable properties for every collision type in the game code will be implemented. Additionally, for this purpose, a small tool will be developed to analyse and convert the original maps collisions into the new collision system with the hit layer removed. Finally, the different sounds collisions in HeartGold and SoulSilver will be listed and researched to identify the different sounds that are triggered by each.
The easiest strategy for searching the game subroutine that handles the hit collisions is to find and trace the map data in the RAM memory. In this case, we took New Bark Town as an example, and disassembled the game while the player was present in the mentioned map. The whole map collision data of New Bark Town was searched in the RAM memory so it could be traced. Optionally, the emulator DeSmuME allows dumping the RAM memory, so the map data can also be searched in an external hex editor. In both cases, we got the memory address of New Bark Town collisions.
New Bark Town collision data in the RAM memory, at address 0x0231F748.
By enabling a trace breakpoint in a specific collision, we can check the subroutines that read the collision data by stepping over that tile. We found that, for HeartGold, subroutine sub_20547D8 was the only one that accessed this data, and this function returned the complete 2 bytes collision without processing it. Researching a bit further, we found that four subroutines called this mentioned subroutine: sub_20548C0 (processed hit collisions), sub_20548EC (processed sound collisions), sub_2054918 (processed collision types) and sub_2054940 (returned the complete collision). The first subroutine, sub_20548C0, was the one that had to be researched and edited for our purpose.
The subroutine sub_20548C0 takes the complete 2 bytes collision and checks the most significant bit. If the bit is set, it returns 1 (meaning that it is a hit). However, if the most significant bit is clear, it returns 0 (standing for no hit). The most significant bit is represented in the maps by the value 80 of the second collision layer in SDSME and DSPRE -although the second layer should be a single bit value, boolean array, not a byte array as it is represented in those tools- forming the halfword 0x8000, or 0b1000000000000000. We will modify this subroutine, so it returns 0 or 1 depending on a custom check.
Collision data scheme for Gen IV maps. H: Hit, S: Sound, T: Type. Every part is processed by a different subroutine. The sound part is only processed in HeartGold and SoulSilver.
Researching a bit more, we found that a collision type properties array can be found in 0x020FCA74. This array is 256 bytes long (one byte per collision type), and in this array, every collision type has two possible bitflags assigned: 1 for surfable collisions and 2 for collisions that trigger wild encounters. In our case, a third bitflag (with value 4) can be added to represent a hittable/walkable property, so we edited the subroutine sub_20548C0 for this goal. To determine whether a collision type had to have this bitflag set or clear, we developed a tool to mass-analyse the hit-type combinations in the original game maps. This tool was eventually also used to convert the map collision data into the new single-layer format.
To identify the sounds triggered by the different sound collisions in HeartGold and SoulSilver, we researched the calls to the subroutine sub_20548EC, eventually finding a table in 0x20FCB98 that handles the played sounds.
Sound Collision | Triggered Sound | Description |
---|---|---|
00 | 2156 - SEQ_SE_GS_ASHIOTO_A_WALK | Step (walking) |
01 | 2157 - SEQ_SE_GS_ASHIOTO_A | Step (running) |
02 | 2154 - SEQ_SE_GS_OCHIBA | Fallen leaves (used in Azalea Gym and Bellchime Trail) |
03 | 2166 - SEQ_SE_GS_EDAPAKI | Cracking branches (Ilex Forest Farfetch'd puzzle) |
04 | 2167 - SEQ_SE_GS_KUSA2 | Grass |
05 | 2158 - SEQ_SE_GS_ASHIOTO_B* | Sand |
06 | 2160 - SEQ_SE_GS_ASHIOTO_D* | Step (used in entrances) |
07 | 2161 - SEQ_SE_GS_ASHIOTO_D_2 | Metal |
08 | 2162 - SEQ_SE_GS_ASHIOTO_E | Unknown (not used) |
09 | 2158 - SEQ_SE_GS_ASHIOTO_B* | Straw (used in Moo-moo Farm) |
0A | 2163 - SEQ_SE_GS_ASHIOTO_F* | Rock (used in some caves) |
0B | 2160 - SEQ_SE_GS_ASHIOTO_D | Step (used in Olivine/Vermilion ports) |
0C | 2165 - SEQ_SE_GS_ASHIOTO_H | Unknown (not used) |
0D | 2164 - SEQ_SE_GS_ASHIOTO_G | Cracking wood |
0E | 1515 - SEQ_SE_DP_BOX02 | Unknown (not used) |
0F | 1514 - SEQ_SE_DP_BOX01 | Unknown (not used) |
Although 128 possible values fit into a sound collision (7 bits), only values 0 to 15 should be used. If more values were to be used, the table should be repointed (changing the addresses written at 0x0205CE5C and 0x0205CE64) and expanded with the new sound IDs, and the limit written at 0x0205CE16 should be increased with the table size.
The source code of the program for both analysing and converting the maps collisions can be downloaded with the following link: CollisionMerger.zip
It requires the original maps binary files, that will be modified. An output text file will be generated after the conversion. The modified map files must be reinserted in the ROM after that.
To modify the game code, we will need to edit the sub_20548C0 subroutine (or its analogue in other game versions) and the collision properties table. The table below shows the offset (in the arm9.bin) and the new data for both, and for every game version.
Game Code | Hit Check Subroutine Offset | Hit Check Subroutine Code | Collision Properties Offset | Collision Properties Data | |
![]() ![]() |
ADAE | 0x4A6B4 | 18 B5 81 B0 84 6D 00 AB 64 68 A0 47 00 28 08 D0 00 A8 00 78 04 49 09 5C 04 20 08 42 01 D0 01 20 00 E0 00 20 01 B0 18 BD 14 5C 0F 02 | 0xF5C14 | ![]() |
---|---|---|---|---|---|
![]() ![]() |
APAE | 0x4A6B4 | 18 B5 81 B0 84 6D 00 AB 64 68 A0 47 00 28 08 D0 00 A8 00 78 04 49 09 5C 04 20 08 42 01 D0 01 20 00 E0 00 20 01 B0 18 BD 14 5C 0F 02 | 0xF5C14 | ![]() |
![]() ![]() |
CPUE | 0x54F68 | 18 B5 81 B0 C4 6D 00 AB 64 68 A0 47 00 28 08 D0 00 A8 00 78 04 49 09 5C 04 20 08 42 01 D0 01 20 00 E0 00 20 01 B0 18 BD C4 D8 0E 02 | 0xED8C4 | ![]() |
![]() ![]() |
IPKE | 0x548C0 | 18 B5 81 B0 04 6E 00 AB 64 68 A0 47 00 28 08 D0 00 A8 00 78 04 49 09 5C 04 20 08 42 01 D0 01 20 00 E0 00 20 01 B0 18 BD 74 CA 0F 02 | 0xFCA74 | ![]() |
![]() ![]() |
IPGE | 0x548C0 | 18 B5 81 B0 04 6E 00 AB 64 68 A0 47 00 28 08 D0 00 A8 00 78 04 49 09 5C 04 20 08 42 01 D0 01 20 00 E0 00 20 01 B0 18 BD 74 CA 0F 02 | 0xFCA74 | ![]() |
![]() ![]() |
ADAS | 0x4A724 | 18 B5 81 B0 84 6D 00 AB 64 68 A0 47 00 28 08 D0 00 A8 00 78 04 49 09 5C 04 20 08 42 01 D0 01 20 00 E0 00 20 01 B0 18 BD 64 5C 0F 02 | 0xF5C64 | ![]() |
![]() ![]() |
APAS | 0x4A724 | 18 B5 81 B0 84 6D 00 AB 64 68 A0 47 00 28 08 D0 00 A8 00 78 04 49 09 5C 04 20 08 42 01 D0 01 20 00 E0 00 20 01 B0 18 BD 64 5C 0F 02 | 0xF5C64 | ![]() |
![]() ![]() |
CPUS | 0x55038 | 18 B5 81 B0 C4 6D 00 AB 64 68 A0 47 00 28 08 D0 00 A8 00 78 04 49 09 5C 04 20 08 42 01 D0 01 20 00 E0 00 20 01 B0 18 BD 58 D9 0E 02 | 0xED958 | ![]() |
![]() ![]() |
IPKS | 0x548B8 | 18 B5 81 B0 04 6E 00 AB 64 68 A0 47 00 28 08 D0 00 A8 00 78 04 49 09 5C 04 20 08 42 01 D0 01 20 00 E0 00 20 01 B0 18 BD 5C CA 0F 02 | 0xFCA5C | ![]() |
![]() ![]() |
IPGS | 0x548C0 | 18 B5 81 B0 04 6E 00 AB 64 68 A0 47 00 28 08 D0 00 A8 00 78 04 49 09 5C 04 20 08 42 01 D0 01 20 00 E0 00 20 01 B0 18 BD 74 CA 0F 02 | 0xFCA74 | ![]() |
After the changes are successfully applied, map collisions for new maps made in PDSMS, DSPRE or SDSME must use collision 01 in the type layer (instead of the old collision 80 in the hit layer). The hit layer should be ignored and left empty (in Diamond, Pearl and Platinum) or used for sound collisions only (HeartGold and SoulSilver). We recommend updating PDSMS and DSPRE, so the collision type 01 is depicted as the new hit collision. After this, you won’t have to paint your map collisions twice when making new maps!
If you are planning to use this research on your project, credits for Mikelan98 and AdAstra/LD3005 are needed. You can cite this research by copying the following reference in the credits section of your project's thread or webpage:
Mikelan98, AdAstra/LD3005: Map Collision Layers Merge (pokehacking.com/r/24061300) |
Total or partial reproduction of this research in other site is forbidden without prior authorization of the authors.