10 Custom Border Dimensions
psf edited this page 2024-09-21 08:02:13 -07:00

Every map in the game is surrounded by a repeating group of metatiles called the border. In Emerald, these borders are always 2x2, while in FireRed/LeafGreen they can have different dimensions.

This tutorial will go over the steps to allow borders with different dimensions to be created in Emerald.

Note that Porymap version ≥ 4.0.0 supports this feature, so after these changes are made you will be able to edit the border dimensions in Porymap like you would for a pokefirered project.

1. Add dimension fields to MapLayout

First we will add 2 new fields to struct MapLayout in include/global.fieldmap.h to hold the width and height of the border.

struct MapLayout
{
    /*0x00*/ s32 width;
    /*0x04*/ s32 height;
    /*0x08*/ u16 *border;
    /*0x0c*/ u16 *map;
    /*0x10*/ struct Tileset *primaryTileset;
    /*0x14*/ struct Tileset *secondaryTileset;
+   u8 borderWidth;
+   u8 borderHeight;
};

2. Use dimensions fields to read border data

Next we need to use these new fields when we're trying to access border data. Depending on which repo you're using, find the definition of GetBorderBlockAt in src/fieldmap.c, and replace it with the version below.

pokeemerald

#define GetBorderBlockAt(x, y) ({                                                                 \
    u16 block;                                                                                    \
    s32 xprime;                                                                                   \
    s32 yprime;                                                                                   \
                                                                                                  \
    const struct MapLayout *mapLayout = gMapHeader.mapLayout;                                     \
                                                                                                  \
    xprime = x - MAP_OFFSET;                                                                      \
    xprime += 8 * mapLayout->borderWidth;                                                         \
    xprime %= mapLayout->borderWidth;                                                             \
                                                                                                  \
    yprime = y - MAP_OFFSET;                                                                      \
    yprime += 8 * mapLayout->borderHeight;                                                        \
    yprime %= mapLayout->borderHeight;                                                            \
                                                                                                  \
    block = mapLayout->border[xprime + yprime * mapLayout->borderWidth] | MAPGRID_COLLISION_MASK; \
})

pokeemerald-expansion

static inline u16 GetBorderBlockAt(int x, int y)
{
  u16 block;
  s32 xPrime;
  s32 yPrime;
  
  const struct MapLayout *mapLayout = gMapHeader.mapLayout;

  xPrime = x - MAP_OFFSET;
  xPrime += 8 * mapLayout->borderWidth;
  xPrime %= mapLayout->borderWidth;

  yPrime = y - MAP_OFFSET;
  yPrime += 8 * mapLayout->borderHeight;
  yPrime %= mapLayout->borderHeight;

  return (mapLayout->border[xPrime + yPrime * mapLayout->borderWidth] | MAPGRID_COLLISION_MASK);
}

3. Add border dimension data

Now we need to specify what the border dimensions are for each map layout. Because Emerald's are all 2x2 by default, this can be done quickly with a find and replace. In data/layouts/layouts.json, make the following substitution.

Find:

"primary_tileset"

Replace:

"border_width": 2,
"border_height": 2,
"primary_tileset"

4. Update the mapjson tool

The tool that converts layouts.json to data needs to know what to do with these fields. Update generate_layout_headers_text in tools/mapjson/mapjson.cpp.

             << "\t.4byte " << json_to_string(layout, "secondary_tileset") << "\n";
-        if (version == "firered") {
            text << "\t.byte " << json_to_string(layout, "border_width") << "\n"
                 << "\t.byte " << json_to_string(layout, "border_height") << "\n"
                 << "\t.2byte 0\n";
-        }
        text << "\n";

Note the .2byte 0 is because structs are aligned to 4 byte boundaries. The new border width/height fields are 1 byte each, so we need an additional 2 bytes of padding. This may not be true if you've already made other changes to struct MapLayout, or if you use different sizes for the new dimension fields.

5. Rebuild and test in Porymap

  • Make sure to make clean and rebuild before attempting changes to ensure mapjson and all the map data gets rebuilt with the new map layout.

  • Open your project with Porymap, and under the Maps tab in Options -> Project Settings... check the Enable Custom Border Size option. Then select OK and reload your project.

  • You should now be able to change the size of the border with the Change Dimensions button while on the Map tab.