-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Random Info, Tips, and Tricks
What follows is a document full of random information learned from experience of working on a pokeemerald hack. If you have some information that would have helped you greatly to know earlier on, I encourage you to add it to this list.
- You'll find a lot of comments in pokeemerald with what look like addresses next to various functions in both C code and Assembly code. These addresses simply indicate where the function sits in memory when building the vanilla game. It has no bearing on where the function ends up in ROM once you start changing code.
- In Assembly files (*.s and *.inc), the
@
character is simply a line comment character. It doesn't mean the function is at this location in memory.
- In Assembly files (*.s and *.inc), the
A metatile is the building block of a map's tilemap. They make up the majority of the graphics in the overworld.
- Metatiles are made up of 4 graphical tiles per layer. In vanilla, a meta tile can only be on two of the three background layers at a time.
- A metatile also has a behavior, which determines various additional information about it, from whether it acts like a warp to whether it can generate encounters.
- A warp must be sitting on a metatile which is considered a warp-capable metatile in order for the warp to function.
- Arrow Warps are a type of metatile behavior which triggers a warp when the player walks off the tile in the direction of the arrow. These are the types of warps used in caves for southern exits; the actual glowing cave tile sitting below the player does nothing mechanically to make up the warp.
- Animated door warps should have collision on their tile: the animation triggers only when the player bumps into the door. If there's no collision on the animated door, it is possible for the player to approach the door sideways and walk up onto it, which won't trigger the animation.
- Furthermore, objects placed on top of animated doors do not block the animated door from working. To disable an animated door warp, you need to change the metatile to one without an
MB_ANIMATED_DOOR
behavior. - Which door animation to use is determined by the lower tile's metatile id. his id is used as an "index" into the array named gDoorAnimGraphicsTable. This use as an index is global across the game, so if you put a new
MB_ANIMATED_DOOR
tile on an already existing id, the game will use the existing animation already. - In vanilla emerald, animated door animations only use one layer of tiles, which replaces all of the tiles in both metatiles of a door during the animation.
- Furthermore, objects placed on top of animated doors do not block the animated door from working. To disable an animated door warp, you need to change the metatile to one without an
Object Events are the various sprites that can move around the overworld, anywhere from the NPCs, to the Item Balls, to the Ship sitting at dock. Each object has a localId that can be referenced in scripts for various purposes.
- Object Events that are more than 1 or 2 tiles off-screen are despawned and removed from the Object Event Array. Object Events that come within this range when the camera moves close to them are recreated from the in-memory Object Event Template list.
- If a script wants to manipulate an object that is outside camera range, it must be explicitly added.
- A map's object events do not get instantiated until the player enters the map. Connected maps will not have events on them until the player steps onto the map itself. This is why there's usually a buffer space of at least 7 squares (the radius of the camera view area) between routes and towns in the map design.
- It is possible to place objects off the edge of a given map; the game doesn't care. However, if you try and put an object in a negative dimension (off the top or left) then various tooling and typings may convert the value to unsigned, thus dropping the negative. If you fix everything along the way to deal with the negative number, the emerald engine itself is fine with it.
A tilemap tile is made up of 3 parts, the metatile (the graphics of the tile), the collision (whether an object is allowed to walk onto it), and the elevation (see this section).
- Objects cannot move directly from one elevation to another. Objects at two different elevations cannot interact with one another.
- The common walk through walls cheat code nullifies the check for collision but doesn't nullify the check for elevation, which is why you can still hit walls sometimes while using it, and why it's difficult to get onto and off of water.
- The term "elevation" evokes a height, but this is a misnomer: having a higher elevation does not mean an object or tile will be in front of objects at lower elevations. Each "elevation" means something different.
- Elevation 0 is the transition elevation. Objects stepping onto this elevation will keep their previous elevation, and objects stepping off of this elevation will be allowed to step onto tiles of any other elevation.
- Elevation 1 is the surfing elevation. If the player is in the surfing state, bumping into a tile on elevation 3 will end surfing and return the player to land. (The transition to surfing itself is handed by the metatile behavior.)
- Elevation 3 is the standard elevation for tilemaps, where "high tiles" (on bg layer 1) will over objects, while "low tiles" (bg layers 2 and 3) will go under.
- Elevation 4 is the standard elevation for going over the "high tiles" (on bg layer 1).
- Elevation 15 is the bridge elevation. Objects on this elevation will keep their previous elevation, but unlike elevation 0, are not allowed to step off onto elevations other than the one they're keeping.
- TODO: Add info about other elevations.
- Triggers at an elevation that does not match the player's elevation will not trigger. This limitation is in place so that a trigger on a bridge does not get hit when the player surfs under said bridge.
- Remember to place triggers on water at elevation 1, the surfing elevation!
- The player "bumps" into an elevation difference: animated door warps will still be triggered from below even if they're at a different elevation.
- The elevation of an object is set to the tile the object is currently sitting on. Attempting to set the elevation of an object via scripting or C will do nothing as it will be overwritten on the next frame.
- The exceptions to this are elevations 0 and 15.
Every map has a scripting header, which can contain a number of scripts that can run in various situations. The following describes each of the vanilla map script types.
- The various types of map scripts are documented in constants/map_scripts.h.
- It's most common to do metatile changes to the map during
ON_LOAD
or rarelyON_RESUME
, as the tiles haven't yet been drawn when these scripts are called. Other times you will likely need to make the special script call toDrawWholeMapView
in order to redraw the map. - The
ON_RETURN_TO_FIELD
script is used to set up transient field effects which are not part of the tilemap or object list, such as the rotation gates in Fortree Gym. - Every script on a map header (except the ones in
FRAME_TABLE
) is run in the "immediate script context", which is to say it is run like a function call, and the game won't continue as long as the script is running. This means if you call any of thewait*
script commands in these scripts, you will just hardlock the game, as nothing else is running until your script finishes, and your script is waiting.- If you want a cutscene to run upon map load, you need to put the script in the map's
FRAME_TABLE
. It will then run at the first possible opportunity, just before control would be given back to the player.
- If you want a cutscene to run upon map load, you need to put the script in the map's
VRAM, or Video RAM, is the part of memory where the GBA graphics are stored for use on the screen itself.
- VRAM is not just another part of memory. It has special write access limitations.
- On hardware and emulators that are accurate enough like mGBA, VRAM can only be written to during VBLANK.
- VRAM doesn't like you trying to write u8s to it. When you do, it tends to duplicate the u8's bits into a u16. VRAM works in u16s.
- This limitation is the reason there's a
LZ77UnCompWRAM
and aLZ77UnCompVRAM
.
- This limitation is the reason there's a
- Consider using the
gDecompressionBuffer
to decompress things before pushing them straight to VRAM via a DMA transfer request. - (As of June 2019) The emulator no$GBA does not seem to emulate the above VRAM write restrictions, so what looks perfectly fine on no$GBA might cause problems on another emulator or real hardware.
- Backgrounds have tile data and tilemap data. The latter references the former. Tile data is the actual pixels that make up a graphic. Tilemap data is which blocks of tile data make up the background map.
-
BG_CHAR_ADDR()
resolves a pointer to the tile data. -
BG_SCREEN_ADDR()
resolves a pointer to the tilemap data.
-
The GBA has 32 palettes of 16 colors each: 16 palettes are for backgrounds, and 16 are for sprites.
- See the Overworld section for palettes used in the overworld.
- The game has two palette buffers in Working RAM:
gPlttBufferUnfaded
holds the unprocessed palettes, andgPlttBufferFaded
holds the same palettes after they've been processed through fading effects. Every frame, the game copies fromgPlttBufferFaded
directly into Palette Memory in VRAM.- Some modifications may tell you to add a third palette buffer for screen effects like weather or time of day. The vanilla game uses the faded buffer for weather effects.
- There are several blending and tinting functions for the palettes already in vanilla emerald, including a sepia tone and monochrome ons.
- Take care when using the blending functions, as the functions do not have any concept of clamping, and so can easily wrap around and cause odd (but harmless) visual effects, especially when blending with a color that's not black or white.
- The vanilla game's lightning effect uses a table of tints per color, carefully selected to not do the above wrapping.