1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
|
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](https://github.com/pret/pokeemerald/blob/master/include/global.fieldmap.h) to hold the width and height of the border.
```diff
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
The following changes will all be in [src/fieldmap.c](https://github.com/pret/pokeemerald/blob/master/src/fieldmap.c).
We'll be simplifying some existing functions with a few new macros. First, insert the following above `MapGridGetZCoordAt`.
```c
#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.
```c
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.
```diff
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))
{
```
## 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](https://github.com/pret/pokeemerald/blob/master/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](https://github.com/pret/pokeemerald/blob/master/tools/mapjson/mapjson.cpp).
```diff
<< "\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.
## 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.
- If you've opened your project with Porymap before there will be a `porymap.project.cfg` file in your root folder. In that file, set `use_custom_border_size` to `1`.
- 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.
|