Download My Tutorial event code Here!
Issues on Tutorial events has been explored by @Kao first: Here! .I strongly recommend you to study on that post first.
1. Look into Prologue Tutorial Code
Dissassemble the FE8_clean and look into prologue event (You can also get it in EA folder: Event Assembler\ Scripts\ FE8_Chapters\ Prologue.txt
), at about line137
Tutorial:
WORD 0x89EF2B0 0x89EF328 0x89EF388 0x89EF398
WORD 0x89EF46C 0x89EF4EC 0x89EF50C 0x89EF53C
WORD 0x89EF56C 0x89EF5A8 0x89EF6E0 0x89EF758
WORD 0x89EF7B8 0x89EF7C8 0x89EF7FC
END_MAIN
Now lets goto the first event: 0x9EF2B0
//Event1_1_WaitForChoosingEirika_Configure
ORG $9EF2B0
EVBIT_T 0x7 // Ensure screen not fade in&out
SVAL 0xD 0x0
// CHECK_ACTIVE
SVAL 0x1 0x1
SAVETOQUEUE
// CURSOR_FLASHING_SB
SVAL 0x1 0x50004
SAVETOQUEUE
// Text ID
SVAL 0x1 0x91B
SAVETOQUEUE
// Text Geometry
SVAL 0x1 0x80058
SAVETOQUEUE
// Text ID
SVAL 0x1 0x91C
SAVETOQUEUE
// Text Geometry
SVAL 0x1 0x80058
SAVETOQUEUE
// Event 2 (When Active=Eirika)
SVAL 0x1 0x89EF328
SAVETOQUEUE
// Event when Active != Eirika
SVAL 0x1 0x89EF2B0
SAVETOQUEUE
// Event1_2_WaitForChoosingEirika_Apply
CALL 0x9EE6F4
ENDA
Just like the “push” instruction in ASMC, Command “SAVETOQUEUE
” temporarily saves the data in the public area for subsequent retrieval. However, unlike Stack’s first-in-last-out method, its data arrangement method is Sequence (First in, First out).
As for the saving values, firstly let’s look at the last piece of code, “CALL 0x9EE6F4
”
// Event1_2: Prepare for Eirika movement
ORG $9EE6F4
TUTORIALTEXTBOXSTART
CHECK_ACTIVE
// 0x1 = Eirika CharID
SLOTS_SETFROMQUEUE 0x2
BNE 0x0 0xC 0x2
SHOW_MOVERANGE 0xFFFF
// Location: (5,4)
SLOTS_SETFROMQUEUE 0xB
CURSOR_FLASHING_SB
STAL 0x12
// TEXT: ID=91B; Geometry=(8,58)
SLOTS_SETFROMQUEUE 0x2
SLOTS_SETFROMQUEUE 0xB
TEXT 0xFFFF
TEXTEND
CURE
IGNORE_KEYS 100001010b
SLOTS_SETFROMQUEUE 0xC
SLOTS_SETFROMQUEUE 0xC
// Event2
SLOTS_SETFROMQUEUE 0x2
WORD 0x30B41 0xFFFFFFFF // Event_0B_EnqueneCall
GOTO 0x1
/* .......................................
............Some EventCode..............
....................................... */
LABEL 0x1
CURE
SLOTS_SETFROMQUEUE 0xC
SLOTS_SETFROMQUEUE 0x2
WORD 0x30B41 0xFFFFFFFF
SVAL 0xC 0x0
LABEL 0x2
ENDA
The instruction “SLOTS_SETFROMQUEUE
” (which means load value) is the inverse operation of the instruction “SAVETOQUEUE
” .We put the head part of the two event codes together to observe :
We can see that the value Enuqeued first (which is equal to 0x1, Eirika CharID
) is dequeued for comparing with the Character Index of Active Unit
. Using the same method, event1-1 and event1-2 can be combined into the following code:
ALIGN 4
Event1:
EVBIT_T 0x7 // Nofade: Cancel the screen fading in and back
SVAL 0xD 0x0
TUTORIALTEXTBOXSTART
// if gActiveUnit!=Eirika
CHECK_ACTIVE
SVAL 0x2 0x1
BNE 0x0 0xC 0x2
SHOW_MOVERANGE 0xFFFF
// Set Cursor flashing at (x,y) = (5,4)
SVAL 0xB 0x50004
CURSOR_FLASHING_SB
STAL 0x12
// Text
SVAL 0xB 0x80058 // Geometry = (8,58)
TEXT 0x91B
TEXTEND
CURE // End Cursor flashing
IGNORE_KEYS 100001010b
//CALL Event2
SHORT 0x0B41 0x3 ; POIN Event2 |0x8000000 // 0x89EF328
LABEL 0x0
SVAL 0xC 0x0
ENDA
Based on these codes, we can find that Event1 functioned as judging wether you choose Eirika, show text 0x91B and deal with another group of event codes, Event2 (0x9EF328) .
Now we have encountered another strange piece of code:
SHORT 0x0B41 0x3; POIN Event2 |0x8000000 // 0x89EF328
For now I can tell you that this event ID=0x0B, corresponding to function Event0B_EnqueueCall (0x800DA2D)
(Later I will discuss on this in more depth). For the time being, it can be understood as enqueuing Event2, which can be processed after ending current event.
2. Vanilla Tutorial Remake
Following the same way, we can sort out Event2, and finally we can sort out the entire tutorial event sets:
- Event1: Judge for whether is Eirika chosen.
- Event2: Judge for whether Eirika move to specfic location.
- Event3: Do Something maybe we don’t care.
- Event4: Set Seth as moved unit and something.
- Event5: Call Dialogue.
- Event6: Do Something maybe we don’t care +1.
- Event7: Set current event state to be Tutorial.
Same way, we can summerize as below:
PUSH
ORG 0x9E85FC+0x4 // 0x9E85FC=Vanilla Toturial event list
POIN Event1
POIN Event2
POIN Event3
POIN Event4
ORG 0x9EF2B0 // Tutorial Event1 of Vanilla
EVBIT_T 0x7
CALL Event1
ENDA
POP
ORG FreeSpace
ALIGN 4
Event1:
EVBIT_T 0x7 // Nofade: Cancel the screen fading in and back
SVAL 0xD 0x0
TUTORIALTEXTBOXSTART
// if gActiveUnit!=Eirika
CHECK_ACTIVE
SVAL 0x2 0x1
BNE 0x0 0xC 0x2
SHOW_MOVERANGE 0xFFFF
// Set Cursor flashing at (x,y) = (5,4)
SVAL 0xB 0x50004
CURSOR_FLASHING_SB
STAL 0x12
// Text
SVAL 0xB 0x80058 // Geometry = (8,58)
TEXT 0x91B
TEXTEND
CURE // End Cursor flashing
IGNORE_KEYS 100001010b
//CALL Event2
SHORT 0x0B41 0x3 ; POIN Event2 |0x8000000 // 0x89EF328
LABEL 0x0
SVAL 0xC 0x0
ENDA
ALIGN 4
Event2: // Wait for Eirika movement
EVBIT_T 0x7
IGNORE_KEYS 0b
SVAL 0xD 0x0
CHECK_CURSOR
SVAL 0xB 0x50004
BNE 0x0 0xC 0xB // if your cursor is not at (5,4)
ASMC 0x1D7E9 // Maybe related to range display?
//CALL Event3
WORD 0x40B41 Event3|0x8000000
LABEL 0x0
SVAL 0xC 0x1
ENDA
ALIGN 4
Event3: // Do nothing
EVBIT_T 0x7
WORD 0x10B41 Event4|0x8000000
ENDA
ALIGN 4
Event4: // Set Seth's action=wait
EVBIT_T 0x7
IGNORE_KEYS 0b
SET_HASMOVED 0x2
ENUT 0xB7 // Set Flag
CALL Event5
ENDA
ALIGN 4
Event5: // Call Dialogue
TEXTSTART
TEXT 0x90F
TEXTEND
TEXTCLEAR
CALL Event6
SVAL 0x3 0x9 // Eirika get Rapier
ITEM 0x1
CALL Event7
ENDA
ALIGN 4
Event6:
ENDA
ALIGN 4
Event7:
ENDA
You can now copy this code directly and import it into the rom. To prove that the above code actually works, we replace the code “SVAL 0x3 0x9 // Eirika get Rapier
” at the end of Event5 before “Item 0x1
” with “SVAL 0x3 0x1 // Eirika get IornSword
” . Let see how it works:
Thats it!
3. Note on EventEngine and Event-0x0B
——BackGround Knowledge——
Refer to EventCode structure doc and Master EA Doc of StanH, Event code is set as:
bits 0-3 | subcode : "subcode" identifier
bits 4-7 | size : byte size / 2
bits 8-15 | id : code identifier
[code-dependent layout]
Proc MainLoop function of EventEngine is set as EventEngine_Loop (0x800CE4C)
, in which, it read a function list, 0x8591B28
(acturally is two lists, but we don’t care another one in this thread), each event code corresponds to a specific function by there event ID.
——————————————————
Back to end of Event1, it enqueue Event2 by using Event coude “SHORT 0x0B41 0x3; POIN 0x89EF328”, or:
BYTE 0x41 0x0B 0x03 0x00
POIN Event2 | 0x8000000
or
WORD 0x-00-03-0B-41
Now we will find that event ID=0xB for sure, which means it corresponds to function:
[0x8591B28+4*0xB]=0x800DA2C
We set this function as Event0B_EnqueueCall= 0x800DA2C+1.
Digging into this function, we will find the value of [EventCode+0x0], “1” of 0x41 will be used as flag to determine whether the current event is a Toturial Event. And inside the function, it called another function, 0x8083DD8
to set more information about tutorial events in gChapterData: save byte Event+0x3 (here to be 0x3)
to [gChapter+0x4A]
, and [gChapter+0x4B]=The order of the tutorial events to be enqueued
.
4. Trigger Timing
Inside Playerphase (gProc_PlayerPhase, 0x859AAD8) and E-MAPMAIN, it is function 0x8083E64
(named as RunTutorialEvent
) to check whether the tutorial event shoyld be triggered.
In this function, it will call next tutorial event if input value r0==[gChapter+0x4A]
.
[gChapter+0x4A]
, remember? We set [Event+0x3]
to [gChapter+0x4A]
inside Event0B_EnqueueCall
!
So! the Byte value of Event+0x3 (here to be 0x3) actually corresponds to the trigger timing!
Furthermore, we can use IDA to investigate function calls, and summarize the time nodes as below:
/*
BYTE [Event Code+0x3]:
0x0 = phase switch
0x1 = post-action
0x2 = On select
0x3 = Pre move
0x4 = Post move
0x5 = Battle Forecast
0x6 = PlayPhase start
*/
#define EnqueneTutorialEvent_0_PhaseSwitch(pEvent) "SHORT 0x0B41; SHORT 0x0; POIN pEvent"
#define EnqueneTutorialEvent_1_PostAction(pEvent) "SHORT 0x0B41; SHORT 0x1; POIN pEvent"
#define EnqueneTutorialEvent_2_OnSelect(pEvent) "SHORT 0x0B41; SHORT 0x2; POIN pEvent"
#define EnqueneTutorialEvent_3_PreMove(pEvent) "SHORT 0x0B41; SHORT 0x3; POIN pEvent"
#define EnqueneTutorialEvent_4_PostMove(pEvent) "SHORT 0x0B41; SHORT 0x4; POIN pEvent"
#define EnqueneTutorialEvent_5_BattleForecast(pEvent) "SHORT 0x0B41; SHORT 0x5; POIN pEvent"
#define EnqueneTutorialEvent_6_PlayerPhaseStart(pEvent) "SHORT 0x0B41; SHORT 0x6; POIN pEvent"
Based on that, the End of Event1 can be translated into:
//CALL Event2
//SHORT 0x0B41 0x3 ; POIN Event2 |0x8000000 // 0x89EF328
EnqueneTutorialEvent_3_PreMove(Event2)
LABEL 0x0
SVAL 0xC 0x0
ENDA
Set Event2 (Judge for whether Eirika move to specfic location.) to be triggered at rhe preparation timing for Eirika movement!
5. Call Tutorial in Beginning Scene
Here lists how prologue call for tutorial in vanilla:
// in Beginning Scene
// .....
SVAL 0x2 0x89EF27C
CALL label24
//.....
label24:
CHECK_ISTUTORIAL
BEQ 0x0 0xC 0x0
TUTORIAL_CALL // Equal to CALL events From Slot2
LABEL 0x0
ENDA
ORG $9EF27C
//.....
EnqueneTutorialEvent_2_OnSelect(Event1)
//.....
No more explanations, now we can simply use such these Beginning scene Event code to set Tutorials:
6. Make a DIY Tutorial !
Make such a tutorial: Choose Eirika then move to some location.
First, we divide these events to two part:
- Event1: Judge whether Eirika is selected. Trigger by the second type (OnSelect).
- Event2: Judge whether Eirika move to Target Location. Triggered by 3rd type (Pre move)
The tutorial event codes:
// Event1: Judge whether Eirika is select
ALIGN 4
MyTutorial1:
EVBIT_T 0x7 // No fade in & fade out
TUTORIALTEXTBOXSTART // Set TextBox Style
CHECK_ACTIVE
SVAL 0x2 0x1
BEQ 0x1 0xC 0x2
// if Active != Eirika
CURSOR_FLASHING 0xFFFD // Set Cursor Flashing
STAL 8
CURE
EnqueneTutorialEvent_2_OnSelect(MyTutorial1) // Enquene itself
GOTO 0xF // ENDA: return 0
// if Active == Eirika
LABEL 0x1
SHOW_MOVERANGE 0xFFFF
SVAL 0xB 0x40004 // Set Cursor Flashing at (4,4)<- Move to
CURSOR_FLASHING_SB
STAL 0x12
IGNORE_KEYS 100001010b
EnqueneTutorialEvent_3_PreMove(MyTutorial2)
LABEL 0xF
ENDA
and:
// Event2: Judge whether Eirika move to Target Location
ALIGN 4
MyTutorial2:
EVBIT_T 0x7
CHECK_CURSOR
SVAL 0xB 0x40004
BEQ 0x1 0xC 0xB
// if Cursor is not at Target Location
SVAL 0xB 0x40004 // Set Cursor Flashing at (4,4)<- Move to
CURSOR_FLASHING_SB
STAL 0x12
EnqueneTutorialEvent_3_PreMove(MyTutorial2)
GOTO 0xF
// if Cursor is at Target Location
LABEL 0x1
ASMC 0x1D7E9 // Maybe related to range display?
LABEL 0xF
IGNORE_KEYS 0b
ENDA
Don’t forget to set TutorialEvents and BeginningScene:
ALIGN 4
BeginningScene:
LOMA 0x0
LOAD1 1 PlayerUnits
ENUN
LOAD1 1 EnemyUnits
ENUN
CHECK_ISTUTORIAL
BEQ 0x0 0xC 0x0
EnqueneTutorialEvent_2_OnSelect(MyTutorial1)
LABEL 0x0
ENDA
ALIGN 4
Tutorial:
POIN MyTutorial1 MyTutorial2
END_MAIN
This is our result: