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.