-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Triple layer metatiles
--UPDATE THIS WIKI PAGE TO USE TRUE TRIPLE LAYER METATILES IN PORYMAP, AND MENTION PORYMAP--
Overworld maps are built out of blocks of tiles. Those blocks get placed in three overlapping layers, but only two layers can be used at a time. A popular binary hack allows triple-layer blocks, using all three layers at once by combining two blocks: one for the bottom and middle layers, and a subsequent "burner" block for the top layer.
To allow this in pokeemerald, just edit src/field_camera.c:
static void DrawMetatile(s32 metatileLayerType, u16 *metatiles, u16 offset)
{
switch (metatileLayerType)
{
+ case 3: // LAYER_TYPE_TRIPLE
+ // Draw metatile's bottom layer to the bottom background layer.
+ gBGTilemapBuffers3[offset] = metatiles[0];
+ gBGTilemapBuffers3[offset + 1] = metatiles[1];
+ gBGTilemapBuffers3[offset + 0x20] = metatiles[2];
+ gBGTilemapBuffers3[offset + 0x21] = metatiles[3];
+
+ // Draw metatile's top layer to the middle background layer.
+ gBGTilemapBuffers1[offset] = metatiles[4];
+ gBGTilemapBuffers1[offset + 1] = metatiles[5];
+ gBGTilemapBuffers1[offset + 0x20] = metatiles[6];
+ gBGTilemapBuffers1[offset + 0x21] = metatiles[7];
+
+ // Draw next metatile's top layer to the top background layer
+ gBGTilemapBuffers2[offset] = metatiles[12];
+ gBGTilemapBuffers2[offset + 1] = metatiles[13];
+ gBGTilemapBuffers2[offset + 0x20] = metatiles[14];
+ gBGTilemapBuffers2[offset + 0x21] = metatiles[15];
+ break;
case 2: // LAYER_TYPE_
// Draw metatile's bottom layer to the bottom background layer.
gBGTilemapBuffers3[offset] = metatiles[0];
gBGTilemapBuffers3[offset + 1] = metatiles[1];
gBGTilemapBuffers3[offset + 0x20] = metatiles[2];
gBGTilemapBuffers3[offset + 0x21] = metatiles[3];
// Draw transparent tiles to the middle background layer.
gBGTilemapBuffers1[offset] = 0;
gBGTilemapBuffers1[offset + 1] = 0;
gBGTilemapBuffers1[offset + 0x20] = 0;
gBGTilemapBuffers1[offset + 0x21] = 0;
// Draw metatile's top layer to the top background layer.
gBGTilemapBuffers2[offset] = metatiles[4];
gBGTilemapBuffers2[offset + 1] = metatiles[5];
gBGTilemapBuffers2[offset + 0x20] = metatiles[6];
gBGTilemapBuffers2[offset + 0x21] = metatiles[7];
break;
case 1: // LAYER_TYPE_COVERED_BY_OBJECTS
// Draw metatile's bottom layer to the bottom background layer.
gBGTilemapBuffers3[offset] = metatiles[0];
gBGTilemapBuffers3[offset + 1] = metatiles[1];
gBGTilemapBuffers3[offset + 0x20] = metatiles[2];
gBGTilemapBuffers3[offset + 0x21] = metatiles[3];
// Draw metatile's top layer to the middle background layer.
gBGTilemapBuffers1[offset] = metatiles[4];
gBGTilemapBuffers1[offset + 1] = metatiles[5];
gBGTilemapBuffers1[offset + 0x20] = metatiles[6];
gBGTilemapBuffers1[offset + 0x21] = metatiles[7];
// Draw transparent tiles to the top background layer.
gBGTilemapBuffers2[offset] = 0;
gBGTilemapBuffers2[offset + 1] = 0;
gBGTilemapBuffers2[offset + 0x20] = 0;
gBGTilemapBuffers2[offset + 0x21] = 0;
break;
case 0: // LAYER_TYPE_NORMAL
// Draw garbage to the bottom background layer.
gBGTilemapBuffers3[offset] = 0x3014;
gBGTilemapBuffers3[offset + 1] = 0x3014;
gBGTilemapBuffers3[offset + 0x20] = 0x3014;
gBGTilemapBuffers3[offset + 0x21] = 0x3014;
// Draw metatile's bottom layer to the middle background layer.
gBGTilemapBuffers1[offset] = metatiles[0];
gBGTilemapBuffers1[offset + 1] = metatiles[1];
gBGTilemapBuffers1[offset + 0x20] = metatiles[2];
gBGTilemapBuffers1[offset + 0x21] = metatiles[3];
// Draw metatile's top layer to the top background layer, which covers event object sprites.
gBGTilemapBuffers2[offset] = metatiles[4];
gBGTilemapBuffers2[offset + 1] = metatiles[5];
gBGTilemapBuffers2[offset + 0x20] = metatiles[6];
gBGTilemapBuffers2[offset + 0x21] = metatiles[7];
break;
}
schedule_bg_copy_tilemap_to_vram(1);
schedule_bg_copy_tilemap_to_vram(2);
schedule_bg_copy_tilemap_to_vram(3);
}
This is slightly different from the binary technique. The topmost third layer comes from the "burner" block's top layer, not its bottom layer. That means the burner block's bottom layer can still be used. So it's not really "wasted" because it can still be useful on its own.
For example, if the first block has a bottom layer of water and a top layer of shoreline, and the burner block has a bottom layer of grass and a top layer of tree, then the first block will show the tree above the water and shoreline, while the burner block will show the tree above the grass.
The problem is, porymap doesn't support any layer types besides the default ones: "Normal - Middle/Top", "Covered - Bottom/Middle", and "Split - Bottom/Top". (Their correspondence to the cases in the DrawMetatile
code should be apparent.) Possible workarounds:
-
Keep an eye on this issue to see if porymap adds support for custom layer types.
-
Build your own copy of porymap, but edit src/ui/tileseteditor.cpp to support triple-layer blocks. After the line:
this->ui->comboBox_layerType->addItem("Split - Bottom/Top", 2);
just add another line:
this->ui->comboBox_layerType->addItem("Triple - Bottom/Middle/Top", 3);
-
Hex-edit the metatile_attributes.bin files to use layer type 3. This is cumbersome compared to using porymap, but it's not a complex format. Every two bytes correspond to a block: the first byte is its behavior value, the second byte is its layer type but shifted to the first nybble. So Normal is $00, Covered is $10, and Split is $20. Just find the pair of bytes for your triple-layer block (the first one, not the burner one) and change its layer type byte to $30.