[DOC] Tutorials on Tutorial events

Download My Tutorial event code Here!

12
3

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:

2

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:
image

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:
image

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:
2

6 Likes

That was an interesting read! Thanks for figuring this out and explaining it. Perhaps I’ll make a tutorial myself, sometime.

That’s great!
Besides, it should to note that there lies more complicated thing if you try to set the condition when you dont select eirika or moving to target position。 You can find more important thing by downloading my tutorial code on top of thread.

I didn’t understand the meaning of args param in “410B”, so your material was very helpful.

1 Like

Btw, ASMC 0x1D7E9 seems to stop you from having to select the tile twice for PreMove tutorial events.

Your info has been very helpful, so thank you.

(eg. using this means that selecting a tile will move you there and bring up the usual menu, as otherwise your A press had instead been used up to trigger the event I guess)