ExplorerScript language specification¶
This document contains the language specification for ExplorerScript. ExplorerScript is a scripting language that can be converted into the SSB script files of Pokémon Mystery Dungeon Explorers of Sky. The language is more flexible though, it can in it’s core also be used for other games or purposes.
The lexer and parser specifications can be found in the
explorerscript.antlr
package as an Antlr 4 grammar.
EoS Compiler
These blocks are used in the document to document things specific to the decompiler / compiler that are contained in this repository, they are specific to Explorers of Sky.
SkyTemple
These blocks contain notes on how the components of the SkyTemple project implement certain things.
We recommend any tool that works with EoS to try to be comaptible with the SkyTemple implementation.
Routines¶
Every script file contains a collection of routines (and/or macros).
A routine contains a set of statements.
Coroutine¶
A coroutine is a named routine and is meant to be used as a common routine used by other scripts.
The behaviour for having coroutines and other routines in the same file is currently undefined.
coro NAME {
/** Statements **/
}
SkyTemple
A script file that contains coroutines can not contain any
other routines. It must contain all of the coroutines that
are specified in the game’s list of coroutines. The only
compiled script file that usually contains coroutines is
SCRIPT/COMMON/unionall.ssb
. Operations exist to call
coroutines.
Generic Routine¶
A generic routine, that has an ID. If a script file contains routines their index must start at 0 and increment by one, there must not be a gap.
def 1234 {
/** Statements **/
}
Targeted Routine¶
A routine for an actor (for_actor
), an object (for_object
) or a performer (for_performer
) and an ID.
As identifiers for the actors, objects and performers any constant or integer can be used.
def 1234 for_actor(ACTOR_PLAYER) {
/** Statements **/
}
Aliases¶
Instead of containing statements, a routine can contain the
keyword alias previous;
. It will then use the same operations
as the routine before it (by index number). In case of coroutines
the assembler decides what the routine before it is, based on an
index of names.
def 0 {
/** Statements **/
}
def 1 {
alias previous;
/** Will use statements from def 0 **/
}
Data Types¶
ExplorerScript defines different data types. These are described below.
Integer¶
A whole number. The size of the integer is not restricted by the language. Numbers can be represented in different bases.
12 // base 10 (decimal)
-12 // negative base 10
0x12 // base 16 (hexadecimal)
0o7 // base 8 (octal)
0b110 // base 2 (binary)
SkyTemple
All integers are 14-bit signed integers. Some may be represented as 16-bit unsigned values instead.
Constants¶
A constant is a stand-in for an integer. Their names are usually in uppercase.
SkyTemple
Variables that are used by the debugger are also actually
constants that represent a number! For example $SCENARIO_MAIN
is actually just a constant containing the number of a variable.
This is because the conditional and assigment statements don’t
actually work with variables, they require the ID of variables
instead.
Please see the note and example on “Conditional check” for “if-Blocks”.
For a list of common constant prefixes, see the document on the CLI interface.
(Constant) Strings¶
A simple string literal that has no translations available.
"Hello World"
Language Strings¶
A collection of strings for different languages. It must have at least one string defined.
{
languageA='String for lang A',
languageB="String for lang B"
}
SkyTemple
What languages are required depends on the ROM. Either only one of the languages supported must be specified, or all of them. If only one is specified, it is also used for the other languages.
Position Marks¶
Position marks mark a place on the map. They have a name (string literal) and two position attributes for X and Y position. In addition these attributes can have the suffix ‘.5’ to place the position marks between two coordinates.
Position<'Name', 20, 20.5>
SkyTemple
In SSB files Position marks are actually 4 parameters:
X Offset: 2 or 4 if the x position ends on ‘.5’, otherwise 0.
Y Offset: 2 or 4 if the x position ends on ‘.5’, otherwise 0.
X Position: The x position, with the decimal place stripped.
Y Position: The y position, with the decimal place stripped.
The coordinates are in tiles.
Statements and Blocks¶
Labels¶
A label marks a statement that can be jumped to. Using the jump
control statement you can make the execution jump to these statements.
A statement can be marked with multiple labels.
§hello_label;
operation();
§another_label;
§and_another_one;
another_operation();
Control statements¶
Control statements have special meaning to the control flow of a script.
Warning
The specfication of end
, hold
and return
are based on the EoS SSB operations of the same name. The
descriptions are speculations based on observed behaviour.
end
¶
Ends the script execution. Get’s compiled as an instruction.
EoS Compiler
Compiles to an End
operation.
hold
¶
Ends the current script execution and holds it, waiting for something external to cause a new instruction. Get’s compiled as an instruction.
EoS Compiler
Compiles to a Hold
operation.
return
¶
If used in a macro ends the macro and resumes script execution after the macro call.
If not used in a macro either returns from a coroutine or act’s
the same as end
.
EoS Compiler
If used in a macro, a Jump
operation is generated
to the end of a macro call.
Otherwise compiles to a Return
operation.
break
¶
Can only be used in case-Blocks of switches.
Ends the case-Block and jumps to the end of the switch-Block.
continue
¶
Can only be used in forever-Blocks, for-Blocks and while-Blocks.
Ends the current iteration of the loop and jumps back to the beginning. In the case of while- and for-Loops the condition is checked again, for for-loops the increment operation is run before.
break_loop
¶
Can only be used in forever-Blocks, for-Blocks and while-Blocks.
Exits the current loop and jumps to the end of the loop block.
jump
¶
Jumps to a label.
// This will execute operation() in an endless loop.
§hello_label;
operation();
jump @hello_label;
with-Blocks¶
Runs a statement in the context of an actor, an object or
a performer. Only simple statements are allowed, no blocks
or labels. The blocks can only contain exactly one statement.
The keywords actor
, object
or performer
can be used
to specify the type. As identifiers for their IDs integers and
constants can be used.
with (actor ACTOR_HELLO_WORLD) {
operation();
}
if-Blocks¶
A conditional block. One or more conditions are checked and if they are true, a block of code is executed.
An if-Block an also be extended with elseif
statements
to specify alternative conditions and blocks and/or a single else
block, that will be executed when neither the if
conditions nor any
of the elseif
conditions apply.
Conditions specifications for if
and elseif
can contain
a list of conditions as described below. These can be combined
with ||
. If any of the conditions apply, the block is executed.
There is no syntax to specify that multiple conditions must apply (“and”). Use nested if-Blocks for this.
If conditions can be negated as a whole, by using the keyword not
.
if (CONDTION || CONDITION) {
/** Statements **/
} elseif not (CONDTION) {
/** Statements **/
} else {
/** Statements **/
}
if-Conditions¶
Conditional check¶
A simple conditional check, that checks if the first field of a variable (identified by an integer or constant) matches a value as specfied by the operator.
The value to compare against can be the value of a variable. In this
case value(X)
must be used, where X
is the id of the variable
to compare against.
if (VAR_A < 3) {}
if (4 < 3) {} // !!This checks if the variable with ID 4 is < 3!!
if ($A == 3 || $A > value($B)) {}
if ($VAR1 >= value(3)) {} // !! This checks if $VAR1 >= the variable with ID 3 !!
SkyTemple
As noted earlier, SkyTemple uses the $
prefix
for variable constants. We recommend only using this prefix.
The rest of the documentation will use this prefix for everything
related to variables.
EoS Compiler
If value(X)
is used, this is compiled as a BranchVariable
operation.
Otherwise, if the operator is not ==
, this is compiled as a BranchValue
operation.
Otherwise it is compiled as a Branch
operation.
Bit check¶
Checks if a bit in a bitfield variable is set. If the compiler
supports it, not
can be used to check if the field is not set instead.
if (VAR_A[3]) {}
if (not VAR_A[3]) {}
if not (not VAR_A[3]) {} // Same as if (VAR_A[3]) {}
EoS Compiler
If the variable is the game variable PERFORMANCE_PROGRESS_LIST
(see CLI docs),
this gets compiled as a BranchPerformance
Else it gets compiled as BranchBit
.
The not
keyword is only allowed for PERFORMANCE_PROGRESS_LIST
!
Scenario check¶
Checks the two fields of a scenario variable against a conditional
operator. Both values compared against must match the conditional operator.
As conditional operators only ==,<,>,<=,>=
can be used.
if (scn($SCENARIO_MAIN) > [30, 2]) {
/** This only gets executed when
field 0 of the variable is > 30 and
field 1 of the variable is > 2 **/
}
EoS Compiler
Depending on the operator, this gets compiled to
BranchScenarioNow
, BranchScenarioNowAfter
, BranchScenarioNowBefore
,
BranchScenarioAfter
, or BranchScenarioBefore
.
Special conditions¶
Some special keywords can be checked against. All of these can
be negated using the keyword not
:
debug: Branches if the debug mode is enabled.
edit: Unknown, not implemented?
variation: Branches if the ROM is a demo ROM.
EoS Compiler
These get compiled to BranchDebug
, BranchEdit
and
BranchVariation
respectively.
SkyTemple
In the SkyTemple Script Engine Debugger, the debug mode can be enabled with the checkbox “Enable Debugging Mode”.
Operations as conditions¶
Any operations supported by the compiler may also be used as if-Conditions.
EoS Compiler
The compiler supports no operations as if-Conditions.
switch-Blocks¶
(This works like switches in most programming languages).
Switch blocks have a switch-Header, which is a condition that a set of case-Blocks check against.
Each case-Block has a case-Header with it’s condition to check against
and a set of instructions. If the end of a case-Block’s body is reached,
it will continue executing the next case body will be executed. The
execution of a case-Block can be stopped with the break
statement.
A switch can have a default
case, that gets executed when no
other case matches.
switch (SWITCH_HEADER) {
CASE_HEADER:
statement1;
statement2;
// Will continue with next block, beause no break.
CASE_HEADER: // Case headers can be combined like this.
CASE_HEADER:
statement1;
statement2;
break; // Will not continue with next block.
CASE_HEADER:
default:
statement1;
statement2;
}
switch-Headers¶
Variable¶
The cases of this switch check against the value of a variable identified by the integer or constant provided.
switch($VAR) { /* ... */ }
EoS Compiler
Get’s compiled as a Switch
operation.
Scenario¶
The cases check the first field of a scenario variable. This is effectively the same as the normal variable switch header.
switch(scn($VAR)) { /* ... */ }
EoS Compiler
Get’s compiled as a SwitchScenario
operation.
Random¶
The cases check against a randomly generated number. The value in parenthesis is the upper bound of that number, the lower bound is 0.
switch(random(1234)) { /* ... */ }
EoS Compiler
Get’s compiled as a SwitchRandom
operation.
Dungeon Mode¶
The cases check against the dungeon mode of a dungeon identified by the integer or constant provided.
switch(dungeon_mode(DUNGEON_ABC)) { /* ... */ }
EoS Compiler
Get’s compiled as a SwitchDungeonMode
operation.
Sector¶
The cases check against whether or not a sector (or layer) of the active scene is activated.
switch(sector()) { /* ... */ }
EoS Compiler
Get’s compiled as a SwitchSector
operation.
Operations as switch headers¶
Operations that are supported by the compiler can also be used as headers for switches.
EoS Compiler
The compiler supports all operations as switch headers, although please note, that it doesn’t make sense to run all operations as such.
case-Headers¶
Value¶
Check that the value provided by the switch header matches a value.
case 9:
EoS Compiler
This gets compiled as a Case
operation.
Check against operator¶
Check that the value provided by the switch header matches
a conditional operator and a value. value(X)
can be used, to check
against the value of variable, specified by the integer or constant X,
instead.
case > 9:
case == 12:
case FALSE 3:
case < value($VAR_TEST):
EoS Compiler
If value(X)
is used, this gets compiled as a CaseVariable
operation. Else it gets compiled as a CaseValue
operation.
Message Switches¶
Message switches check against a value to see what message to display.
The begin with either message_SwitchTalk
or message_SwitchMonologue
.
As case-Header only “Value” headers are allowed. Instead of statements the bodies of case-Blocks contain a single string.
message_SwitchTalk ($PARTNER_TALK_KIND) {
case 1:
" Oh, wow! What a pretty sight!"
case 2:
{
english=" Oh, wow! What a pretty sight!"
}
default:
{
english=" Wow! What a beautiful sight!"
}
}
EoS Compiler
The switch statements get compiled as either message_SwitchTalk
or message_SwitchMonologue
.
The cases get compiled as CaseText
and the default case gets compiled as DefaultText
.
forever-Loops¶
The statements in this block will loop forever,
unless broken out of using break_loop
.
forever {
operation1();
operation2();
// Will repeat at beginning again.
}
for-Loops¶
(Works like for-Loops in other programming languages).
Executes code until a condition is no longer met. Has an initializing statement and an incrementing statement that is run every iteration of the loop.
The for loop has the following syntax:
for (initial; condition; increment;) {
/* Statements */
}
“initial” is executed (one time) before the execution of the code block. Any simple statement is allowed.
“condition” defines the condition for executing the code block. Any if-Header is allowed (no “||”).
“increment” is executed (every time) after the code block has
been executed, including when continue
is used. Any simple statement is allowed.
while-Loops¶
(Works like while-Loops in other programming languages).
Loops until a condition is no longer true. Any if-Header condition is allowed (no “||”).
while (condition;) {
/* Statements */
}
EoS Compiler
The switch statements get compiled as either message_SwitchTalk
or message_SwitchMonologue
.
The cases get compiled as CaseText
and the default case gets compiled as DefaultText
.
Assignments¶
Statements to assign values to variables (identified by integers or constants).
Warning
Assignments can NOT be used to assign values to macro variables. Game variables and macro variables are fundamentally different, please also see the section on macro variables.
If you use a macro variable as the variable in an assignment,
the variable with the ID of the macro variables current content
will be set. In the following example $SCENARIO_MAIN
will be set
to 3.
macro example($var) { $var = 3; // $SCENARIO_MAIN is now 3. } def 0 { ~example($SCENARIO_MAIN); }
Simple¶
Assign a integer value or the value of another variable (with value(X)
)
to a variable. How the assignment is done depends on the assignment
operator used.
$VAR = 3;
$VAR = value(3);
$VAR += 3;
EoS Compiler
If value(X)
is used, this gets compiled as flag_CalcVariable
.
Else if, an assigment operator other than =
is used, this gets compiled as flag_CalcValue
.
Else, this gets compiled as flag_Set
.
Bit-Set¶
Set a bit field in a bitfield variable.
Only 0 and 1 are allowed as values.
$VAR[3] = 1;
EoS Compiler
If the variable is the game variable PERFORMANCE_PROGRESS_LIST
(see CLI docs),
this gets compiled as a flag_SetPerformance
Else it gets compiled as flag_CalcBit
.
Scenario¶
Set’s the two fields of a scenario variable
$VAR = scn[1, 2];
EoS Compiler
This gets compiled as flag_SetScenario
.
Clear¶
Clears a variable (TODO: What exactly happens is not clear).
clear $VAR;
EoS Compiler
This gets compiled as flag_Clear
.
Reset¶
Resets a scenario variable or the dungeon result (TODO: What exactly happens is not clear).
reset $VAR;
reset dungeon_result;
EoS Compiler
This gets compiled as flag_Reset
or flag_ResetDungeonResult
.
Initial¶
Set’s a variable to it’s initial value (TODO: What exactly happens is not clear).
init $VAR;
EoS Compiler
This gets compiled as flag_Initial
.
Adventure Log¶
Set’s a value of the adventure log. Only =
is allowed.
adventure_log = 3;
EoS Compiler
This gets compiled as flag_SetAdventureLog
.
Dungeon Mode¶
Set’s the dungeon mode of a dungeon with it’s ID specfied by an integer or constant.
dungeon_mode(3) = 3;
dungeon_mode(4) = DMODE_OPEN;
EoS Compiler
This gets compiled as flag_SetDungeonMode
.
Operations¶
An operation can be any valid identifier and a list of arguments. The arguments can have any data type.
The compiler usually compiles these directly as the specified operations, it may raise errors for unknown operations.
MyOperation(3, "String", Position<'Name', 20, 20.5>);
SkyTemple
A list of operations supported by Explorers of Sky can be found in this Google sheet. Feel free to extend the list. Some of the opcodes are reserved for ExplorerScript specific syntax and should not be used (eg. the “Branch”, “Switch”, “Case” and “flag” operations).
Macro call¶
A macro call calls a macro (duh!). See the documentation on macros
(below) for more information. Macro calls start with ~
.
~MyMacro(3, "String", Position<'Name', 20, 20.5>);
Conditional operators¶
Operator |
Description |
---|---|
FALSE |
The condition always fails |
TRUE |
The condition always succeeds. |
== |
Both must be equal. |
> |
The value of the left side must be greater than the right side’s value. |
< |
The value of the left side must be smaller than the right side’s value. |
>= |
The value of the left side must be greater or equal to the right side’s value. |
<= |
The value of the left side must be smaller or equal to the right side’s value. |
!= |
Both must not be equal. |
& |
Bitwise AND. |
^ |
Bitwise XOR. |
&<< |
True if the bit of the left side (as specified by the right side) is set. |
Assigment operators¶
Operator |
Description |
---|---|
= |
Set’s the left side to the right side’s value. |
-= |
Changes the left side’s value to have the right side’s value subtracted. |
+= |
Changes the left side’s value to have the right side’s value added. |
*= |
Changes the left side’s value to be multiplied by the right side’s value. |
/= |
Changes the left side’s value to be divided by the right side’s value. Warning: No floats exist! |
Imports / Includes¶
An ExplorerScript source file can imports other ExplorerScript files. These files are merged together. Imports are always at the top of source files, before any routine or macro.
Imports can be specified using:
Relative paths to the source file (starting with ‘./’ or ‘../’)
Absolute paths (starting with ‘/’).
Paths relative to the include paths of the compiler (all other paths).
The path separator is always /
, even under Windows.
import "/xyz/abc.exps"; // Absolute
import "./abc.exps"; // Relative
import "../abc.exps"; // Relative
import "abc.exps"; // In include path
import "xyz/abc.exps"; // In include path
SkyTemple
SkyTemple Script Engine Debugger does not allow any imported files to contain routines, they must only contain macros.
The include path for SkyTemple projects is the “Macros” directory inside the project’s directory.
Macros¶
Macros are small functions that can be specified by the user.
They have a name and can take a list of arguments.
When called these arguments get filled with the values
of the parameters specified in the call and turn into
macro variables. Macro variables have the prefix $
.
When compiled, the entire content of a macro is copied to where the call was in the source code, the actual compiled code does not contain a function call. All references to the macro variables are replaced with the values of the call.
Macros can call other macros but can not cause any form of recursion.
Using imports you can share macros across multiple source files.
macro another_example($anotherVariable) {
another_print($anotherVariable);
}
macro example($variable1, $variable2) {
print($variable1, $variable2);
~another_example($variable1);
}
def 0 {
~example($SCENARIO_MAIN, 3);
~example(ANOTHER_CONSTANT, "A string");
~another_example("Another string");
}
This example is equivalent to:
def 0 {
print($SCENARIO_MAIN, 3);
another_print($SCENARIO_MAIN);
print(ANOTHER_CONSTANT, "A string");
another_print(ANOTHER_CONSTANT);
another_print("Another string");
}
SkyTemple
SkyTemple uses the $
prefix for both macro variables
and game variables. Please note that these two are, as explained,
not interchangeable. Game variables are actually constants for
integers that refer to the variables and macro variables
can have game variable constants as values!
See the warning at the “Assignment” section for more info on this behaviour.
To avoid confusing game variables and macro variables, you should use lowercase or camel case names for macro variables.