User:Tiddlywinks/Map header data structure in Generation I
This article is intended to be moved into one of Bulbapedia's main content spaces. Be mindful that it is still part of a personal userspace. Any content changes should be brought up on the talk page for this article. |
The map header data structure tells the game everything it needs to know about how to build a map and how it should operate. A map is a town, route, house, cave, or generally any area that requires a "warp" transition (whether through doors or more literal warps).
Notes
Map header | ||
---|---|---|
size | offset | |
Tileset index (0-based) | 1 byte | 0 |
Map height (blocks) | 1 byte | 1 |
Map width (blocks) | 1 byte | 2 |
Pointer to map | 2 bytes | 3 |
Pointer to text script pointer list | 2 bytes | 5 |
Pointer to map script | 2 bytes | 7 |
Connections | 1 byte | 9 |
Connections data | ... | opt |
Pointer to object data | 2 bytes | ... |
Map height and width
The map height and width are given in blocks, which is the unit in which the map is "drawn". A block is a square with dimensions of four "tiles" on each side, where a tile is an 8×8 pixel square. A block's dimensions are also equal to two steps (by the player) per side.
Pointer to map
The "map" that is pointed to is a list of blocks that "draws" the map from the map's defined tileset. The length in bytes of the data pointed to is the product of the width and the height.
Pointers to scripts
The text scripts pointed to by the map header are activated when a sign or sprite is interacted with. Signs and sprites are defined in the map's object data, and they access the text script list by a 1-based index they are assigned. Text scripts can be simple or complex, accomplishing any variety of things from simple text display to giving the player an item.
The map script is machine code that manages things that happen automatically within the map, such as an NPC who will not allow the player to pass until a certain condition has been met.
Connections byte
The connections byte at offset 0x09 in the map header defines in what directions another map connects to the defined map (such that the other map can be seen while standing in the defined map). Each bit corresponds to a direction, and if a given bit is on (set to 1), the map has a connection there.
Bit | Decimal | Direction |
---|---|---|
0 | +1 | East |
1 | +2 | West |
2 | +4 | South |
3 | +8 | North |
Connections data
List item - map connection | ||
---|---|---|
size | offset | |
Index of connected map (0-based) | 1 byte | 0 |
Pointer to start in connected map | 2 bytes | 1 |
Pointer to start of connection | 2 bytes | 3 |
Connection size (blocks) | 1 byte | 5 |
Connected map width (blocks) | 1 byte | 6 |
Player Y offset (steps) | 1 byte | 7 |
Player X offset (steps) | 1 byte | 8 |
Pointer to window | 2 bytes | 9 |
After the connections byte at 0x09, there may be a list of connection data. If the connections byte is 0, there will be no list and the object data pointer will immediately follow the connections byte.
The number of entries in this list equals the number of connections the map has. The entries must be ordered north first, then south, then west, then east. If the entries are not ordered properly, though the maps may display properly, the game may use the wrong data when the player crosses into a connected map.
Note that all dimensions herein ("height" and "width") are measured in blocks. Coordinates also start with (0, 0) being the point in the top-left corner of the map.
Background information
When the player enters a map, the game "draws" that map in a "working" map in RAM. The address of the working map is constant (0xC6E8), with its contents changing as the player changes maps.
The working map does not only include the currently occupied map, however. When the game draws the working map, it adds a 3-block buffer on all sides of the current map. The game then uses this buffer zone to draw the "connections" to any maps adjacent to the currently occupied map. The 3-block buffer means that even if the player stands at the edge of a map next to a connection, the area that the player can see (18 tiles tall x 20 tiles wide, with 8 tiles on all sides except on the right; and 4 tiles = 2 steps = 1 block) is always within the working map.
Pointers to starts
These pointers define, respectively, what part of the connected map is drawn and where it gets drawn relative to the current map.
- Pointer to start in connected map
The pointer to the start in the connected map points to the first (upper-left) block of the connection that will be drawn. This combines with the defined connection size to define the section of the map that will be drawn for the connection. The below illustrates how this may be calculated.
- For a north or south connection, to draw a connection that starts B blocks east of the leftmost edge of the map, start with:
map_ptr + B
- where map_ptr is the pointer to the connected map.
- If the connection is in the south, add onto the above:
width * ( height - 3 )
- where width and height refer to the dimensions of the connected map in blocks.
- For an east or west connection, to draw a connection that starts B blocks south of the topmost edge of the map, start with:
map_ptr + ( width * B )
- where map_ptr is the pointer to the connected map and width is its width in blocks.
- If the connection is in the west, add onto the above:
width - 3
- Pointer to start of connection
The pointer to the start of the connection points to the first (upper-left) block where the connection will be drawn in the working map. The below illustrates how this may be calculated.
- For a north or south connection, to position a connection so it starts B blocks east (where B can be negative) of the leftmost edge of the map, start with:
0xC6E8 + B + 3
- If the connection is in the south, add onto the above:
( height + 3 ) * ( width + 6 )
- to move the pointer to the southern buffer zone, where height and width refer to the base dimensions of the current map in blocks.
- For an east or west connection, to position a connection so it starts B blocks south (where B can be negative) of the uppermost edge of the map, start with:
0xC6E8 + ( width + 6 ) * ( B + 3 )
- where width refers to the base width of the current map in blocks.
- If the connection is in the east, add onto the above:
width + 3
- to move the pointer to the eastern buffer zone.
Connection size
This combined with the pointer to the start point in the connected map defines the section of the connected map that is drawn beyond the edge of the current map. For connections that are on the north or south, this defines the width that is drawn, while for connections on the east or west, it defines the height. (The size of the connection in the other dimension is 3 blocks, i.e., the size of the buffer zone.)
Player Y and X offsets
These offsets allow the game to translate the player's position in one map to a new position in a connected map when they cross a connection.
While the player is in the current map, their position is (Y, X) (measured in steps from the top and left edges of the map, with 0 being at the edge). When the player crosses from one map into a connected map, if the player is crossing a north or south connection, their Y position will be treated as 0; if the player is crossing an east or west connection, their X position will be treated as 0. Translating the remaining unmodified value then depends on two variables used above: the offset of the connection within the connected map, con_map_offset (i.e., the value used for B in deriving the pointer to the start in the connected map above), and the offset of the connection strip within the working map, strip_offset (i.e., the value used for B in deriving the pointer to the start of the connection above). Note that strip_offset can be negative.
For a north or south connection, then, the player offsets are:
- Y:
0
for a south connection,2 * height - 1
for a north connection (where height is the connected map's height in blocks). - X:
-2 * ( strip_offset - con_map_offset )
.
For a east or west connection, the player offsets are:
- Y:
-2 * ( strip_offset - con_map_offset )
. - X:
0
for an east connection,2 * width - 1
for a west connection (where width is the connected map's width in blocks).
Pointer to window
This pointer indicates the leftmost or uppermost block in the working map that will be visible on the Game Boy's screen after the player crosses into a connected map and the game loads it into the working map.
The Game Boy's screen is 18 tiles tall and 20 tiles wide, with the player's sprite occupying a 2×2-tile square approximately in the middle, such that there are always 8 tiles to any side of the player except to the right (where there are 10). Since blocks are 4×4 squares of tiles, this means that there are always two blocks both above and to the left of the player (where the screen starts drawing from).
This pointer does not point to the exact block that will be visible, since this is impossible given that the player could be in a number of positions depending on the connection size. Instead, it points to the second block that is in the uppermost (for north/south connections) or leftmost (for east/west connections) line of blocks that may be visible, and the game automatically adjusts for the player's variable position. Presumably, the reason why the second block is used instead of the first is because, being the outermost edge of the working map's buffer zone (which is three blocks tall/wide), that first block would never be visible since the player can only enter on the new map's uppermost or leftmost edge, with only two blocks of the buffer zone visible.
For a north or south connection, then, this pointer may be derived by calculating:
0xC6E8 + 1 + x * ( 6 + width )
where x is 1 if the connection is a north connection, or height if it is a south connection. Width and height refer to the connected map's dimensions in blocks.
For an east or west connection, this pointer may be derived by calculating:
0xC6E8 + ( 6 + width ) + x
where x is 1 if the connection is an east connection, or width if it is a west connection. Width refers to the connected map's width in blocks.
Object data
Object data | ||
---|---|---|
size | offset | |
Border block | 1 byte | 0 |
Number of warps | 1 byte | 1 |
List of warps | ... | opt |
Number of signs | 1 byte | ... |
List of signs | ... | opt |
Number of sprites | 1 byte | ... |
List of sprites | ... | opt |
List of warp-in points | ... | opt |
Object data is a further set of data about a map that is located separate from the map header. It is pointed to by the last pointer in the map header.
Border block
The border block is the block that is used to fill undefined spaces outside of the current map that come into view.
Warps
List item - warp | ||
---|---|---|
size | offset | |
Y position (steps) | 1 byte | 0 |
X position (steps) | 1 byte | 1 |
Warp-in index (0-based) | 1 byte | 2 |
Target map index (0-based) | 1 byte | 3 |
The list of warps has the same number of entries as the number of warps value. If there are no warps, the number of signs value immediately follows the number of warps value.
Map warps can activate in two ways, defined by the type of tile that is at or around the warp point's coordinates.
The first way for a warp to activate is for the tile at that coordinate (defined as the bottom-left tile in the 2×2 square of tiles the player would stand on) to be a special tile. If it is, the player will warp immediately.
If the tile at the warp's coordinates is not a special tile, then either the tile in front of the player (defined as the bottom-left tile in the 2×2 square of tiles the player might stand on) must be another type of special tile or (in certain maps) the player must be facing the edge of the map. If one of these is true, then if the player attempts to move forward again and collides with the tile or the edge of the map, then they will warp. (If the player does not collide, the player will not warp.)
Notes
The warp-in index is a 0-based index referencing the warp-in list of the target map.
If the target map index is set to 0xFF, the warp will take the player "outside" (that is, to the last outdoors map; or the last map set as such, such as when crossing Diglett's Cave). The target map may also be the same as the current map's index.
Signs
List item - sign | ||
---|---|---|
size | offset | |
Y position (steps) | 1 byte | 0 |
X position (steps) | 1 byte | 1 |
Text script index (1-based) | 1 byte | 2 |
The list of signs has the same number of entries as the number of signs value. If there are no signs, the number of sprites value immediately follows the number of signs value.
An entry in the signs list will cause the designated text script to be activated when the player presses the A button while facing the designated coordinates.
Sprites
List item - sprite | ||
---|---|---|
size | offset | |
Sprite index (1-based) | 1 byte | 0 |
Adjusted Y position (steps) | 1 byte | 1 |
Adjusted X position (steps) | 1 byte | 2 |
Mobility | 1 byte | 3 |
Movement | 1 byte | 4 |
Type flags + text script index (1-based) | 1 byte | 5 |
Item sprites only | ||
Item index (1-based) | 1 byte | 6 |
Battle sprites only | ||
Opponent index (1-based) | 1 byte | 6 |
Level or team index (1-based) | 1 byte | 7 |
The list of sprites has the same number of entries as the number of sprites value. If there are no sprites, the warp-in points list immediately follows the number of sprites value.
There are three types of sprites: normal NPCs, items that can be picked up, and battles (Trainers or stationary Pokémon). The length of an entry in the sprites list depends on the type of sprite being described. The type of sprite is indicated in the sixth byte of an entry, the "type flags + text script pointer".
Adjusted Y and X positions
These values are equal to the sprite's position on the map (in steps) plus four. The addition makes it easier for the game to quickly determine whether the sprite is visible on the screen or not, since one of the sprite's coordinates will be equal to one of the player's coordinates when the sprite appears on the edge of the screen (because there are always four steps to the left of or above the player).
Mobility
The mobility determines whether the sprite can move from its starting position. Possible values are:
- 0xFE: mobile (the sprite may move from its starting position)
- 0xFF: stationary (the sprite will remain in its starting position)
- Other: the sprite will always move (even if it is not visible) and will ignore collisions.
Movement
The movement describes how the sprite may try to move. A sprite chooses its movements randomly from among its permitted directions. Possible values are:
- 0x01: only move up or down
- 0x02: only move left or right
- 0xD0: always move down
- 0xD1: always move up
- 0xD2: always move left
- 0xD3: always move right
- Other: move randomly
If a sprite is stationary (as defined by its mobility), instead of moving, it will only change the direction it is facing (if possible). If a mobile sprite chooses a movement where it would collide with something (including the edge of the screen), it will instead only change direction.
Types flags + text script index
The highest two bits of this byte indicate which type of sprite this entry describes. The lowest five bits are the sprite's text script index number.
The high bits are interpreted as below:
Bit | Decimal | Sprite type |
---|---|---|
6 | +64 | Battle |
7 | +128 | Item |
If neither of these bits is set (i.e., if this byte's value is less than 64), the sprite is a normal NPC. If both of these bits are set, the sprite will be recognized as a battle sprite (since that is the bit that is checked first).
Opponent index (battles)
If this value is less than 0xC8, it is interpreted as the index number of a Pokémon and will start a wild Pokémon battle with that Pokémon. This is used in places such as the Kanto Power Plant (Voltorb and Electrode) and Cerulean Cave (Mewtwo).
Otherwise, 0xC8 is subtracted from this value and it is interpreted as the (1-based) index number of a Trainer class and will start a Trainer battle.
Level or team index (battles)
If the opponent index designates a wild Pokémon battle, this value is the Pokémon's level.
Otherwise, this value is the index that indicates which team the Trainer will use among the list of Pokémon teams for their Trainer class.
Warp-in points
List item - warp-in point | ||
---|---|---|
size | offset | |
Pointer to window | 2 bytes | 0 |
Y position | 1 byte | 2 |
X position | 1 byte | 3 |
The list of warp-in points has the same number of entries as the number of warps value (i.e., it is the same size as the warps list). If there are no warp-in points, the object data ends immediately after the sprites list.
This list defines where the player will warp in after taking a warp that leads to this map. The warp-in index of the used warp indicates which entry in this list to enter with.
Pointer to window
Like the pointer to the new window for the connection data in the map header, this pointer indicates the leftmost or uppermost block in the working map that will be visible on the Game Boy's screen after the player crosses into a connected map and the game loads it into the working map.
Since the player always has two blocks above and to the left of them, this pointer may be derived by calculating:
0xC6E8 + 1 + X_block + ( 6 + width ) * ( 1 + Y_block )
where width is this map's width in blocks and where Y_block and X_block are the (0-based) blocks where the player will be located (calculated by taking, respectively, half of the Y position (in steps) and half of the X position, rounded down).
Map header location data
Map headers are located across many "banks" of the ROM and are not generally consecutive with each other. Two separate lists combine to point to the location of each map header. One is a list of offset pointers, which has the format of a simple list of pairs of bytes (written in little endian), one pair for each map header. The other is a list of bank numbers, which has the format of a simple list of bytes, one for each map header.
Game | Offsets | Banks |
---|---|---|
Red and BlueEN | 0x01AE | 0xC23D |
All maps
The table below identifies all of the possible map indices and the address of their map header in English Pokémon Red games. (The addresses in Blue are generally the same, with some small differences.) Rows that are in red mark invalid maps that do not point to actual map data and that will freeze the game if visited. Rows that are in gray mark unused but otherwise valid maps.
|
|