CHAPTER 2: Opcodes and the Event Engine
Event Engine
Now that we have basic functioning events, we can move on to more complex ones. To do more with our events, first we need to learn a bit. What’s actually going on here?
When the chapter starts, the BeginningScene
event plays automatically. The piece responsible for this is the event engine. The event engine reads your event scene, takes the instructions you give it within that scene, and performs actions based on your instructions. Each of these instructions is known as an opcode. Of what we’ve covered so far, only 3 are opcodes: LOAD commands, ENUN, and ENDA. The rest of the event codes we’ve covered are just fancy ways of setting up data for other game systems to read. There are a number of event engine systems, including flags and memory slots that we have covered already, but there are others we haven’t covered, as well as event opcodes for manipulating them.
Evbits
Evbits are a set of event engine flags that have specific meanings. Many codes modify and/or behave differently based on their state.
-
Evbit 0 is the “ignore queued scenes” evbit.
-
Evbit 1 is the “scene is active” evbit.
-
Evbit 2 is the “scene-skipping” evbit.
-
Evbit 3 is the “text-skipping” evbit.
-
Evbit 4 is the “prevent scene-skipping” evbit.
-
Evbit 5 is the “prevent text-skipping” evbit.
-
Evbit 6 is the “prevent text-fast-forwarding” evbit.
-
Evbit 7 is the “no end fade” evbit.
-
Evbit 8 is the “faded in” evbit.
-
Evbit 9 is the “camera follows moving units” evbit.
-
Evbit 10 is the “moving to another chapter” evbit.
-
Evbit 11 is the “changing game mode” evbit.
-
Evbit 12 is the “gfx locked” evbit.
Most of these you will never need to set individually, as they are set/unset by other codes automatically.
CHECK_EVBIT evbitID
returns the value of the specified evbit to sC.
EVBIT_T evbitID
will set the specified evbit to true.
EVBIT_F evbitID
will set the specified evbit to false.
There exists the macro NoFade
, shorthand for EVBIT_T 7
, which if placed just before an ENDA will cancel the screen fading in and back out at the end of your event. You likely want to use this 99.9% of the time. If the evbitID is negative, the evbit to set will be read from s2.
EVBIT_MODIFY setting
will configure evbits en masse related to skipping. The settings are as follows:
-
0
allows scene skipping, dialogue skipping and dialogue fast-forwarding.
-
1
disallows scene skipping, dialogue skipping and dialogue fast-forwarding.
-
2
allows scene skipping and dialogue skipping, but disallows dialogue fast-forwarding.
-
3
disallows scene skipping, but allows dialogue skipping and dialogue fast-forwarding.
-
4
disallows scene skipping and dialogue skipping, but allows dialogue fast-forwarding.
Any other setting will freeze the game. If the setting is non-zero and the scene is currently being skipped, evbit 2 is set to false and evbit 8 is set to true. You cannot read the setting from a memory slot.
Flags
As we covered earlier, flags run from 0-40 for temporary flags and 101-300 for permanent flags, a number of which from each group having reserved functions.
ENUT flagID
will set the specified flag.
ENUF flagID
will clear the specified flag.
If flagID is negative, the flag to set or clear will be read from s2.
CHECK_EVENTID flagID
returns the true/false state of the specified flag to sC.
Memory Slots
As covered prior, memory slots range from s0-sD with some having specific reserved functions.
SVAL slotID value
will set the given slot to the given value. Setting s0 has no effect.
You can perform a number of mathematical operations with the values in slots.
SADD destSlotID operand1slot operand2slot
will put operand1 + operand2
in destSlotID
SSUB destSlotID operand1slot operand2slot
will put operand1 - operand2
in destSlotID
SMUL destSlotID operand1slot operand2slot
will put operand1 * operand2
in destSlotID
SDIV destSlotID operand1slot operand2slot
will put operand1 / operand2
in destSlotID
SMOD destSlotID operand1slot operand2slot
will put operand1 % operand2
in destSlotID
SAND destSlotID operand1slot operand2slot
will put operand1 & operand2
in destSlotID
SORR destSlotID operand1slot operand2slot
will put operand1 | operand2
in destSlotID
SXOR destSlotID operand1slot operand2slot
will put operand1 ^ operand2
in destSlotID
SLSL destSlotID operand1slot operand2slot
will put operand1 << operand2
in destSlotID
SLSR destSlotID operand1slot operand2slot
will put operand1 >> operand2
in destSlotID
Setting destination to s0 has no effect. Note that for all of these operations, all numbers are signed, divisions round towards 0, and right shift is an arithmetic shift.
Vanilla uses SADD destSlotID sourceSlotID s0
to move the value of one slot into another.
Counters
Counters store a value from 0-15 inclusive. There are 8 of them in total, from counter 0 to counter 7. They reset between chapters, but do not reset through suspend, much like temporary flags.
COUNTER_CHECK counterID
returns the value of the specified counter to sC.
COUNTER_SET counterID value
sets the value of the specified counter to the given value. The value cannot be read from a memory slot.
COUNTER_INC counterID
increases the value of the specified counter by 1.
COUNTER_DEC counterID
decreases the value of the specified counter by 1.
COUNTER_INC
and COUNTER_DEC
cannot overflow or underflow the counter; if the value in the counter is 0 and you use COUNTER_DEC
, the value will remain as zero. Similarly, if the value is 15 and you use COUNTER_INC
the value will still be 15.
Event Queue
The event queue is a list of values following a FIFO (First In First Out) model (hence why it’s called a queue). It is generally used to pass a list of values to codes or subscene. As you may recall, sD always holds the current size of the queue. There is room for up to 30 values in the queue.
SENQUEUE slotID
will enqueue the value stored in the given slot.
SENQUEUE
is the same as the above, but defaults to s1.
SDEQUEUE slotID
will dequeue the value at the front of the queue to the given slot.
These systems are all used by different event opcodes, so having an understanding of how they work is important. There is one more set of useful utility codes to know before moving forward:
STAL time
will pause the scene for the given amount of time.
STAL2 time
will pause the game for the given amount of time. If the game speed option is set to fast or the A button is held, the time will decrement 4 times faster.
STAL3 time
will pause the game for the given amount of time. If the game speed option is set to fast or the A button is held, the time will decrement 4 times faster. The pause will end early if the text-skipping evbit is set or the B button is pressed at any point.
No pause will occur if events are currently being skipped. There is no way of getting time from a memory slot. If time is 0, the game will freeze.
Now that we understand our basic systems, we can move on to some more practical features:
Fades
One of the most common things you’ll do in events is fading in and fading out to black or white. There’s a set of four codes for this:
FADU speed
fades out of black.
FADI speed
fades into black.
FAWU
fades out of white.
FAWI
fades into white.
These will set and clear respectively the faded in evbit. Note the speed value is not fade duration, and as such a larger value will be a shorter fade. No fade happens if the scene is being skipped. The event engine will pause until the fade is completed.
Text
There are three pieces to displaying text: setting the text mode, displaying the text, and cleaning up afterwards. First, we set the text mode:
TEXTSTART
will set the text mode to 0, which is standard, no-background dialogue.
REMOVEPORTRAITS
will set the text mode to 1, which is standard dialogue with a background.
CGTEXTSTART
will set the text mode to 2, which is CG text. Using [SetName] at the start of your text displayed in this mode will display the text immediately following in a smaller box in the top corner of the text box. This can be used with CG images or standard conversation BG images.
TUTORIALTEXTBOXSTART
will set the text type to 3, which is the scroll box used in vanilla for tutorials. The location on the screen that this box will be drawn at is determined by coordinates in sB. If the coordinates are -1, the text box will be centered automatically. The macro CenterTutorialTextBox
sets sB to center the box.
SOLOTEXTBOXSTART
will set the text mode to 4, which displays a text box similar to CG text but without the option for a name field. The location on the screen that this box will be drawn at is determined by coordinates in sB. If the coordinates are -1, the text box will be centered automatically. The macro CenterTutorialTextBox
sets sB to center the box.
No matter what text mode we use, the next step is using TEXTSHOW
. This is where we tell the game what text to display by giving it a text ID.
TEXTSHOW textID
displays the given text in the currently set text mode after resetting the text-skipping evbit. In practice, this means that pressing B mid-text will take you to the next TEXTSHOW
.
TEXTSHOW2 textID
displays the given text in the currently set text mode only if the text-skipping evbit is not set. In practice, this means that pressing B mid-text before a TEXTSHOW2 will skip over its text.
Finally, we end our text. This is done with the aptly named TEXTEND
.
TEXTEND
waits for text to end then continues events following from it.
After TEXTEND
, we still need to clean up a few text-related things. REMA
will do this for us:
REMA
ends any running text interpreters, clears text from the screen, removes portraits, and clears the text-skipping evbit.
So, the most basic text event would be laid out as:
TEXTSTART
TEXTSHOW textID
TEXTEND
REMA
But this is a bit cumbersome to type out every time, no? Well, that’s where macros come in! The macro Text(textID)
is the same as the above event, but in a single line. This is much easier to follow than typing all it out every single time.
Text with Backgrounds
There are a few ways to do backgrounds, but by far the easiest is setting the background separately from the text.
BACG backgroundID
displays the given background for the active text mode. If text mode is not 1 or 2, the game will hang.
The default behavior of BACG
is to display a background in text mode 1, which is useful as it means we can do some shenanigans to display text in non-background mode on backgrounds. Due to what is presumably a bug, CG text mode can only display a single CG image. But if we set the background beforehand, we can display whatever type of text we want overtop of it.
To set backgrounds, the vanilla game calls a macro event to fade in the background, which is expressed for our purposes through the macro SetBackground(backgroundID)
. Following this, we can set up text of any type to display overtop of the background.
When we’re done with our background, we call a vanilla macro event to get rid of it, CALL $9EE2C4
. For convenience, we define this as EndConvo
. Text with backgrounds then looks something like this:
SetBackground(backgroundID)
Text(textID)
EndConvo
Mid-Text Events
You may be aware of the text control code [LoadOverworldFaces]
, which can be used to activate events mid-text without ending it. This can be accomplished using TEXTCONT
:
TEXTCONT
returns to text from events.
Now, this alone isn’t enough to get our text to pause for events. Recall that TEXTEND
returns to events from text. Well, [LoadOverworldFaces]
counts as ending text to TEXTEND
. Therefore, you lay out your mid-text events like so:
TEXTSTART
TEXTSHOW textID
TEXTEND
//mid-text events go here
TEXTCONT
TEXTEND
REMA
You can have more than one event break in your text as well. As long as you have a TEXTEND; TEXTCONT
for each one, you can have as many events run as you like.
Clearing
Sometimes, you need to remove everything off of the screen. There’s an event code for this:
CLEAN
clears the top 2 graphics layers, text, and portraits, reloads the font, default map sprite palettes, and UI graphics, and unblocks game graphics logic.
CLEAN
is more or less a nuke button for anything displayed over the map on the screen. If you recall, we used the macro EndConvo
to accomplish this for our backgrounds earlier. The event this is calling contains, among other things, CLEAN
.
Other times, you don’t want to clear absolutely everything from the screen, but just text boxes. There’s also an event code for this:
TEXTCLOSE
will clear any open text box.
More Backgrounds
There are also some other background-related things you can do:
BACKGROUND_CHANGE backgroundID newTextMode speed
will transition from the currently displayed background to the given background as if it were being displayed in the given text mode. This does not change the current text mode. You cannot transition from a non-background text mode to another non-background text mode or the game will freeze. You can transition from a background text mode to a non-background text mode, in which case the current background is just faded out. Valid background text modes are 1 for BGs and 2 for CGs.
BACKGROUND_TOCOLOR backgroundID color speed
displays the given background in the active text mode, and fades it in from the given color at the given speed.
BACKGROUND_FROMCOLOR color speed
fades the currently displayed background out to the given color at the given speed.
Note these cannot be used properly without being in a background text mode, something that we’ve previously avoided doing.
Displaying Portraits
You can always display portraits through your text, but what if you want to do it through events instead? There’s a set of event codes for this purpose:
FACE_SHOW faceSlot portraitID
Slot IDs are as follows:
-
0: FarLeft
-
1: MidLeft
-
2: Left
-
3: Right
-
4: MidRight
-
5: FarRight
-
6: FarFarLeft
-
7: FarFarRight
If portraitID is -1 the ID is read from s2. If portraitID is -2 the portrait in the code’s position will be cleared, along with any open text box. If portraitID is -3 all portraits are cleared, regardless of which code is used. Displaying text immediately after using this may have some side effects, so to be safe you should follow each of these with a STAL
for a few frames as the portrait is loaded in.
Much like in text, you can also move portraits drawn via events to different locations on the screen:
FACE_MOVE fromSlot toSlot
moves the portrait in the given from slot to the given to slot.
Subscenes
Subscenes are events run from within other events.
CALL eventOffset
will pause the current event, go to the event at the given offset, and play that event. When the scene finishes, the current event is unpaused and continues from after the CALL
.
CALL
doesn’t always have to return to the parent scene, however. Remember earlier we mentioned that ENDA
ends your events? Well, the behavior of this changes slightly when you’re in a subscene. If a subscene ends with ENDA
, it will return to the parent scene. However, if it instead ends with ENDB
, it will end all scenes. If you have subscenes within subscenes, using ENDB
will end every event there instead of having to back out to the highest layer to end.
ENDA
ends the active scene. If the active scene is a subscene, it will return to its parent scene.
ENDB
ends the active scene and all parent scenes.
Giving Items and Money
GIVE_ITEM charId
will give an item based on the item ID in s3 to the specified unit, and displays an item get popup.
GIVE_MONEY charId
will give money of the amount in s3 to the specified unit’s faction, and displays a gold get popup.
TAKE_MONEY
will silently take money of the amount in s3 from the player faction. This will now allow money to underflow.
If char ID is 0, the unit used is the first player unit. If char ID is -1, the unit used is the active unit. If char ID is -2, the unit used is the one at the coordinates in sB. If char ID is -3, the unit used is the one whose char ID matches the one in s2.
Music and Sound Effects
MUSC songID
will transition the BGM to the given song.
MUSCFAST songID
will stop the current BGM and transition to the given song slightly faster than MUSC
.
MUSCMID songID
will stop the current BGM and transition to the given song slightly slower than MUSC
.
MUSCSLOW songID
will stop the current BGM and transition to the given song slower than MUSCMID
.
MUSS songID
will transition the BGM to the given song. The previously playing BGM is remembered. You can only remember one song at a time.
MURE speed
will transition the BGM back to the song previously stored using MUSS. You can only remember one song at a time.
MUSI
will lower the BGM volume.
MUNO
will restore the BGM volume to its state pre-MUSI
.
SOUN songID
will play the given song. Does not play if skipping or if sound effects are turned off in the options menu.
Song ID 0x7FFF is silent. If song ID is negative, the ID will be read from s2.
Manually Moving Units
Sometimes, you need to move an already loaded unit to a different location than it was loaded to. These event codes allow you to do so:
MOVE speed charID [x, y]
will move the specified unit to the specified coordinates at the specified speed.
MOVEONTO speed charID targetCharID
will move the specified unit to where the specified target unit is standing.
MOVE_1STEP speed charID direction
will move the specified unit 1 step in the specified direction.
MOVE_DEFINED charID
will move the specified unit according to movement instructions in the event queue. Movement instructions are the same format as REDA
s.
If speed is negative, movement is performed instantly. The higher the speed value, the slower the unit moves. If you are currently skipping, movement is performed instantly regardless of speed.
If char ID is 0, the unit used is the first player unit. If there are too many units currently moving, movement will be held until a moving unit reaches its destination. If char ID is -1, the unit used is the active unit. If char ID is -2, the unit used is the one at the coordinates in sB. If char ID is -3, the unit used is the one whose char ID matches the one in s2. None of these apply to MOVEONTO
’s target char ID.
Directions for MOVE_1STEP
are:
-
0 for left.
-
1 for right.
-
2 for down.
-
3 for up.
These values are defined as MoveLeft
, MoveRight
, MoveDown
, and MoveUp
respectively.
The event engine will not wait for movement to complete before continuing. As such, you require an ENUN
after your MOVE
s to avoid things breaking.
Camera Movement
You can manually move the camera using events.
CAMERA [X, Y]
will move the camera to the given coordinates in such a way that they are on screen and the camera sits more than 2 tiles away from the edge of the map, if possible.
CAMERA charID
will move the camera to the given character in such a way that they are on screen and the camera sits more than 2 tiles away from the edge of the map, if possible.
CAMERA2 [X, Y]
will move the camera to the given coordinates such that they become the center of the screen.
CAMERA2 charID
will move the camera to the given character such that they become the center of the screen.
Due to a bug, CAMERA2
can move the camera past the edge of the map, so be careful using it. For coordinate variants, if both coordinates are negative the target position will be read from sB. For convenience, you can use CAMERA_SB
and CAMERA2_SB
. Camera movement is instantaneous if you are currently skipping. If not, the event engine will wait for camera movement to complete before continuing.
If char ID is 0, the unit used is the first player unit. If char ID is -1, the unit used is the active unit. If char ID is -2, the unit used is the one at the coordinates in sB. If char ID is -3, the unit used is the one whose char ID matches the one in s2.
Change AI
You can change a unit’s AI after loading them in.
CHAI charID
will change all units of the specified ID’s AI1 and AI2 values based on the value in s1.
CHAI [X, Y]
will change the AI1 and AI2 values of the unit at the given coordinates based on the value in s1.
The data in s1 is formatted as 0x0000YYXX
, where YY
is AI2 and XX
is AI1. If AI1 is changed to 0x13, no change will occur. If A12 is changed to 0x15, no change will occur. There is no way to change AI3 and AI4 with CHAI
in vanilla, but Tequila has written a fix that allows for this.
Triggering Tile Changes
You can trigger tile changes for the current map manually via events, in addition to the few automatic ways they are triggered.
TILECHANGE tilechangeID
will activate the tile change with the given ID if it has not already been activated.
TILEREVERT tilechangeID
will revert the tile change with the given ID if it has been activated.
If tilechange ID is -1, a position is read from sB and the first tile change in the current map’s list that contains those coordinates will be activated. If tilechange ID is -2, the position of the active unit is used and the first tile change in the current map’s list that contains those coordinates will be activated. If tilechange ID is -3, multiple tile changes will be activated or reverted. The tilechange IDs to use are stored in the event queue. If you are currently skipping, tilechanges happen instantaneously. Otherwise, the event engine will wait for them to fade in/out.
Change Allegiance
You can change any unit’s allegiance using events.
CUSA charID
will change the given unit’s allegiance to blue.
CUSN charID
will change the given unit’s allegiance to green.
CUSE charID
will change the given unit’s allegiance to red.
Display Cursor
You can display the cursor on the screen during events to highlight a given tile or character.
CURSOR charID
will display the cursor on the given character.
CURSOR [X, Y]
will display the cursor at the given coordinates.
CURSOR_FLASHING charID
will display a flashing cursor on the given character.
CURSOR_FLASHING [X, Y]
will display a flashing cursor at the given coordinates.
REMOVE_CURSORS
will remove all cursors on the map.
If the given coordinates are negative, the position in sB will be used instead. CURSOR_SB
and CURSOR_FLASHING_SB
exist as shortcuts for this. Note that the event engine does not pause for cursors, so when using them your event will likely look something like:
CURSOR Seth
STAL 20
REMOVE_CURSORS
Prep Screen
Starting the prep screen is very simple:
PREP
will call the preparations screen.
Calling the prep screen automatically clears flag 0x84, which suppresses blinking map sprite icons.
EA stdlib gives us the macro GotoPrepScreen
, but be wary! This macro is set up to function like an FE7 event code, and as such contains an ENDA
. You can have events occur after the prep screen within the same event, but if you use this macro, it will automatically end your event after the prep screen.
Changing Chapters
When your chapter is over, you can use these to move on to the next one. Chapters do not have to go in linear order, as you can define the ID of the chapter to move to every time.
MNCH chapterID
will move to the world map and run the world map unlock events tied to the given chapter ID.
MNC2 chapterID
will move to the specified chapter without going through the world map.
MNC3 chapterID
will move to the specified chapter without going through the world map or the save screen.
GOTO_EPILOGUE
will move to the Epilogue and credits.
GOTO_TITLE
will return to the title screen.
If chapter ID is negative, the chapter ID will be read from s2.
Be careful when using MNC2
and MNC3
, as if the chapter you are moving to is not set as the next “story” location, normally done through world map unlock events, the game will consider the chapter a skirmish when you arrive.
Changing Maps
For cutscene maps or fancy chapter schenanigans, you can change the current map mid-chapter. Note that switching maps will update the chapter number, and events for the chapter the map you loaded will be run instead of the one you came from as a result.
LOMA chapterID
will load the map associated with the given chapter and sets the camera given the position in sB. If chapter ID is negative, chapter ID will be read from s2.
So far, we’ve covered most of the event opcodes most people will ever use. But, there’s still two pretty major pieces of events we haven’t covered yet…