-
Notifications
You must be signed in to change notification settings - Fork 37
Tile Data
Any addresses and data structures mentioned here are specifically for EarthBound (USA), on the SNES. Mother 2 on the SFC works basically the same way, just with different addresses. Mother 2's GBA port seems to work a lot differently, though. This guide will go over how CoilSnake stores the data in a project, rather than just how the data is actually stored in the ROM. Complete support for everything map-related in EarthBound would require some things that the current tools just don't support.
EarthBound maps can choose from one of 32 different tilesets. Several of them share graphical, collision, arrangement and animation information, though, so if you exclude those, there are only 20. To distinguish the two concepts, the 20 will be referred as “fat tilesets” or “FTSes,” and the 32 as “tilesets.” (This might change over time.)
First, raw graphics, or as many people call them, “minitiles.” EarthBound’s maps all use 4bpp graphics divided into two layers, foreground and background. Each foreground tile is strictly paired with a background tile; the first background tile goes with the first foreground tile, the 2nd with the 2nd, etc. EarthBound dedicates space for 512 background tiles and 384 foreground tiles for every fat tileset. This means that 128 background tiles do not have a matching foreground tile; in this case, the tilemap (arrangement) specifies a minitile number greater than or equal to 384, EarthBound will always load foreground tile 0 into background layer 2; the foreground layer. Foreground tile 0, like background tile 0, is always completely transparent for all tilesets. Additionally, unlike actual loaded foreground tiles, EarthBound will not set the "BG priority bit" on this tile, causing it to be rendered (transparently anyway) behind the background tile and sprites instead of in front. In the ROM, a 32-bit pointer to the minitiles for each fat tileset is stored at a table at $EF105B
(EarthBound uses HiROM, so just subtract C00000
to get the file address). These graphics are compressed using HAL Laboratories’ standard compression tools. (See devinacker/exhal on GitHub for a library and utility that handles that format pretty well.)
In a CoilSnake project, minitiles are stored in the first third of a text file with the extension .FTS in the Tilesets folder. Each tile is stored in three lines: the first line is the background tile, the second line is the foreground tile, and the third line is blank. Tiles are stored as hexadecimal numbers (one character = one pixel), ordered left-to-right top-to-bottom as you'd expect.
Each tileset includes one or more palettes. These can be easily identified with a number, e.g. tileset 31’s first palette might be referred to as 31/0 in UI or in YAML files. Each palette consists of one or more event palettes, which enable the game to change the palette for a map based on what event flags have been set or cleared over the course of the story. Each event palette includes 6 subpalettes of 16 colors each; these subpalettes may be numbered 2 through 7, reflecting their placement in CGRAM in-game. Each event palette stores metadata in the transparent colors of certain subpalettes. This includes the flag for whether or not the palette is valid, an offset to the next event palette to try in a long linked list, some properties related to color animation effects, and custom sprite palette information sometimes. (This information will be covered later.) All you need to know when dealing with EB Project Editor files is, most of those transparent colors are set to 0; instead, the information that’s normally there is moved into map_palette_settings.yml
in a CoilSnake project. It is recommended not to try and change those 000s unless you're trying to preview ported maps.
In the ROM, 32-bit palette pointers for each tileset (not FTS) can be found at $EF10FB
. There’s no compression involved... In a CoilSnake project, the initial event palettes in a chain of them are stored in the middle portion of an FTS file. Each event palette is one line of text. The first two characters of the line indicate a (non-fat) tileset number and a palette number in base-32. (Base 32 meaning it counts up from 0-9 then continues from a to v... don’t ask why.) Colors are stored in a similar format: one base-32 character for each color channel, in the order red-green-blue.
All subsequent event palettes are kept in the aforementioned map_palette_settings.yml
in a CoilSnake project. They're stored in exactly the same format as the FTS file palettes, except that they don't include the tileset/palette number characters (since that would be redundant). All the metadata for the event palettes is also stored in this file:
Event flag (first color in subpalette 2). This marks the condition for using a given event palette instead of the next one in the chain. Flag & 0x7FFF
is the event flag number; flag & 0x8000
is 0 if the flag should be set, nonzero if the flag should be unset. (Technically the game logic is "if flag > 0x8000
then that flag must be unset," but since flag 0 doesn't exist, it's basically the same thing). If the whole flag number including the most significant bit is 0x0000
, that means the palette will be used unconditionally. The end of the chain.
Next event palette offset (first color in subpalette 3). A 16-bit pointer to the next event palette to try, if the currently checked one did not have its condition fulfilled. (The bank of the pointer is the same as the table of map palette pointers.)
Sprite palette ID (first color in subpalette 4). One of the six subpalettes may optionally be used as a palette for NPC/object/etc sprites. 0 indicates no custom colors are used; a nonzero number attempts to copy that subpalette number on top of the NPC palette. Technically it's valid for this to be outside the range 2-7, but… nobody really uses this feature to begin with, because CoilSnake makes it a bit annoying to work with sprites with map-based palettes.
Palette animation number (also called "flash effect"; first color in subpalette 5). If it's 0, then there's no animation for this event palette. If it's nonzero, then it specifies an index in the palette animation table (1-indexed). (More details on that table later.)
Finally, to close out everything related to .FTS files, let's look at arrangements and collision. The smallest possible "metatile" that can be placed in a given sector of the map is 4 tiles wide by 4 tiles tall. ("Metatiles" are just very small reusable tilemaps. A Link to the Past uses a very similar system with 32x32 pixel blocks natively.) It can be constructed by any 16 two-layer minitiles, each with any of the 6 valid subpalettes (2-7). Every minitile in a metatile can also be horizontally and/or vertically flipped, of course. Any given fat tileset can have up to 960 metatiles.
Every metatile has a piece of collision data associated with it. Collision data consists of 16 bytes, one for each minitile that makes up the metatile. The bits in the byte have the following meanings:
0x01: foreground covers NPCs/sprites
0x02: background covers NPCs/sprites; when used together with 0x80, indicates a "desk" or "countertop" that lets you talk to NPCs on the other side
0x04: terrain causes damage to the party
when used on its own, makes the party sweat, and occasionally inflicts sunstroke on a party member
when used together with 0x08, submerses NPCs deeper than normal ("deep water") causing HP damage
0x08: terrain is water you can wade in
0x10: indicates a location in the metatile where a "doorlike" object can be placed
0x20: (unused)
0x40: (unused, alternate version of solid)
0x80: solid collision
Only the foreground tiles ever render in front of sprites, never background tiles. The difference between the bits 0x01
and 0x02
is that for 0x01, a sprite entity positioned on that tile will have the lower half of the sprite reduced in priority and rendered behind foreground tiles. For 0x02, the upper half of the sprite will be reduced in priority and rendered behind foreground tiles. (And combined 0x03
will have both halves of the sprite rendered behind foreground tiles. In the ROM, 32-bit pointers to arrangements are stored in a table at $EF10AB
. The arrangement data itself is compressed. It goes through the first metatile left to right, top to bottom, then the second metatile, then the third, etc. The game does not store more metatiles than necessary; in the vanilla game, fat tileset 0 contains 832 metatiles, while fat tileset 1 contains 845 metatiles.)
In the ROM, 32-bit pointers to all the collision data for each fat tileset are stored in a table at $EF117B
. The data at this address is an array of (up to) 960 near pointers of some constant bank (in the vanilla game, this is bank D8; there are LDA #CollisionDataBank
instructions at $C00D48
and at $C00DCA
). This structure allows every fat tileset to share the same collision data to save space.
In an FTS file that EB Project Editor uses, the arrangement and collision data is interleaved at the end of the FTS file. There are 1024 lines of text, one for each metatile (yes, this is more than 960... There isn't a known reason why 1024 was chosen, except for just not knowing the real limit). Each of the 16 minitiles is represented by 6 characters. The first four characters are the tilemap entry, converted to hexadecimal, and the last two characters are the collision byte for that minitile area converted to hexadecimal. For example, the characters "086780"
would be divided into "0867"
(minitile number 0x067
, subpalette 2, no flipping) and "80"
(solid collision).
Also known as Tile Animations, are covered here.
Occasionally, the game will change physical things about the map depending on your story progress – a bridge that was once broken has been repaired the next time you visit it, or an invisible wall that triggered dialogue will be replaced with normal walkable terrain. This is done at the fat tileset level. Each fat tileset can have a list of flag conditions that cause a map change to occur. Each of these flag conditions is associated with a list of metatiles to swap out for different metatiles in the fat tileset. I DIDN'T FINISH WRITING THIS YET FIXME Combining metatiles into maps EarthBound's maps are all combined into one big, um… atlas, that uses a global set of coordinates. This means that if you have the ability to walk out of bounds, you can travel from any map to any other map in the whole game if you walk for long enough. You can get an idea for what that looks like here
TODO: explain sectors, and plates, and whatever else exists
- Overworld Sprites
- Battle Backgrounds
- Battle Sprites
- Title Screen
- Window Graphics
- Logos
- Fonts
- Animations
- Swirls
- EB Project Editor
- Tile Data
- Tile Editor
- Collision Data
- Adding Map Palettes
- Map Editor
- Doors
- Warp Styles
- Enemy Placement
- Hotspots