The current URL is datacrystal.tcrf.net.
Pokémon Red and Blue/Notes
The following article is a Notes Page for Pokémon Red and Blue.
False 'level' addresses
These are stored values that may change after switching a Pokémon up to a specific position in the party. The value for that process is updated after depositing and withdrawing the Pokémon into a box.
Tileset headers
12 bytes per header, as follows :
1 byte - Bank ID of blocks and tiles 2 bytes - Pointer to blocks 2 bytes - Pointer to tiles 2 bytes - Pointer to collision data 3 bytes - Up to 3 'talking-over tiles' tile numbers ("empty" slots are filled with $FF) 1 byte - Grass tile ($FF if none) 1 byte - Animation flag
Since the player moves on a 2x2 tile grid, the game checks the bottom-left tile of such a group for its properties (grass, talking-over...)
(Note : there's an inconsistency with this, the game uses the bottom-right tile to determine water, hence the Old Man Glitch)
Blocks
Maps are made up of 4x4 tile blocks, stored (how?)
A tile is 8x8 pixels, so a block is 32x32 pixels.
Tiles
Tiles are stored using the GB's format, and directly copied to VRAM.
Collision data
This is a pointer to a list of tile numbers over which the player can walk. Terminated with a FF byte.
Talking-over tiles
These are tiles which allow the player to talk to a person on the other side, such as the counters in the Pokémon Centers or Pokémarts.
Each tileset can have up to 3 talking over tiles. Use FF to fill the unused slots.
Grass tile
This is both a tile that will be rendered above sprites, and one where random "grass" encounters are possible.
Animation flag
This byte defines whether the tileset has animated tiles.
If the byte is zero, no animations take place. Otherwise, the water animation will be in effect, and if bit 0 is reset, the flower animation also will.
The water animation is done by rotating tile $14's pixels left and right. The flower animation, however, overwrites tile $03 with images stored in the ROM, at addresses $1F29, $1F29 and $1F39.
Map headers
This contains all the data (pointers count as data) to build the map.
1 byte - Tileset ID 1 byte - (Y Size) Map height 1 byte - (X Size) Map width 2 bytes - Pointer to map data 2 bytes - Pointer to text pointers 2 bytes - Pointer to script 1 byte - Connection Byte 11 bytes per connection - Connection data (No connections? Straight to object data!) 2 bytes - Pointer to object data
Tileset numbers
The tileset descriptions are copied from a document compiled by Cartmic, called "Pokemon Red Documents", which may help you with other stuff too.
Tileset No. | Location in US R/B ROM | Description |
---|---|---|
00 | C7BE | Outside |
01 | C7CA | Ash's House |
02 | C7D6 | Pokemon Center |
03 | C7E2 | Viridian Forest |
04 | C7EE | Ash's House (copy) |
05 | C7FA | Gym |
06 | C806 | Pokemon Center |
07 | C812 | Gym (copy) |
08 | C81E | House |
09 | C82A | Museum |
0A | C836 | Museum (copy) |
0B | C842 | Underground Path |
C0C | 84E | Museum (second copy) |
0D | C85A | S.S. Anne |
0E | C866 | Vermilion Port |
0F | C872 | Pokemon Cemetery |
10 | C87E | Silph Co. |
11 | C88A | Cave |
12 | C896 | Celadon Mart |
13 | C8A2 | Game Freak HQ |
14 | C8AE | Lab |
15 | C8BA | Bike Shop/Cable Center |
16 | C8C6 | Cinnabar Mansion/Power Plant etc |
17 | C8D2 | Indigo Plateau |
NOTE: As you can see there's multiple copies of some tileset headers. They are literally copies.
If you're ROM hacking the game and want to add custom tilesets, you can delete copies to make room. However make sure that all maps that used the copy now use the original.
Map height (Y axis) & width (X axis)
Exactly that, the amount of blocks high and wide the map data is.
Map data pointer
You can use the GoldMap engine for hacking map data quite easily.
The map you walk around on and stuff is stored as block indexes. A single block consists of 4x4 tiles.
Pointer to text pointers
This points to a list of pointers. When the game attempts to display textbox #3, it will fetch the 3rd pointer in this list.
(Note : textbox #0 is hardwired to the START menu)
Pointer to script
Points to a function ran on each frame spent in this overworld (for example, not ran while a textbox is active)
If a map has a functionality you can't implement otherwise, check this out. You can sometimes find things like XY positions of Poke Balls and 1-time only Pokemon.
Connection mask
Note: If this value is $00 it is immediately followed by the object data pointer, no gap.
Connections can be obtained with binary masks:
connect_byte & (1 << 3) -> North connect_byte & (1 << 2) -> South connect_byte & (1 << 1) -> West connect_byte & (1 << 0) -> East
List of values:
00 = No Connections 01 = East 02 = West 03 = West + East 04 = South 05 = South + East 06 = South + West 07 = South + West + East 08 = North 09 = North + East 0A = North + West 0B = North + West + East 0C = North + South 0D = North + South + East 0E = North + South + West 0F = North + South + West + East
Connection Data
Size can range from 0 to 44 bytes.
Probably the toughest thing to manipulate. This will certainly require planning, with no background distractions. (Unless a editor gets built that can hack the connections for RGBY.)
X/Y movement of connection
A X movement is how many map blocks there are to the left of one of your north/south connections.
A Y movement is how many map blocks there are above your west/east connection.
Structure
1 byte - Map ID of connected map 2 bytes - Pointer to "connection strip"s upper-left block (Connected map) 2 bytes - Pointer to "connection strip"s upper-left block (Current map) 1 byte - "Bigness" 1 byte - Map Width 1 byte - Y alignment 1 byte - X alignment 2 bytes - Window
TODO : convert this block to wiki format.
=============================================================================== #1 : Map Indexes =============================================================================== Not included! Download UltraMap, the text file known as "RedEnglish.ini" contains all the Map Indexes in decimal for R/B, which you'll obviously need to convert to hex. =============================================================================== #2-#3 : "Connection Strip" Location =============================================================================== The "connection strip" pointer -- points to the area of the connected map that is visible when standing before you even enter it. Points to the upperleft block of the "connection strip". * connection strip is always 3 blocks wide (E/W connection) 3 blocks high (N/S connection) =============================================================================== #4-#5 : Current Map Position =============================================================================== This points to the part of the current map (further up in RAM) that the connection strips upperleft block is placed on the current map. ____________________ Connection | Direction | Formula ___________|_______ North: C6EB + X_Movement_of_Connection Strip South: C6EB + (Height of Map + 3) * (Width of Map + 6) + X_Movement_of_Connection Strip West: C6E8 + (Width of Map + 6) * (Y_Movement_of_"Connection Strip" + 3) East: C6E5 + (Width of Map + 6) * (Y_Movement_of_"Connection Strip" + 4) =============================================================================== #6 : Map "Bigness" =============================================================================== North/South Connection = Connected Maps Width East/West Connection = Connected Maps Height =============================================================================== #7 : Map Width =============================================================================== The width of the connected map. =============================================================================== #8 : Y alignment =============================================================================== Relative Y-position of player after entering connected map. ____________________ Connection | Direction | Formula ___________|_______ North: (Height_of_connected_map * 2) - 1 South: 0 West/East: (Y_movement_of_connection_strip_in_blocks * -2) =============================================================================== #9 : X alignment =============================================================================== Relative X-Position of player after entering connected map. ____________________ Connection | Direction | Formula ___________|_______ North/South: (X_movement_of_connection_strip_in_blocks * -2) West: (Width_of_connected_map * 2) - 1 East: 0 =============================================================================== #10-#11 : Window =============================================================================== Position of the upper left block after entering the Map. ____________________ Connection | Direction | Formula ___________|_______ Above: C6E9h + Height_of_connected_map * (Width_of_connected_map + 6) South/East: C6EFh + Width_of_connected_map West: C6EEh + 2 * Width_of_connected_map ...............................................................................
Example
PKMN Red Example -- Saffron City (Header: 0x509A4) Diagram: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Saffrons need-to-know stats: Block Height (Y): 12 Block Width (X): 14 Connection Byte: 0F (North + South + West + East) - - - - + = current maps border blocks ++++++++NNNNNNNNNN++++++++ ++++++++NNNNNNNNNN++++++++ ++++++++NNNNNNNNNN++++++++ +++####################+++ +++####################+++ +++####### My #########+++ +++####################+++ WWW####### Name's #####EEE WWW####################EEE WWW###### Saffron #####EEE WWW####################EEE WWW###### City! #######EEE WWW####################EEE WWW####################EEE WWW####################EEE WWW####################EEE +++####################+++ +++####################+++ +++####################+++ +++####################+++ +++####################+++ ++++++++SSSSSSSSSS++++++++ ++++++++SSSSSSSSSS++++++++ ++++++++SSSSSSSSSS++++++++ Connection to Route 5 (to the North) *Y: 12 *X: 0A *X_Movement_of_Connection Strip = 5 Map Index: 10 Connection Strip: 4668 Where Connected: C6F0 (C6EB + 5) "Bigness": 0A Width: 0A Y alignment: 23 (12 * 2 - 1) X alignment: F6 (5 * -2) Window: C809 (C6E9 + (12 * (0A + 6))) Connection to Route 6 (to the South) *Y: 12 *X: 0A *X_Movement_of_Connection Strip = 5 Map Index: 11 Connection Strip: 4079 Where Connected: C912 (C6EB + ((12 + 3) * (14 + 6)) + 5) "Bigness": 0A Width: 0A Y alignment: 00 X alignment: F6 (5 * -2) Window: C6F9 (C6EF + 0A) Connection to Route 7 (to the West) *Y: 09 *X: 0A *Y_Movement_of_Connection Strip = 4 Map Index: 12 Connection Strip: 4058 Where Connected: C79E (C6E8 + (14 + 6) * (4 + 3)) "Bigness": 09 Width: 0A Y alignment: F8 (4 * -2) X alignment: 13 ((0A * 2) - 1) Window: C702 (C6EE + (0A * 2)) Connection to Route 8 (to the East) *Y: 09 *X: 1E *Y_Movement_of_Connection Strip = 4 Map Index: 13 Connection Strip: 41C6 Where Connected: C7B5 (C6E5 + (14 + 6) * (4 + 4)) "Bigness": 09 Width: 1E Y alignment: F8 (4 * -2) X alignment: 00 Window: C70D (C6EF + 1E)
Object Data
1 byte - Border block ID 1 byte - Number of warps 4 bytes per warp - Warp data 1 byte - Number of signs 3 bytes per sign - Sign data 1 byte - Number of NPCs (total) 6/8/7 bytes per NPC - NPC data 4 bytes per warp-to - Warp-To data
Warps
1 byte - Y position 1 byte - X position 1 byte - Destination warp-to's ID (within target map) 1 byte - Destination map
Signposts
1 byte - Y position 1 byte - X position 1 byte - Text string ID
NPCs
In order to distinguish People, Trainers and Items, you must check the text string ID:
strID & (1 << 6) != 0 : Trainer (2 extra bytes, the trainer class and roster IDs)
strID & (1 << 7) != 0 -> Item (1 extra byte, the item ID)
Normal people
1 byte - Picture number 1 byte - Y position + 4 1 byte - X position + 4 1 byte - Movement byte 1 1 byte - Movement byte 2 1 byte - Text string ID
Trainers / One-time Pokémon
One-time Pokémon and Trainers are essentially the same thing, separated by their species ID. If it's under 200, it's a Pokémon. Over or equal to 200, it's a Trainer.
1 byte - Picture number 1 byte - Y position + 4 1 byte - X position + 4 1 byte - Movement 1 1 byte - Movement 2 1 byte - Text string ID 1 byte - Pokémon species ID / Trainer class 1 byte - Pokemon level / Trainer's roster ID
Items
1 byte - Picture number 1 byte - Y position + 4 1 byte - X position + 4 1 byte - Movement 1 1 byte - Movement 2 1 byte - Text string ID 1 byte - Item ID
Warp-To Points
2 bytes - Event Displacement 1 byte - Y position 1 byte - X position
Sprites
Sprites are loaded when changing maps. However, there is a little subtlety : when traversing a map connection, the game assumes the correct sprite set is loaded, and doesn't reload sprites. (Hence glitchy sprites sometimes observed with Walk Through Walls).
The other occasion where sprites are loaded is when a text display is closed ; the game reloads sprites because the "walking" sprites are overwritten by font tiles.
Also, if there are no sprites (NPCs) the game doesn't reload sprite graphics.
How the game reloads sprites differs for "exterior" and "interior" maps.
Exterior maps
The game reads an entry from the table at 17A64, MapSpriteSets. If the entry is < $F0, the game uses the corresponding sprite set. Otherwise, it uses the corresponding split sprite set (see dedicated section).
Split sprite sets
Some exterior maps have two sprite sets, which depend on position.
Consider the entry read from MapSpriteSets. If the entry is $F8, that's Route 20 - a special case with a complex splitting. See its dedicated section.
The table maintains a table at 17A89, SplitMapSpriteSets, structured like so :
1 byte - Split type (1 = East/West division, 2 = North/South division) 1 byte - Split coordinate 1 byte - Sprite set ID if coordinate < split coordinate 1 byte - Sprite set ID if coordinate >= split coordinate
The game reads an entry, indexed using (the byte read from MapSpriteSets AND $0F), and uses this sprite set.
Sea Route 20
Sea Route 20 has two sprite sets, $01 (West) and $0A (East). The only thing that differs from above is that the splitting is more complex.
If X coordinate < $2B Use set $01 End If X coordinate >= $3E Use set $0A End If X coordinate >= $37 If Y coordinate < $08 Use set $0A Else Use set $01 End Else If Y coordinate < $0D Use set $0A Else Use set $01 End End
More visually :
Y X ... 28 29 2A 2B 2C 2D ... 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F 40 ... . ... . . . . . . ... . . . . . . . . . . . . . ... . ... . . . . . . ... . . . . . . . . . . . . . ... . ... . . . . . . ... . . . . . . . . . . . . . ... 05 ... 01 01 01 0A 0A 0A ... 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A ... 06 ... 01 01 01 0A 0A 0A ... 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A ... 07 ... 01 01 01 0A 0A 0A ... 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A ... 08 ... 01 01 01 0A 0A 0A ... 0A 0A 0A 01 01 01 01 01 01 01 0A 0A 0A ... 09 ... 01 01 01 0A 0A 0A ... 0A 0A 0A 01 01 01 01 01 01 01 0A 0A 0A ... 0A ... 01 01 01 0A 0A 0A ... 0A 0A 0A 01 01 01 01 01 01 01 0A 0A 0A ... 0B ... 01 01 01 0A 0A 0A ... 0A 0A 0A 01 01 01 01 01 01 01 0A 0A 0A ... 0C ... 01 01 01 0A 0A 0A ... 0A 0A 0A 01 01 01 01 01 01 01 0A 0A 0A ... 0D ... 01 01 01 01 01 01 ... 01 01 01 01 01 01 01 01 01 01 0A 0A 0A ... 0E ... 01 01 01 01 01 01 ... 01 01 01 01 01 01 01 01 01 01 0A 0A 0A ... 0F ... 01 01 01 01 01 01 ... 01 01 01 01 01 01 01 01 01 01 0A 0A 0A ... . ... . . . . . . ... . . . . . . . . . . . . . ... . ... . . . . . . ... . . . . . . . . . . . . . ... . ... . . . . . . ... . . . . . . . . . . . . . ...
Interior maps
The game loads sprites by request, ie. the sprite tiles will be loaded as required by NPCs present on the loaded map.
Useful notes
Picture numbers
In order to get the ROM address of the tile relative to an entity (people/trainers/items), here is the formula to focus on the entity information:
5 * $4000 + ($7b27 + 4 * (picture_id - 1)) % $4000
Then the entity information is stored according to this:
2 bytes - Tile address 1 byte - Total size of tile data 1 byte - Bank ID
So then, address = bankID * 0x4000 + tileAddr % 0x4000
Event displacement formula
$C6EF + (Map width) + (Map width + 6) * (Y offset) + (X offset)
Pointers
R/B Pointers to Map Headers: 0x01AE R/B Map Header Banks: 0xC23D R/B Pointer to Tileset Headers: 0xC767
Misc.
Maximum tileset size in VRAM: 6 rows of 16 tiles (9000-95FF)
Credits
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% RGBY Map Headers & Stuff That Goes With It Version 1.6 -Feel free to distribute this document and/or edit it. -Try and credit people you get info from and/or write how you updated. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Things that need adding: ------------------------ More information about tilesets, and info about what sprites get loaded for which maps. Updates: -------- v1.6 by ISSOtm: Re-formatted the document, less ASCII and more wiki. v1.5.1 by Sawakita: Fixed Tileset Header, completely (including last byte: Animation Flag) v1.5 by phire: Completed Tileset Header, except for that last byte. v1.4 Complete object data info with details about picture id. v1.3 Edit by Cartmic to include almost completed tileset header information and Hat's further clarification on what the X/Y Movement of the Connection means. v1.2: By ubitux: Add some information about connections and distinction between entities (People/Trainers/Items) v1.11: Typo fixed by Hat: "01 = North" changed to "01 = East". Thanks to IIMarckus for pointing that out. v1.1: By Hat, minor improvements. v1.0: Original version typed up by Hat. Main Credits (Structure Information): ------------------------------------- [Xeon] Wrote some stuff about Map Headers and Object Data. Which this document expands upon, a bit. [F-Zero] & [Tauwasser] Wrote a document (in German) explaining how GSC connection data is stored. RGBY connection data is stored almost the same way, it turns out, so that helped a great deal. All that is really different is the values you add the amount of blocks to. ...............................................................................