-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Custom Border Dimensions
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.
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;
};
The following changes will all be in src/fieldmap.c.
We'll be simplifying some existing functions with a few new macros. First, insert the following above MapGridGetZCoordAt
.
#define MapGridGetBorderTileAt(x, y) ({ \
u16 block; \
s32 xprime; \
s32 yprime; \
\
const struct MapLayout *mapLayout = gMapHeader.mapLayout; \
\
xprime = x - 7; \
xprime += 8 * mapLayout->borderWidth; \
xprime %= mapLayout->borderWidth; \
\
yprime = y - 7; \
yprime += 8 * mapLayout->borderHeight; \
yprime %= mapLayout->borderHeight; \
\
block = mapLayout->border[xprime + yprime * mapLayout->borderWidth] | METATILE_COLLISION_MASK; \
})
#define AreCoordsWithinMapGridBounds(x, y) (x >= 0 && x < gBackupMapLayout.width && y >= 0 && y < gBackupMapLayout.height)
#define MapGridGetTileAt(x, y) (AreCoordsWithinMapGridBounds(x, y) ? gBackupMapLayout.map[x + gBackupMapLayout.width * y] : MapGridGetBorderTileAt(x, y))
Now we'll use these new macros. Replace the MapGridGetZCoordAt
, MapGridIsImpassableAt
, and MapGridGetMetatileIdAt
functions with the versions below.
u8 MapGridGetZCoordAt(int x, int y)
{
u16 block = MapGridGetTileAt(x, y);
if (block == METATILE_ID_UNDEFINED)
return 0;
return block >> METATILE_ELEVATION_SHIFT;
}
u8 MapGridIsImpassableAt(int x, int y)
{
u16 block = MapGridGetTileAt(x, y);
if (block == METATILE_ID_UNDEFINED)
return 1;
return (block & METATILE_COLLISION_MASK) >> METATILE_COLLISION_SHIFT;
}
u32 MapGridGetMetatileIdAt(int x, int y)
{
u16 block = MapGridGetTileAt(x, y);
if (block == METATILE_ID_UNDEFINED)
return MapGridGetBorderTileAt(x, y) & METATILE_ID_MASK;
return block & METATILE_ID_MASK;
}
Then in GetMapBorderIdAt
, make the following change.
int GetMapBorderIdAt(int x, int y)
{
- struct MapLayout const *mapLayout;
- u16 block, block2;
- int i, j;
- if (x >= 0 && x < gBackupMapLayout.width
- && y >= 0 && y < gBackupMapLayout.height)
- {
- i = gBackupMapLayout.width;
- i *= y;
- block = gBackupMapLayout.map[x + i];
- if (block == METATILE_ID_UNDEFINED)
- {
- goto fail;
- }
- }
- else
- {
- mapLayout = gMapHeader.mapLayout;
- j = (x + 1) & 1;
- j += ((y + 1) & 1) * 2;
- block2 = METATILE_COLLISION_MASK | mapLayout->border[j];
- if (block2 == METATILE_ID_UNDEFINED)
- {
- goto fail;
- }
- }
- goto success;
-fail:
- return -1;
-success:
+ if (MapGridGetTileAt(x, y) == METATILE_ID_UNDEFINED)
+ return -1;
if (x >= (gBackupMapLayout.width - 8))
{
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"
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 " << layout["primary_tileset"].string_value() << "\n"
- << "\t.4byte " << layout["secondary_tileset"].string_value() << "\n\n";
+ << "\t.4byte " << layout["secondary_tileset"].string_value() << "\n"
+ << "\t.byte " << layout["border_width"].int_value() << "\n"
+ << "\t.byte " << layout["border_height"].int_value() << "\n"
+ << "\t.2byte 0\n\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.
-
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. -
If you've opened your project with Porymap before there will be a
porymap.project.cfg
file in your root folder. In that file, setuse_custom_border_size
to1
. -
Open your project with Porymap and you should now be able to change the size of the border with the
Change Dimensions
button while on the Map tab.