SCM language
It may contain non-standard SCM definitions as R* hasn't published enough documentation about it yet.
Contents
|
On the occasion of the GTAIII's Tenth Anniversary, after a long period of darkness where we fell about the real SCM syntax, R* finally treated us by attaching part of its own original source code into the GTAIII Anniversary game, available for iOS and Android devices. As far back as 2001, a snip of some debugging scripts has been already provided with main.sc and debug.sc files. However, many secrets are unrevealed yet, thus some things cannot be documented fully and so they can be only guessed. The SCM format abbreviation is one of countless proofs of this inconvenience, which may stand for Script Multifile. Other doubts come with source files, whose SC extension appears to be very close to Mission SCript. Although we have enough information to suppose the currently unknown mysteries of the used language, we still have no safe clue about which was its original denomination. Furthermore, it is a matter of fact that R* developers have been left untouched the miss2 executable name of the GTA 3 series compiler since the chapter 2. In this connection, we could imagine the new language is a variant or an evolution of the GTA2script. The ancient documentation by DMA (at present Rockstar North), mentions GTA2script as a successor to GBHscript - a language used in GTA 1 (GBH was a planned name for GTA). Therefore, the language used in GTA 3 series should've been called GTA3script. It was influenced by both BASIC and C programming languages.
Preliminary remarks
This article makes use of formatted codes to improve the reading comprehension. Note that:
- Square brackets mean everything inside may be omitted;
- Curly brackets denote the presence of useful codes but not necessarily needed;
- Vertical bars divide what can be chosen alternatively.
Fundamentals
Comments
Comments are notes left in the code to aid readability or to explain a part of code that may be confusing otherwise. Comments are ignored by the compiler. R*'s compiler supports C++ style comments – single-line comments and multiline comments.
A single line comment only affects a single line. They are prefixed with //
(two slashes). Anything between these slashes and the end of the line is a comment. Single line comments may appear at the end of a line of code as well as on their own line.
[...] // This comment is at the end of a line.
// This comment is on its own line.
Multiline comments appear between /*
and */
. They do not actually have to span multiple lines, and anything outside of the opening and closing tokens is not ignored (i.e. these comments may appear with code on either side on the same line).
[...]
/*
This comment spans
multiple lines.
*/
[...]
[...] /* This comment is within a line */ [...] /* As is this one */ [...]
R*'s compiler allows multiline comments to be nested.
Highlighters
Highlighters behaviour sounds trivial, that's to say they simply highlight one or more arguments per command within round brackets, individually or together. In , they appear to be used only for SETUP_ZONE_PED_INFO (in a various order) and GXT keys:
SETUP_ZONE_PED_INFO FISHFAC DAY (0) 0 0 0 (0 0 0 0) 0 PRINT_BIG (T4X4_1) 5000 2
- Limits
- Opening and closing round brakets are treated as blank spaces;
- An optional comma can be used as well to distinguish each argument, processed as a space.
Scopes
Scopes are delimited by curly brackets (or multiline brackets) which act like a local variable scope. Essentially, they enclose the code where local variables are used, including timers. They can be opened and closed many times in a script:
{ [...] }
- Limits
- Scopes cannot be nested;
- Opening and closing curly brakets are real commands.
Labels
A label is a sequence of characters which identify the reference of a location of the source code useful for gotoes. It can be accessed by any part of the source code. To define a label just append : (colon) to its name:
[...] {lblname}: [...]
At the compiling time, they are automatically converted into an offset.
Variables
A variable is a memory address that is given a name. Variables are used to reference values that are stored in the game's memory and that can change (hence the name "variable"). Instructions may read the variable's memory or write to it. They act in the same way as pointers in other programming languages.
There are several words related to variables that are important to know:
- The value of the variable is the data that lies at the variable's memory address.
- The type is what kind of value the variable stores.
- The scope is the region of code in which the variable may be used (and in which it is declared). There are two types:
- The global scope is shared between all game scripts. Global variables have the same value everywhere. They are declared with the
VAR
prefix. - The local scope is a region of code within a script. Locals are only visible to other code inside the same pair of curly braces. They are declared with the
LVAR
prefix.
- The global scope is shared between all game scripts. Global variables have the same value everywhere. They are declared with the
- A timer is a unique local variable whose value rises automatically. They start counting when the script starts executing, and continue counting indefinitely. There are two such timers,
TIMERA
andTIMERB
which are always defined, so do not need to be declared.
Data types
Among the available data types, some are equivalent to those of the most known programming languages. Their length is up to 4, 8 and 16 bytes. Each type is appended as a suffix in the variable declaration.
For an in-depth description of different data types, see the SCM instruction page.
LABEL
The LABEL type handles variable-length strings. It can refer to either a label name or a file name.
- Notes
- While inside a script file, R* compiler treats it unambiguously as a label;
- R* compiler allocates 32 bytes per label.
- Limit
- LABEL variables aren't available.
INT
The INT type handles 32-bit signed integers. It is also used to store values with less bytes, such as a bool, a char and a short int.
FLOAT
The FLOAT type handles 32-bit floating-points. As it normally does, decimal precision of a float is usually stuck to 6-7 digits beyond which it may get lost.
- Notes
- R* compiler also accepts f and F suffixes for the immediate value.
TEXT_LABEL
The TEXT_LABEL type handles 8-byte strings. Generally, a string is an array of 1-byte characters. It requires 7 characters plus the null-terminator (a blank byte meaning the end of the string). It is used to hold GXT keys (those of town zones, interiors, help textes or dialogue subtitles) script names or any short string. Literal only TEXT_LABEL* strings are probably marked by single quotation marks to distinguish them from variable and constant identifiers:
PRINT_BIG 'GXT_KEY'
TEXT_LABEL16
The TEXT_LABEL16 type handles 16-byte strings. Like the previous, this type holds 15 characters plus the null-terminator. It is used to store model and texture names of player clothes, animation names or any long string.
TEXT_LABEL32
The TEXT_LABEL32 type handles 32-byte strings or larger, depending on how many continuous parameters of the same type there are, each of which occupies 32 bytes. It can hold up to 127 characters plus the null-terminator, after which another TEXT_LABEL32 argument may begin. Strings of such type must be put within double quotation marks:
SAVE_STRING_TO_DEBUG_FILE "32B-128B TEXT"
TEXT
The TEXT type handles N-byte strings. It holds N characters plus the null-terminator. Strings of this type mustn't exceed 255 characters (including the null-byte).
CONST (pseudo)
The CONST type handles 32-bit signed integers. It is used only to assign and compare constants to INT variables regarding model identifiers, task statuses, ped or audio events and such. It is a pseudo type of INT.
- Limit
- The assignment and comparison of CONST values are supported since .
MULTI (pseudo)
The MULTI type handles a group of few data types acceptable per argument. It is used only for commands featuring optional arguments, those whose type is unpredictable before the compilation. It can be a pseudo type of INT, FLOAT and TEXT_LABEL.
Declaration
Defining a variable means assigning a token string to a memory cell at the compiling time. Variables must be declared in the following manner:
VAR_* {varname0}[,] [... {varnameN}] LVAR_* {varname0}[,] [... {varnameN}]
As mentioned in the sections above, local variables have to be put within curly brackets:
{ VAR_* {varname0}[,] [... {varnameN}] LVAR_* {varname0}[,] [... {varnameN}] [...] }
Inline variable declaration is allowed, you just have to separate them by spaces or tabulations. Adding a preceding comma before these characters is optional.
- Limits
- Whereas the variable buffer is limited, you can declare a certain amount of globals and locals. INT and FLOAT types take 1 variable, while TEXT_LABEL and TEXT_LABEL16 types occupy respectively 2 and 4 variables to store their data (have a look here for further details);
- isn't affected by what said above;
- Global and local variable names must not collide.
Arrays
A array is a collection of variables having the same type which can be accessed by an index, an INT lesser than or equal to the size specified, enclosed by square brackets:
{ VAR_* {varname0}{[arrsize0]}[,] [... {varnameN}{[arrsizeN]}] LVAR_* {varname0}{[arrsize0]}[,] [... {varnameN}{[arrsizeN]}] [...] }
- Limits
- The usage of arrays is allowed since ;
- Variable indices are quite buggy in and therefore unrecommended, but they are fully supported since ;
- The aforesaid indices are one-based, possibly zero-based since ;
- Multidimensional arrays are not supported.
Handles
A handle is an univocal identifier assigned to a game entity. It is given by the following statement:
short nHandle = (iEntityIndexInPool << 8) | ucEntityFlag;
- Note
- R* compiler won't let you assign different entity types to the same variable or using a variable which hasn't been passed to any command designated to the creation of an entity.
Operators
In general, an operator is a token string that represents a math calculation or an operation of any other kind, in order to make the code understanding clearer at a glance.
Arithmetic
Arithmetic operators compute some of the most common algebric calculations between either a variable and a value or two variables. As well as in some programming language happens, CONST, TEXT_LABEL and TEXT_LABEL16 types are free from these operators, except for the basic assignment (see also Operators composition):
Operator | Name | Syntax | Description | |
---|---|---|---|---|
= | Assignment | expr0 = expr1
|
Store expr1 to expr0 | |
+ | Addition | expr0 + expr1
|
Add expr1 to expr0 | |
- | Subtraction | expr0 - expr1
|
Subtract expr1 from expr0 | |
* | Multiplication | expr0 * expr1
|
Multiply expr0 by expr1 | |
/ | Division | expr0 / expr1
|
Divide expr0 by expr1 | |
+@ | Timed addition | expr0 +@ expr1
|
Multiply expr2 by delta time and add the result to expr1 | |
-@ | Timed subtraction | expr0 -@ expr1
|
Multiply expr2 by delta time and subtract the result from expr1 | |
++ | Increment | Pre[*] | ++ expr0
|
Increment expr0 by 1 and store the result to expr0 |
Post | expr0 ++
| |||
-- | Decrement | Pre[*] | -- expr0
|
Decrement expr0 by 1 and store the result to expr0 |
Post | expr0 --
|
- Note
- ^ Pre and post increments have no difference unlike what you would expect.
Yet, you can put the assignment and algebric operators together inline as follows:
Operators | Name | Syntax | Description |
---|---|---|---|
= + | Addition and assignment | expr0 = expr1[*] + expr2
|
Add expr2 to expr1 and store the result to expr0 |
= - | Subtraction and assignment | expr0 = expr1[*] - expr2
|
Subtract expr2 from expr1 and store the result to expr0 |
= * | Multiplication and assignment | expr0 = expr1[*] * expr2
|
Multiply expr1 by expr2 and store the result to expr0 |
= / | Division and assignment | expr0 = expr1[*] / expr2
|
Divide expr1 by expr2 and store the result to expr0 |
= +@ | Timed addition and assignment | expr0 = expr1[*] +@ expr2
|
Multiply expr2 by delta time, add the result to expr1 and store everything to expr0 |
= -@ | Timed subtraction and assignment | expr0 = expr1[*] -@ expr2
|
Multiply expr2 by delta time, subtract the result from expr1 and store everything to expr0 |
- Note
- ^ expr1 can represent expr0 too.
- Limit
- Multiple algebric operators per line are not allowed.
Compound assignment
Compound assignment operators store values or variable content to other variables having a particular type afterwards the computation of an arithmetic operation, to squeeze the code and clear it up from granted repetitions:
Operator | Name | Syntax | Description |
---|---|---|---|
+= | Addition assignment | expr0 += expr1
|
Add expr1 to expr0 and store the result to expr0 |
-= | Subtraction assignment | expr0 -= expr1
|
Subtract expr1 from expr0 and store the result to expr0 |
*= | Multiplication assignment | expr0 *= expr1
|
Multiply expr0 by expr1 and store the result to expr0 |
/= | Division assignment | expr0 /= expr1
|
Divide expr0 by expr1 and store the result to expr0 |
+=@ | Timed addition assignment | expr0 +=@ expr1
|
Multiply expr1 by delta time, add the result to expr0 and store everything to expr0 |
-=@ | Timed subtraction assignment | expr0 -=@ expr1
|
Multiply expr1 by delta time, subtract the result from expr0 and store everything to expr0 |
Uncompounded assignment
Uncompounded assignment operators are those on their own, or rather they are neither derivable nor decomposable similarly as those compounds:
Operator | Name | Syntax | Description |
---|---|---|---|
=# | Cast assignment | expr0 =# expr1
|
Cast expr1 to any other type and store the result to expr0 |
Logical
Logical operators influence the way conditions are evalueted and enable to test more of them at a time. More than anything, they are built-in statements:
Operator | Name | Syntax | Description |
---|---|---|---|
NOT | Logical negation | IF NOT condition0
|
Test if condition0 is false |
AND | Logical conjunction | IF condition0
|
Test if both condition0 and conditionN are true |
OR | Logical disjunction | IF condition0
|
Test if either condition0 or conditionN is true |
Comparison
Comparison operators test the truth or falsity of the relation between either a variable and a value, a value and a variable or two variables:
Operator | Name | Syntax | Description |
---|---|---|---|
= | Equal to | IF expr0 = expr1
|
Test if expr0 and expr1 are equal |
> | Greater than | IF expr0 > expr1
|
Test if expr0 is greater than expr1 |
<[*] | Lesser than | IF expr0 < expr1
|
Test if expr0 is lesser than expr1 |
>= | Greater than or equal to | IF expr0 >= expr1
|
Test if expr0 is greater than or equal to expr1 |
<=[*] | Lesser than or equal to | IF expr0 <= expr1
|
Test if expr0 is lesser than or equal to expr1 |
- Note
- ^ As a result of a critical bug, R* compiler mistakenly applies the operator inversion.
Commands
A command is a symbolic name associated to an identifier which executes a portion of code that specifies the operation to be performed by passing zero or more arguments. An argument is in turn some data given as input to a command. Normally, commands have a defined amount of arguments and those not, such as START_NEW_SCRIPT, can pass as many arguments as the available local variables are, except timers. Being a procedure, a command does not return values that can be assigned to a variable, even though the boolean flag is kept whenever it is used as a condition. It follows the common programming syntax adopted for function calls:
{commandname} [{anyvalue0|varname0} ... {anyvalueN|varnameN}]
Alternators
An alternator is an operation that is implemented with a set of commands rather than a single command, with a specific implementation being chosen by the compiler based on the data types used in the operation. For example, the game sees adding an integer to an integer as a different operation to adding an integer to a float, so there are multiple commands that add values together that differ only in parameter types.
Each command in a set has the same arity as the other commands in the set. The only difference is the combination and order of the parameter types.
The following commands are all alternators:
- :
- SET (=)
- CSET (=#)
- ADD_THING_TO_THING (+=)
- SUB_THING_FROM_THING (-=)
- MULT_THING_BY_THING (*=)
- DIV_THING_BY_THING (/=)
- IS_THING_EQUAL_TO_THING (=)
- IS_THING_NOT_EQUAL_TO_THING (NOT =)
- IS_THING_GREATER_THAN_THING (>, <=)
- IS_THING_GREATER_OR_EQUAL_TO_THING (>=, <)
- ADD_THING_TO_THING_TIMED (+=@)
- SUB_THING_FROM_THING_TIMED (-=@)
- ABS
- :
- IS_EMPTY
- IS_BIT_SET
- SET_BIT
- CLEAR_BIT
- STRING_CAT
This section is incomplete. You can help by fixing and expanding it.
Hardcoded
Hardcoded commands are those which have unique characteristics and are handled internally:
- :
- GOTO
- GOTO_IF_FALSE
- TERMINATE_THIS_SCRIPT
- START_NEW_SCRIPT
- VAR_INT
- VAR_FLOAT
- LVAR_INT
- LVAR_FLOAT
- {
- }
- REPEAT
- ENDREPEAT
- IF
- IFNOT
- ELSE
- ENDIF
- WHILE
- WHILENOT
- ENDWHILE
- ANDOR
- LAUNCH_MISSION
- SAVE_VAR_INT
- SAVE_VAR_FLOAT
- START_CUTSCENE[*]
- PLAYER_MADE_PROGRESS
- SET_PROGRESS_TOTAL[*]
- REGISTER_MISSION_GIVEN[*]
- REGISTER_MISSION_PASSED
- SCRIPT_NAME
- LOAD_AND_LAUNCH_MISSION
- LOAD_AND_LAUNCH_MISSION_INTERNAL
- SET_TOTAL_NUMBER_OF_MISSIONS[*]
- VAR_TEXT_LABEL
- LVAR_TEXT_LABEL
- :
- REGISTER_ODDJOB_MISSION_PASSED
- :
- GOTO_IF_TRUE
- GOSUB_FILE
- :
- CREATE_COLLECTABLE1
- SET_COLLECTABLE1_TOTAL[*]
- :
- LOAD_AND_LAUNCH_MISSION_EXCLUSIVE
- :
- CALL
- CALLNOT
- :
- VAR_TEXT_LABEL16
- LVAR_TEXT_LABEL16
- SWITCH
- ENDSWITCH
- CASE
- DEFAULT
- BREAK
- SWITCH_START
- SWITCH_CONTINUED
- :
- SAVE_VAR_TEXT_LABEL[*]
- SET_COLLECTABLE2_TOTAL
- Notes
- ^ The argument of these commands must be set respectively according to:
- The sum of PLAYER_MADE_PROGRESS values;
- The amount of REGISTER_MISSION_PASSED (those that don't have an immediate value are excluded) and REGISTER_ODDJOB_MISSION_PASSED;
- The amount of CREATE_COLLECTABLE1.
- If the argument of the listed commands differs from what expected, a 0-value must be passed;
- ^ This command was intended to be counted originally but its counter got deprecated.
This section is incomplete. You can help by fixing and expanding it.
WAIT
WAIT stops the execution of a script according to some milliseconds after which it will resume again. Indeed, it is absolutely necessary into infinite loops or those that may or may not break after more than one frame, such as the WHILE control flow. In this case, a INT equal to 0 is passed.
GOTO
GOTO jumps to the label of any location of the source code but conceptually it should never point off the current context. It is also used internally to build the control flows offered by the scripting language:
// File: any.sc goto_ref0: GOTO goto_refN
// File: any.sc goto_refN: GOTO goto_ref0
ANDOR
ANDOR sets out the way the comparison among more conditions have to occur (see also Compare flag).
GOTO_IF_TRUE
GOTO_IF_TRUE operates in conjunction with ANDOR and jumps to a label if the returned boolean flag is true.
GOTO_IF_FALSE
Unlike GOTO_IF_TRUE, GOTO_IF_FALSE jumps to the desired label only if the comparison returns false.
SCRIPT_NAME
SCRIPT_NAME simply associates an unique name to the current working script.
- Note
- R* compiler doesn't enable you to associate a name previously used for another script.
SAVE_STRING_TO_DEBUG_FILE
SAVE_STRING_TO_DEBUG_FILE accepts an argument which can admit up to 127 characters plus the null-terminator. In the compiling process, the argument is skipped but its string is copied to a predefined 128-bytes buffer, compiled afterwards. Since , these are the seemingly predetermined bytes of a random empty string block which are actually the result of uninitialized data:
00 00 41 00 09 2E 00 00 00 00 00 00 00 00 00 00 09 2E 00 00 00 00 00 00 1C FB 12 00 D8 A8 41 00 00 00 41 00 09 2E 00 00 00 00 00 00 01 00 00 00 09 2E 00 00 00 00 00 00 1C FB 12 00 D8 A8 41 00 00 00 41 00 09 2E 00 00 00 00 00 00 02 00 00 00 09 2E 00 00 00 00 00 00 1C FB 12 00 D8 A8 41 00 00 00 41 00 09 2E 00 00 00 00 00 00 03 00 00 00 09 2E 00 00 00 00 00 00 1C FB 12 00 D8 A8 41 00
The split of such bytes into 4 blocks of 32-bytes each is quite noticeable.
Constants
A constant is a symbolic name associated to a specific value. When compiling, their caption is converted to the assigned value. Since , names and identifiers of objects within OBJS and TOBJ blocks are loaded from every IDE file defined into gta_vc.dat, then those of vehicles and pedestrians within PEDS and CARS blocks are retrieved from default.ide. In , they are listed into TXT files, whose name follows the Pascal Case (eg. AudioEvents.txt). These files respect the syntax below:
{constname0} {constvalue0} {constnameN} {constvalueN}
Constant names and values are divided by as many spaces or tabulations as you want. Constant lines are distinguished by two \n (new line) characters. The model names which aren't assigned to a constant are still valid (see also Identifiers). Keep in mind arguments of some commands having the CONST type accept only constant values of a single namespace.
- Notes
- Constants don't collide even though they belong to different namespaces;
- In and , they are hardcoded as everything inside R* compiler;
- In , the subdivision of constant namespaces in files might be just a listing of hardcoded constants useful for developers. The same would apply to and .
Formatting
Everything is case-insensitive, that means the uppercase and lowercase letters have no dissimilarities when taken. Usually, the source code is conform to the same formatting according to:
- Labels and variables are entirely in lowercase;
- Declarations, commands and control flows are in uppercase;
- Constants are mostly in uppercase but the lowercase variant can be seen as well.
Compiling
Structure
The source code is split up into several SC files which comprehend main file, foreign gosubs, subscripts, mission scripts, and streamed scripts. These files can be included more times because they are actually processed once.
Main file
The main file is the most significant part of the whole source. It can include many script files and/or embedded gosubs, scripts or functions. Originally, it is characterized by the absence of the local scope. It must be put outside the directory, having the same name as the main script file, where all other foreign scripts must be:
<directory> | main | | gosub | | |- gosub1.sc | | \- gosubN.sc | | subscript | | |- subscript1.sc | | \- subscriptN.sc | | mission_guy | | |- mission_guy1.sc | | \- mission_guyN.sc | |- gosub.sc | |- subscript.sc | \- mission.sc \- main.sc
- Note
- R* compiler will scan subfolders too.
Foreign gosubs
Foreign gosubs (also called subroutines) are main extension files. They are called using the GOSUB_FILE command which jumps to a specific label and executes some code that returns back to the place where it has been called with RETURN. You are able to specify the gosub label to start jumping at as well:
// File: main.sc GOSUB_FILE gosub_ref foreign_gosub.sc
// File: foreign_gosub.sc gosub_ref: { [...] } RETURN
- Limit
- Foreign gosubs were introduced since . They were unused in and got removed in , but then they were reimplemented in and .
Gosubs
As mentioned, gosubs are also embedded in any script file. They follow almost the same rules, except they are called by GOSUB and can actually inehrit the local scope of the parent script:
// File: any.sc GOSUB gosub_ref
// File: any.sc gosub_ref: { [...] } RETURN
- Note
- R* compiler doesn't take care if the code within a scope jumps to a gosub inside which another scope is declared. It is strongly recommended to pay attention at this issue or you will fall down into an irreparable local variable mismatch.
Subscripts
Subscripts are code blocks which take part of a queue of other scripts and are allocated over the memory by LAUNCH_MISSION. They are denoted by the presence of MISSION_START at the very top of the mission file. As long as they aren't ended with MISSION_END, their execution never expires till the end of the game process. Each one works independently, even though they are able to share global variables:
// File: main.sc LAUNCH_MISSION subscript.sc
// File: subscript.sc MISSION_START [VAR_* {varname0}[,] [... {varnameN}]] SCRIPT_NAME main subscript_loop: { [LVAR_* {varname0}[,] [... {varnameN}]] [...] } //GOTO subscript_loop MISSION_END
- Notes
- MISSION_START is a special and fake directive that isn't assigned to any command. R* compiler will notify an error if it isn't placed at the first line of a subscript or a mission script;
- MISSION_END is an alias of TERMINATE_THIS_SCRIPT.
Scripts
As for gosubs, scripts can be embedded everywhere in a script file. They are started by START_NEW_SCRIPT which has an undefined amount of arguments, whose type must match with those of each local variable of the starting script in order to be passed, else the compilation will interrupt. Unlike subscripts, they get terminated by TERMINATE_THIS_SCRIPT or TERMINATE_ALL_SCRIPTS_WITH_THIS_NAME (elsewhere in another script):
// File: any.sc START_NEW_SCRIPT script [{anyvalue0|varname0} ... {anyvalueN|varnameN}]
// File: any.sc script: { SCRIPT_NAME script script_loop: [LVAR_* {varname0}[,] [... {varnameN}]] [...] //GOTO script_loop TERMINATE_THIS_SCRIPT }
- Notes
- Scripts must have a local scope;
- Script commands must be inserted within or after the local scope;
- Since , the opening curly bracket must be put before the script label when more arguments are passed.
Functions
This section is incomplete. You can help by fixing and expanding it.
Mission scripts
Mission scripts are those subscripts which are responsible for the presence of a storyline in the game. When they are launched with LOAD_AND_LAUNCH_MISSION, the mission is loaded in the mission block, allocated over the memory and the script pointer is moved to the corresponding mission offset. Do not forget to begin a mission script with MISSION_START and end it with MISSION_END:
// File: main.sc LOAD_AND_LAUNCH_MISSION mission.sc
// File: mission.sc MISSION_START GOSUB mission_start IF HAS_DEATHARREST_BEEN_EXECUTED GOSUB mission_failed ENDIF GOSUB mission_cleanup MISSION_END [VAR_* {varname0}[,] [... {varnameN}]] mission_start: REGISTER_MISSION_GIVEN SCRIPT_NAME mission // Variables initialization { [LVAR_* {varname0}[,] [... {varnameN}]] [...] } GOTO mission_passed mission_failed: [...] RETURN mission_passed: REGISTER_MISSION_PASSED mission //PLAYER_MADE_PROGRESS 1 [...] RETURN // Mark everything as no longer needed mission_cleanup: //MISSION_HAS_FINISHED [...] RETURN
Some missions doesn't need to be executed multiple times because they may just initialize some global variables defined in the main script or launch the intro mission. For this matter, here comes the usage of LOAD_AND_LAUNCH_MISSION_EXCLUSIVE:
// File: main.sc LOAD_AND_LAUNCH_MISSION_EXCLUSIVE initial.sc LOAD_AND_LAUNCH_MISSION_EXCLUSIVE intro.sc
- Note
- Exclusive missions are never launched in the source code. It's likely, it was an idea not came to the end successfully or rather they were useful for debugging purposes.
- Limits
- LOAD_AND_LAUNCH_MISSION_EXCLUSIVE is available only in and ;
- Only 2 exclusive missions in and 3 in are handled, plus they must be launched before any of the counterpart.
Streamed scripts
This section is incomplete. You can help by fixing and expanding it.
Control flow
As usual, the evolution of something implies its development over the years. Alongside, the control flows implementation has been distributed equally into every chapter. Their definitions are similar to those used in pseudocodes resulting in a raw source code. However, you are still able to build your own control flows:
ANDOR {value} [NOT] {condition0} [[NOT] {condition8}] GOTO_IF_FALSE ELSE {consequence} [GOTO ENDIF] ELSE: [{alternative} ENDIF:]
- Note
- It's likely, user-made control flows weren't intended to be usable because R* compiler cannot recognize an equal to rather than an assignment operator.
If
IF
is the most common conditional statement. It executes one block of code if a boolean condition is true
, and (optionally) another if the condition is false
. The condition may be made of multiple conditions combined using the logical operators AND
or OR
, or it may just be a single condition. AND
makes the final condition true
only if all of the input conditions are true
, whereas OR
only requires one input condition to be true
. The condition may be inverted with NOT
, another logical operator. Syntax:
IF [NOT] {condition0}
[AND|OR [NOT] {condition8}]
{consequence}
[ELSE
{alternative}]
ENDIF
- Limitations
- There is a maximum of 8 conditions.
If-not
IFNOT
works in the same way as IF
except that the condition is always inverted.
IFNOT [NOT] {condition0}
[AND|OR [NOT] {condition8}]
{consequence}
[ELSE
{alternative}]
ENDIF
- Limitations
While
WHILE
is similar to IF
, but it continues executing the body until the condition is false
.
WHILE [NOT] {condition0}
[AND|OR [NOT] {condition8}]
{consequence}
ENDWHILE
While-not
WHILENOT
is to WHILE
as IFNOT
is to IF
: it loops until the condition is true
.
WHILENOT [NOT] {condition0}
[AND|OR [NOT] {condition8}]
{consequence}
ENDWHILE
- Limitations
Repeat
REPEAT
acts as a range-based loop (generally a for-loop in modern programming languages). It executes the body and increments a certain variable until it reaches a target value, at which point the loop stops.
REPEAT {target} {varname}
{consequence}
ENDREPEAT
- Limitations
REPEAT
is supported in games from ;- The target value must be positive;
- The body will be read at least once in any case.
Switch
A SWITCH
statement is functionally equivalent to multiple nested IF...ELSE
statements. It takes a single value and executes a block of code that is specific to that value (a CASE
) until it finds a BREAK
, which moves execution to the end of the construct. If no matching case is found, the DEFAULT
clause is executed.
SWITCH {varname}
CASE {value0}
{consequence}
BREAK
[CASE {valueN}
{consequence}
BREAK]
[DEFAULT
{alternative}
BREAK]
ENDSWITCH
- Limitations
- It is supported from ;
CASE
allows the use of INT and CONST values only;- In , the
CASE
values should be sorted (R*'s compiler should do it implicitly); - Every
CASE
, includingDEFAULT
, must end with aBREAK
.
Decompiling
Structure
For further information about the SCM file format, read this article. Take into account the compiling order of each SC file is main file » foreign gosubs » subscripts » mission scripts apart from the reading order of the commands used to include them. Streamed scripts are compiled individually into the script.img file. On the other hand, functions are compiled like gosubs.
Identifiers
Undefined constants of model identifiers, whose name refers to a DFF which is presumably archived into any of the IMGs, loaded by the game, are overwritten by a decrementing value in the order they get compiled. These model names are then put into the second segment of the SCM header. Those of mission scripts and streamed scripts respect the same rule except the fact they are turned into a zero-based growing identifier, while exclusive mission scripts are launched by a negative identifier resulting from the bits inversion (bitwise complement).
Offsets
An offset is a 32-bit signed integer which points to a location of the script file. Those within the main file, foreign gosubs and subscripts are absolute offsets that start from the beginning of the main script, while the ones inside mission scripts and streamed scripts are relative and negative offsets starting from their beginning. The offset is related to global variables as well, whose interval goes from 8 and ends to 65532 (0xFFFC), each one is aligned to the nearest 4 bytes.
Variables range
The following table shows the variables range of the local scope for each game version:
Context | |||||
---|---|---|---|---|---|
Foreign gosub/Gosub | 0-15 | 0-15 | n/a | 0-95 | 0-95 |
Subscript/Script | 0-15 | 0-15 | 0-31 | 0-95 | 0-95 |
Mission script | 0-15 | 0-15 | 0-1023 | 0-95 | 0-95 |
Streamed script | n/a | n/a | 0-31 | n/a | n/a |
Function | n/a | n/a | n/a | 0-95 | 0-95 |
Timer | 16-17 | 16-17 | 32-33 | t0-t1 | t0-t1 |
Operators composition
As far as you wouldn't know, SCM's operators always take two operands to compute an operation. Their composition is listed below:
Operator/s | Name | Syntax | Composition | |
---|---|---|---|---|
++ | Increment | Pre | ++ expr0
|
expr0 += 1
|
Post | expr0 ++
| |||
-- | Decrement | Pre | -- expr0
|
expr0 -= 1
|
Post | expr0 --
| |||
= + | Addition and assignment | expr0 = expr1 + expr2
|
expr0 = expr1
| |
= - | Subtraction and assignment | expr0 = expr1 - expr2
|
expr0 = expr1
| |
= * | Multiplication and assignment | expr0 = expr1 * expr2
|
expr0 = expr1
| |
= / | Division and assignment | expr0 = expr1 / expr2
|
expr0 = expr1
| |
= +@ | Timed addition and assignment | expr0 = expr1 +@ expr2
|
expr0 = expr1
| |
= -@ | Timed subtraction and assignment | expr0 = expr1 -@ expr2
|
expr0 = expr1
|
Command ID
A command ID is a 16-bit signed integer internal and progressive identifier, somehow referred to as part of the hexadecimal representation of an opcode, which identifies the command to execute at runtime, forming the game's script bytecode. The maximum number of available commands is 32767 (0x7FFF), since the least significant bit (0x8000) is set whenever they are used as negative conditions (those with the NOT logical operator, just to clear things up).
Command arguments
The limitation of the amount of arguments a variadic command can pass is game specific:
Managed commands
Here is the list of all managed commands and their relative specifications:
Legend:
- Specifier:
- R, LABEL;
- I, INT;
- F, FLOAT;
- T, TEXT_LABEL;
- T16, TEXT_LABEL16;
- T32, TEXT_LABEL32;
- T[N], TEXT;
- C, CONST;
- M, MULTI.
- Suffix:
- O, OPTIONAL.
List:
Be aware, the argument data types of the commands below are just informative:
Command | ID | Arguments | |||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
# | 1 | 2 | 3 | 4 | ... | 18 | ... | n+l | |||
n+i+o | |||||||||||
MISSION_START[*] | 0 | ||||||||||
GOTO[*] | 2 | 1 | R | ||||||||
= SET |
SET_VAR_INT | 4 | 2 | VI | I | ||||||
SET_VAR_FLOAT | 5 | VF | F | ||||||||
SET_LVAR_INT | 6 | LI | I | ||||||||
SET_LVAR_FLOAT | 7 | LF | F | ||||||||
SET_VAR_INT_TO_VAR_INT | 132 | VI | VI | ||||||||
SET_LVAR_INT_TO_LVAR_INT | 133 | LI | LI | ||||||||
SET_VAR_FLOAT_TO_VAR_FLOAT | 134 | VF | VF | ||||||||
SET_LVAR_FLOAT_TO_LVAR_FLOAT | 135 | LF | LF | ||||||||
SET_VAR_FLOAT_TO_LVAR_FLOAT | 136 | VF | LF | ||||||||
SET_LVAR_FLOAT_TO_VAR_FLOAT | 137 | LF | VF | ||||||||
SET_VAR_INT_TO_LVAR_INT | 138 | VI | LI | ||||||||
SET_LVAR_INT_TO_VAR_INT | 139 | LI | VI | ||||||||
+= + ADD_THING_TO_THING |
ADD_VAL_TO_INT_VAR | 8 | 2 | VI | I | ||||||
ADD_VAL_TO_FLOAT_VAR | 9 | VF | F | ||||||||
ADD_VAL_TO_INT_LVAR | 10 | LI | I | ||||||||
ADD_VAL_TO_FLOAT_LVAR | 11 | LF | F | ||||||||
ADD_INT_VAR_TO_INT_VAR | 88 | VI | VI | ||||||||
ADD_FLOAT_VAR_TO_FLOAT_VAR | 89 | VF | VF | ||||||||
ADD_INT_LVAR_TO_INT_LVAR | 90 | LI | LI | ||||||||
ADD_FLOAT_LVAR_TO_FLOAT_LVAR | 91 | LF | LF | ||||||||
ADD_INT_VAR_TO_INT_LVAR | 92 | LI | VI | ||||||||
ADD_FLOAT_VAR_TO_FLOAT_LVAR | 93 | LF | VF | ||||||||
ADD_INT_LVAR_TO_INT_VAR | 94 | VI | LI | ||||||||
ADD_FLOAT_LVAR_TO_FLOAT_VAR | 95 | VF | LF | ||||||||
-= - SUB_THING_FROM_THING |
SUB_VAL_FROM_INT_VAR | 12 | 2 | VI | I | ||||||
SUB_VAL_FROM_FLOAT_VAR | 13 | VF | F | ||||||||
SUB_VAL_FROM_INT_LVAR | 14 | LI | I | ||||||||
SUB_VAL_FROM_FLOAT_LVAR | 15 | LF | F | ||||||||
SUB_INT_VAR_FROM_INT_VAR | 96 | VI | VI | ||||||||
SUB_FLOAT_VAR_FROM_FLOAT_VAR | 97 | VF | VF | ||||||||
SUB_INT_LVAR_FROM_INT_LVAR | 98 | LI | LI | ||||||||
SUB_FLOAT_LVAR_FROM_FLOAT_LVAR | 99 | LF | LF | ||||||||
SUB_INT_VAR_FROM_INT_LVAR | 100 | LI | VI | ||||||||
SUB_FLOAT_VAR_FROM_FLOAT_LVAR | 101 | LF | VF | ||||||||
SUB_INT_LVAR_FROM_INT_VAR | 102 | VI | LI | ||||||||
SUB_FLOAT_LVAR_FROM_FLOAT_VAR | 103 | VF | LF | ||||||||
*= * MULT_THING_BY_THING |
MULT_INT_VAR_BY_VAL | 16 | 2 | VI | I | ||||||
MULT_FLOAT_VAR_BY_VAL | 17 | VF | F | ||||||||
MULT_INT_LVAR_BY_VAL | 18 | LI | I | ||||||||
MULT_FLOAT_LVAR_BY_VAL | 19 | LF | F | ||||||||
MULT_INT_VAR_BY_INT_VAR | 104 | VI | VI | ||||||||
MULT_FLOAT_VAR_BY_FLOAT_VAR | 105 | VF | VF | ||||||||
MULT_INT_LVAR_BY_INT_LVAR | 106 | LI | LI | ||||||||
MULT_FLOAT_LVAR_BY_FLOAT_LVAR | 107 | LF | LF | ||||||||
MULT_INT_VAR_BY_INT_LVAR | 108 | VI | LI | ||||||||
MULT_FLOAT_VAR_BY_FLOAT_LVAR | 109 | VF | LF | ||||||||
MULT_INT_LVAR_BY_INT_VAR | 110 | LI | VI | ||||||||
MULT_FLOAT_LVAR_BY_FLOAT_VAR | 111 | LF | VF | ||||||||
/= / DIV_THING_BY_THING |
DIV_INT_BY_VAL | 20 | 2 | VI | I | ||||||
DIV_FLOAT_VAR_BY_VAL | 21 | VF | F | ||||||||
DIV_INT_LVAR_BY_VAL | 22 | LI | I | ||||||||
DIV_FLOAT_LVAR_BY_VAL | 23 | LF | F | ||||||||
DIV_INT_VAR_BY_INT_VAR | 112 | VI | VI | ||||||||
DIV_FLOAT_VAR_BY_FLOAT_VAR | 113 | VF | VF | ||||||||
DIV_INT_LVAR_BY_INT_LVAR | 114 | LI | LI | ||||||||
DIV_FLOAT_LVAR_BY_FLOAT_LVAR | 115 | LF | LF | ||||||||
DIV_INT_VAR_BY_INT_LVAR | 116 | VI | LI | ||||||||
DIV_FLOAT_VAR_BY_FLOAT_LVAR | 117 | VF | LF | ||||||||
DIV_INT_LVAR_BY_INT_VAR | 118 | LI | VI | ||||||||
DIV_FLOAT_LVAR_BY_FLOAT_VAR | 119 | LF | VF | ||||||||
> <= IS_THING_GREATER_THAN_THING |
IS_INT_VAR_GREATER_THAN_NUMBER | 24 | 2 | VI | I | ||||||
IS_INT_LVAR_GREATER_THAN_NUMBER | 25 | LI | I | ||||||||
IS_NUMBER_GREATER_THAN_INT_VAR | 26 | I | VI | ||||||||
IS_NUMBER_GREATER_THAN_INT_LVAR | 27 | I | LI | ||||||||
IS_INT_VAR_GREATER_THAN_INT_VAR | 28 | VI | VI | ||||||||
IS_INT_LVAR_GREATER_THAN_INT_LVAR | 29 | LI | LI | ||||||||
IS_INT_VAR_GREATER_THAN_INT_LVAR | 30 | VI | LI | ||||||||
IS_INT_LVAR_GREATER_THAN_INT_VAR | 31 | LI | VI | ||||||||
IS_FLOAT_VAR_GREATER_THAN_NUMBER | 32 | VF | F | ||||||||
IS_FLOAT_LVAR_GREATER_THAN_NUMBER | 33 | LF | F | ||||||||
IS_NUMBER_GREATER_THAN_FLOAT_VAR | 34 | F | VF | ||||||||
IS_NUMBER_GREATER_THAN_FLOAT_LVAR | 35 | F | LF | ||||||||
IS_FLOAT_VAR_GREATER_THAN_FLOAT_VAR | 36 | VF | VF | ||||||||
IS_FLOAT_LVAR_GREATER_THAN_FLOAT_LVAR | 37 | LF | LF | ||||||||
IS_FLOAT_VAR_GREATER_THAN_FLOAT_LVAR | 38 | VF | LF | ||||||||
IS_FLOAT_LVAR_GREATER_THAN_FLOAT_VAR | 39 | LF | VF | ||||||||
>= < IS_THING_GREATER_OR_EQUAL_TO_THING |
IS_INT_VAR_GREATER_OR_EQUAL_TO_NUMBER | 40 | 2 | VI | I | ||||||
IS_INT_LVAR_GREATER_OR_EQUAL_TO_NUMBER | 41 | LI | I | ||||||||
IS_NUMBER_GREATER_OR_EQUAL_TO_INT_VAR | 42 | I | VI | ||||||||
IS_NUMBER_GREATER_OR_EQUAL_TO_INT_LVAR | 43 | I | LI | ||||||||
IS_INT_VAR_GREATER_OR_EQUAL_TO_INT_VAR | 44 | VI | VI | ||||||||
IS_INT_LVAR_GREATER_OR_EQUAL_TO_INT_LVAR | 45 | LI | LI | ||||||||
IS_INT_VAR_GREATER_OR_EQUAL_TO_INT_LVAR | 46 | VI | LI | ||||||||
IS_INT_LVAR_GREATER_OR_EQUAL_TO_INT_VAR | 47 | LI | VI | ||||||||
IS_FLOAT_VAR_GREATER_OR_EQUAL_TO_NUMBER | 48 | VF | F | ||||||||
IS_FLOAT_LVAR_GREATER_OR_EQUAL_TO_NUMBER | 49 | LF | F | ||||||||
IS_NUMBER_GREATER_OR_EQUAL_TO_FLOAT_VAR | 50 | F | VF | ||||||||
IS_NUMBER_GREATER_OR_EQUAL_TO_FLOAT_LVAR | 51 | F | LF | ||||||||
IS_FLOAT_VAR_GREATER_OR_EQUAL_TO_FLOAT_VAR | 52 | VF | VF | ||||||||
IS_FLOAT_LVAR_GREATER_OR_EQUAL_TO_FLOAT_LVAR | 53 | LF | LF | ||||||||
IS_FLOAT_VAR_GREATER_OR_EQUAL_TO_FLOAT_LVAR | 54 | VF | LF | ||||||||
IS_FLOAT_LVAR_GREATER_OR_EQUAL_TO_FLOAT_VAR | 55 | LF | VF | ||||||||
= IS_THING_EQUAL_TO_THING |
IS_INT_VAR_EQUAL_TO_NUMBER | 56 | 2 | VI | I | ||||||
IS_INT_LVAR_EQUAL_TO_NUMBER | 57 | LI | I | ||||||||
IS_INT_VAR_EQUAL_TO_INT_VAR | 58 | VI | VI | ||||||||
IS_INT_LVAR_EQUAL_TO_INT_LVAR | 59 | LI | LI | ||||||||
IS_INT_VAR_EQUAL_TO_INT_LVAR | 60 | VI | LI | ||||||||
IS_FLOAT_VAR_EQUAL_TO_NUMBER | 66 | VF | F | ||||||||
IS_FLOAT_LVAR_EQUAL_TO_NUMBER | 67 | LF | F | ||||||||
IS_FLOAT_VAR_EQUAL_TO_FLOAT_VAR | 68 | VF | VF | ||||||||
IS_FLOAT_LVAR_EQUAL_TO_FLOAT_LVAR | 69 | LF | LF | ||||||||
IS_FLOAT_VAR_EQUAL_TO_FLOAT_LVAR | 70 | VF | LF | ||||||||
NOT = IS_THING_NOT_EQUAL_TO_THING |
IS_INT_VAR_NOT_EQUAL_TO_NUMBER | 61 | 2 | VI | I | ||||||
IS_INT_LVAR_NOT_EQUAL_TO_NUMBER | 62 | LI | I | ||||||||
IS_INT_VAR_NOT_EQUAL_TO_INT_VAR | 63 | VI | VI | ||||||||
IS_INT_LVAR_NOT_EQUAL_TO_INT_LVAR | 64 | LI | LI | ||||||||
IS_INT_VAR_NOT_EQUAL_TO_INT_LVAR | 65 | VI | LI | ||||||||
IS_FLOAT_VAR_NOT_EQUAL_TO_NUMBER | 71 | VF | F | ||||||||
IS_FLOAT_LVAR_NOT_EQUAL_TO_NUMBER | 72 | LF | F | ||||||||
IS_FLOAT_VAR_NOT_EQUAL_TO_FLOAT_VAR | 73 | VF | VF | ||||||||
IS_FLOAT_LVAR_NOT_EQUAL_TO_FLOAT_LVAR | 74 | LF | LF | ||||||||
IS_FLOAT_VAR_NOT_EQUAL_TO_FLOAT_LVAR | 75 | VF | LF | ||||||||
GOTO_IF_FALSE[*] | 77 | 1 | R | ||||||||
TERMINATE_THIS_SCRIPT | 78 | 0 | |||||||||
MISSION_END | |||||||||||
START_NEW_SCRIPT[*] | 79 | 1+l | R | AM | |||||||
GOSUB | 80 | 1 | R | ||||||||
RETURN | 81 | 0 | |||||||||
+=@ +@ ADD_THING_TO_THING_TIMED |
ADD_TIMED_VAL_TO_FLOAT_VAR | 120 | 2 | VF | F | ||||||
ADD_TIMED_VAL_TO_FLOAT_LVAR | 121 | LF | F | ||||||||
ADD_TIMED_FLOAT_VAR_TO_FLOAT_VAR | 122 | VF | VF | ||||||||
ADD_TIMED_FLOAT_LVAR_TO_FLOAT_LVAR | 123 | LF | LF | ||||||||
ADD_TIMED_FLOAT_LVAR_TO_FLOAT_VAR | 124 | LF | VF | ||||||||
ADD_TIMED_FLOAT_VAR_TO_FLOAT_LVAR | 125 | VF | LF | ||||||||
-=@ -@ SUB_THING_FROM_THING_TIMED |
SUB_TIMED_VAL_FROM_FLOAT_VAR | 126 | 2 | VF | F | ||||||
SUB_TIMED_VAL_FROM_FLOAT_LVAR | 127 | LF | F | ||||||||
SUB_TIMED_FLOAT_VAR_FROM_FLOAT_VAR | 128 | VF | VF | ||||||||
SUB_TIMED_FLOAT_LVAR_FROM_FLOAT_LVAR | 129 | LF | LF | ||||||||
SUB_TIMED_FLOAT_LVAR_FROM_FLOAT_VAR | 130 | LF | VF | ||||||||
SUB_TIMED_FLOAT_VAR_FROM_FLOAT_LVAR | 131 | VF | LF | ||||||||
=# CSET |
CSET_VAR_INT_TO_VAR_FLOAT | 140 | 2 | VI | VF | ||||||
CSET_VAR_FLOAT_TO_VAR_INT | 141 | VF | VI | ||||||||
CSET_LVAR_INT_TO_VAR_FLOAT | 142 | LI | VF | ||||||||
CSET_LVAR_FLOAT_TO_VAR_INT | 143 | LF | VI | ||||||||
CSET_VAR_INT_TO_LVAR_FLOAT | 144 | VI | LF | ||||||||
CSET_VAR_FLOAT_TO_LVAR_INT | 145 | VF | LI | ||||||||
CSET_LVAR_INT_TO_LVAR_FLOAT | 146 | LI | LF | ||||||||
CSET_LVAR_FLOAT_TO_LVAR_INT | 147 | LF | LI | ||||||||
ABS | ABS_VAR_INT | 148 | 1 | VI | |||||||
ABS_LVAR_INT | 149 | LI | |||||||||
ABS_VAR_FLOAT | 150 | VF | |||||||||
ABS_LVAR_FLOAT | 151 | LF | |||||||||
VAR_INT[*] | 199 | n | VI | VIO | |||||||
VAR_FLOAT[*] | 200 | n | VF | VFO | |||||||
LVAR_INT[*] | 201 | n | LI | LIO | |||||||
LVAR_FLOAT[*] | 202 | n | LF | LFO | |||||||
{[*] | 203 | 0 | |||||||||
}[*] | 204 | 0 | |||||||||
IF[*] | 207 | 1 | I | ||||||||
ELSE[*] | 209 | 0 | |||||||||
ENDIF[*] | 210 | 0 | |||||||||
WHILE[*] | 211 | 1 | I | ||||||||
ENDWHILE[*] | 213 | 0 | |||||||||
ANDOR[*] | 214 | 1 | I | ||||||||
LAUNCH_MISSION | 215 | 1 | R | ||||||||
START_CUTSCENE | 743 | 0 | |||||||||
PLAYER_MADE_PROGRESS | 780 | 1 | I | ||||||||
SET_PROGRESS_TOTAL | 781 | 1 | I | ||||||||
REGISTER_MISSION_GIVEN | 791 | 0 | |||||||||
REGISTER_MISSION_PASSED | 792 | 1 | T | ||||||||
SCRIPT_NAME | 932 | 1 | T | ||||||||
LOAD_AND_LAUNCH_MISSION | 1046 | 1 | R | ||||||||
LOAD_AND_LAUNCH_MISSION_INTERNAL | 1047 | 1 | I | ||||||||
SET_TOTAL_NUMBER_OF_MISSIONS | 1068 | 1 | I | ||||||||
REGISTER_ODDJOB_MISSION_PASSED | 1429 | 0 | |||||||||
REPEAT[*] | 205 | 2 | I | VLI | |||||||
ENDREPEAT[*] | 206 | 0 | |||||||||
CREATE_COLLECTABLE1 | 748 | 1 | I | ||||||||
SET_COLLECTABLE1_TOTAL | 749 | 1 | I | ||||||||
GOTO_IF_TRUE[*] | 76 | 1 | R | ||||||||
= IS_THING_EQUAL_TO_THING |
IS_INT_VAR_EQUAL_TO_CONSTANT | 1187 | 2 | VI | C | ||||||
IS_INT_LVAR_EQUAL_TO_CONSTANT | 1188 | LI | C | ||||||||
= SET |
SET_VAR_INT_TO_CONSTANT | 1198 | 2 | VI | C | ||||||
SET_LVAR_INT_TO_CONSTANT | 1199 | LI | C | ||||||||
> <= IS_THING_GREATER_THAN_THING |
IS_INT_VAR_GREATER_THAN_CONSTANT | 1200 | 2 | VI | C | ||||||
IS_INT_LVAR_GREATER_THAN_CONSTANT | 1201 | LI | C | ||||||||
IS_CONSTANT_GREATER_THAN_INT_VAR | 1202 | C | VI | ||||||||
IS_CONSTANT_GREATER_THAN_INT_LVAR | 1203 | C | LI | ||||||||
>= < IS_THING_GREATER_OR_EQUAL_TO_THING |
IS_INT_VAR_GREATER_OR_EQUAL_TO_CONSTANT | 1204 | 2 | VI | C | ||||||
IS_INT_LVAR_GREATER_OR_EQUAL_TO_CONSTANT | 1205 | LI | C | ||||||||
IS_CONSTANT_GREATER_OR_EQUAL_TO_INT_VAR | 1206 | C | VI | ||||||||
IS_CONSTANT_GREATER_OR_EQUAL_TO_INT_LVAR | 1207 | C | LI | ||||||||
LOAD_AND_LAUNCH_MISSION_EXCLUSIVE | 1301 | 1 | R | ||||||||
IFNOT[*] | 208 | 1 | I | ||||||||
WHILENOT[*] | 212 | 1 | I | ||||||||
GOSUB_FILE | 717 | 2 | R | R | |||||||
= SET |
SET_VAR_TEXT_LABEL | 1449 | 2 | VT | AT | ||||||
SET_LVAR_TEXT_LABEL | 1450 | LT | AT | ||||||||
VAR_TEXT_LABEL[*] | 1451 | n | VT | VTO | |||||||
LVAR_TEXT_LABEL[*] | 1452 | n | LT | LTO | |||||||
= IS_THING_EQUAL_TO_THING |
IS_VAR_TEXT_LABEL_EQUAL_TO_TEXT_LABEL | 1453 | 2 | VT | AT | ||||||
IS_LVAR_TEXT_LABEL_EQUAL_TO_TEXT_LABEL | 1454 | LT | AT | ||||||||
= SET |
SET_VAR_TEXT_LABEL16 | 1745 | 2 | VT16 | AT16 | ||||||
SET_LVAR_TEXT_LABEL16 | 1476 | LT16 | AT16 | ||||||||
VAR_TEXT_LABEL16[*] | 1477 | n | VT16 | VT16O | |||||||
LVAR_TEXT_LABEL16[*] | 1478 | n | LT16 | LT16O | |||||||
= IS_THING_EQUAL_TO_THING |
IS_INT_LVAR_EQUAL_TO_INT_VAR | 2006 | 2 | LI | VI | ||||||
IS_FLOAT_LVAR_EQUAL_TO_FLOAT_VAR | 2007 | LF | VF | ||||||||
IS_EMPTY[*] | IS_VAR_TEXT_LABEL_EMPTY | 2116 | 1 | VT | |||||||
IS_LVAR_TEXT_LABEL_EMPTY | 2117 | LT | |||||||||
IS_VAR_TEXT_LABEL16_EMPTY | 2118 | VT16 | |||||||||
IS_LVAR_TEXT_LABEL16_EMPTY | 2119 | LT16 | |||||||||
SWITCH[*] | 2120 | 1 | VLI | ||||||||
ENDSWITCH[*] | 2121 | 0 | |||||||||
CASE[*] | 2122 | 1 | I | ||||||||
DEFAULT[*] | 2123 | 0 | |||||||||
BREAK[*] | 2124 | 0 | |||||||||
SWITCH_START[*] | 2161 | 18 | VI | I | I | R | I | ||||
R | |||||||||||
LI | I | I | R | I | |||||||
R | |||||||||||
SWITCH_CONTINUED[*] | 2162 | 18 | I | ||||||||
R | |||||||||||
IS_BIT_SET[*] | IS_GLOBAL_VAR_BIT_SET_CONST | 2228 | 2 | VI | C | ||||||
IS_GLOBAL_VAR_BIT_SET_VAR | 2229 | VI | VI | ||||||||
IS_GLOBAL_VAR_BIT_SET_LVAR | 2230 | VI | LI | ||||||||
IS_LOCAL_VAR_BIT_SET_CONST | 2231 | LI | C | ||||||||
IS_LOCAL_VAR_BIT_SET_VAR | 2232 | LI | VI | ||||||||
IS_LOCAL_VAR_BIT_SET_LVAR | 2233 | LI | LI | ||||||||
SET_BIT[*] | SET_GLOBAL_VAR_BIT_CONST | 2234 | 2 | VI | C | ||||||
SET_GLOBAL_VAR_BIT_VAR | 2235 | VI | VI | ||||||||
SET_GLOBAL_VAR_BIT_LVAR | 2236 | VI | LI | ||||||||
SET_LOCAL_VAR_BIT_CONST | 2237 | LI | C | ||||||||
SET_LOCAL_VAR_BIT_VAR | 2238 | LI | VI | ||||||||
SET_LOCAL_VAR_BIT_LVAR | 2239 | LI | LI | ||||||||
CLEAR_BIT[*] | CLEAR_GLOBAL_VAR_BIT_CONST | 2240 | 2 | VI | C | ||||||
CLEAR_GLOBAL_VAR_BIT_VAR | 2241 | VI | VI | ||||||||
CLEAR_GLOBAL_VAR_BIT_LVAR | 2242 | VI | LI | ||||||||
CLEAR_LOCAL_VAR_BIT_CONST | 2243 | LI | C | ||||||||
CLEAR_LOCAL_VAR_BIT_VAR | 2244 | LI | VI | ||||||||
CLEAR_LOCAL_VAR_BIT_LVAR | 2245 | LI | LI | ||||||||
= IS_THING_EQUAL_TO_THING |
IS_VAR_TEXT_LABEL16_EQUAL_TO_TEXT_LABEL | 2297 | 2 | VT16 | AT16 | ||||||
IS_LVAR_TEXT_LABEL16_EQUAL_TO_TEXT_LABEL | 2298 | LT16 | AT16 | ||||||||
STRING_CAT[*] | STRING_CAT16 | 2443 | 3 | VT16 | VT16 | VT16 | |||||
STRING_CAT8 | 2444 | VT | VT | VT | |||||||
RETURN_TRUE | 197 | 0 | |||||||||
RETURN_FALSE | 198 | 0 | |||||||||
SWITCH[*][*] | 214 | 1 | VLI | ||||||||
ENDSWITCH[*][*] | 215 | 0 | |||||||||
CASE[*][*] | 216 | 1 | I | ||||||||
DEFAULT[*][*] | 217 | 0 | |||||||||
BREAK[*][*] | 218 | 0 | |||||||||
ANDOR[*] | 219 | 1 | I | ||||||||
LAUNCH_MISSION | 220 | 1 | R | ||||||||
SAVE_VAR_INT[*] | 502 | l | VI | VIO | |||||||
SAVE_VAR_FLOAT[*] | 503 | l | VF | VFO | |||||||
GOSUB_FILE | 722 | 2 | R | R | |||||||
START_CUTSCENE | 748 | 0 | |||||||||
PLAYER_MADE_PROGRESS | 785 | 1 | I | ||||||||
SET_PROGRESS_TOTAL | 786 | 1 | I | ||||||||
REGISTER_MISSION_GIVEN | 796 | 0 | |||||||||
REGISTER_MISSION_PASSED | 797 | 1 | T | ||||||||
SCRIPT_NAME | 937 | 1 | T | ||||||||
LOAD_AND_LAUNCH_MISSION | 1051 | 1 | R | ||||||||
LOAD_AND_LAUNCH_MISSION_INTERNAL | 1052 | 1 | I | ||||||||
SET_TOTAL_NUMBER_OF_MISSIONS | 1073 | 1 | I | ||||||||
= IS_THING_EQUAL_TO_THING |
IS_INT_VAR_EQUAL_TO_CONSTANT | 1192 | 2 | VI | C | ||||||
IS_INT_LVAR_EQUAL_TO_CONSTANT | 1193 | LI | C | ||||||||
= SET |
SET_VAR_INT_TO_CONSTANT | 1203 | 2 | VI | C | ||||||
SET_LVAR_INT_TO_CONSTANT | 1204 | LI | C | ||||||||
> <= IS_THING_GREATER_THAN_THING |
IS_INT_VAR_GREATER_THAN_CONSTANT | 1205 | 2 | VI | C | ||||||
IS_INT_LVAR_GREATER_THAN_CONSTANT | 1206 | LI | C | ||||||||
IS_CONSTANT_GREATER_THAN_INT_VAR | 1207 | C | VI | ||||||||
IS_CONSTANT_GREATER_THAN_INT_LVAR | 1208 | C | LI | ||||||||
>= < IS_THING_GREATER_OR_EQUAL_TO_THING |
IS_INT_VAR_GREATER_OR_EQUAL_TO_CONSTANT | 1209 | 2 | VI | C | ||||||
IS_INT_LVAR_GREATER_OR_EQUAL_TO_CONSTANT | 1210 | LI | C | ||||||||
IS_CONSTANT_GREATER_OR_EQUAL_TO_INT_VAR | 1211 | C | VI | ||||||||
IS_CONSTANT_GREATER_OR_EQUAL_TO_INT_LVAR | 1212 | C | LI | ||||||||
REGISTER_ODDJOB_MISSION_PASSED | 1434 | 0 | |||||||||
CALL[*][*][*] | 1454 | 1+i+o | R | AM | |||||||
VLM | |||||||||||
CALLNOT[*][*][*] | 1455 | 1+i+o | R | AM | |||||||
VLM | |||||||||||
= SET |
SET_INT[*] | 4 | 2 | VLI | I | ||||||
SET_FLOAT[*] | 5 | VLF | F | ||||||||
SET_TEXT_LABEL[*] | 6 | VLT | T | ||||||||
SET_INT_TO_INT[*] | 53 | VLI | VLI | ||||||||
SET_FLOAT_TO_FLOAT[*] | 54 | VLF | VLF | ||||||||
SET_TEXT_LABEL_TO_TEXT_LABEL[*] | 55 | VLT | VLT | ||||||||
SET_INT_TO_CONSTANT[*] | 738 | VLI | C | ||||||||
+= + ADD_THING_TO_THING |
ADD_VAL_TO_INT[*] | 7 | 2 | VLI | I | ||||||
ADD_VAL_TO_FLOAT[*] | 8 | VLF | F | ||||||||
ADD_INT_TO_INT[*] | 41 | VLI | VLI | ||||||||
ADD_FLOAT_TO_FLOAT[*] | 42 | VLF | VLF | ||||||||
-= - SUB_THING_FROM_THING |
SUB_VAL_FROM_INT[*] | 9 | 2 | VLI | I | ||||||
SUB_VAL_FROM_FLOAT[*] | 10 | VLF | F | ||||||||
SUB_INT_FROM_INT[*] | 43 | VLI | VLI | ||||||||
SUB_FLOAT_FROM_FLOAT[*] | 44 | VLF | VLF | ||||||||
*= * MULT_THING_BY_THING |
MULT_INT_BY_VAL[*] | 11 | 2 | VLI | I | ||||||
MULT_FLOAT_BY_VAL[*] | 12 | VLF | F | ||||||||
MULT_INT_BY_INT[*] | 45 | VLI | VLI | ||||||||
MULT_FLOAT_BY_FLOAT[*] | 46 | VLF | VLF | ||||||||
/= / DIV_THING_BY_THING |
DIV_INT_BY_VAL[*] | 13 | 2 | VLI | I | ||||||
DIV_FLOAT_BY_VAL[*] | 14 | VLF | F | ||||||||
DIV_INT_BY_INT[*] | 47 | VLI | VLI | ||||||||
DIV_FLOAT_BY_FLOAT[*] | 48 | VLF | VLF | ||||||||
> <= IS_THING_GREATER_THAN_THING |
IS_INT_GREATER_THAN_NUMBER[*] | 15 | 2 | VLI | I | ||||||
IS_NUMBER_GREATER_THAN_INT[*] | 16 | I | VLI | ||||||||
IS_INT_GREATER_THAN_INT[*] | 17 | VLI | VLI | ||||||||
IS_FLOAT_GREATER_THAN_NUMBER[*] | 18 | VLF | F | ||||||||
IS_NUMBER_GREATER_THAN_FLOAT[*] | 19 | F | VLF | ||||||||
IS_FLOAT_GREATER_THAN_FLOAT[*] | 20 | VLF | VLF | ||||||||
IS_INT_GREATER_THAN_CONSTANT[*] | 739 | VLI | C | ||||||||
IS_CONSTANT_GREATER_THAN_INT[*] | 740 | C | VLI | ||||||||
>= < IS_THING_GREATER_OR_EQUAL_TO_THING |
IS_INT_GREATER_OR_EQUAL_TO_NUMBER[*] | 21 | 2 | VLI | I | ||||||
IS_NUMBER_GREATER_OR_EQUAL_TO_INT[*] | 22 | I | VLI | ||||||||
IS_INT_GREATER_OR_EQUAL_TO_INT[*] | 23 | VLI | VLI | ||||||||
IS_FLOAT_GREATER_OR_EQUAL_TO_NUMBER[*] | 24 | VLF | F | ||||||||
IS_NUMBER_GREATER_OR_EQUAL_TO_FLOAT[*] | 25 | F | VLF | ||||||||
IS_FLOAT_GREATER_OR_EQUAL_TO_FLOAT[*] | 26 | VLF | VLF | ||||||||
IS_INT_GREATER_OR_EQUAL_TO_CONSTANT[*] | 741 | VLI | C | ||||||||
IS_CONSTANT_GREATER_OR_EQUAL_TO_INT[*] | 742 | C | VLI | ||||||||
= IS_THING_EQUAL_TO_THING |
IS_INT_EQUAL_TO_NUMBER[*] | 27 | 2 | VLI | I | ||||||
IS_INT_EQUAL_TO_INT[*] | 28 | VLI | VLI | ||||||||
IS_FLOAT_EQUAL_TO_NUMBER[*] | 29 | VLF | F | ||||||||
IS_FLOAT_EQUAL_TO_FLOAT[*] | 30 | VLF | VLF | ||||||||
IS_TEXT_LABEL_EQUAL_TO_STRING[*] | 31 | VLT | T | ||||||||
IS_TEXT_LABEL_EQUAL_TO_TEXT_LABEL[*] | 32 | VLT | VLT | ||||||||
IS_INT_EQUAL_TO_CONSTANT[*] | 731 | VLI | C | ||||||||
GOTO_IF_TRUE[*] | 33 | 1 | R | ||||||||
GOTO_IF_FALSE[*] | 34 | 1 | R | ||||||||
TERMINATE_THIS_SCRIPT | 35 | 0 | |||||||||
MISSION_END | |||||||||||
START_NEW_SCRIPT[*] | 36 | 1+l | R | AM | |||||||
GOSUB | 37 | 1 | R | ||||||||
RETURN | 38 | 0 | |||||||||
+=@ +@ ADD_THING_TO_THING_TIMED |
ADD_TIMED_VAL_TO_FLOAT[*] | 49 | 2 | VLF | F | ||||||
ADD_TIMED_FLOAT_TO_FLOAT[*] | 50 | VLF | VLF | ||||||||
-=@ -@ SUB_THING_FROM_THING_TIMED |
SUB_TIMED_VAL_FROM_FLOAT[*] | 51 | 2 | VLF | F | ||||||
SUB_TIMED_FLOAT_FROM_FLOAT[*] | 52 | VLF | VLF | ||||||||
=# CSET |
CSET_INT_TO_FLOAT[*] | 56 | 2 | VLI | VLF | ||||||
CSET_FLOAT_TO_INT[*] | 57 | VLF | VLI | ||||||||
ABS | ABS_INT[*] | 58 | 1 | VLI | |||||||
ABS_FLOAT[*] | 59 | VLF | |||||||||
RETURN_TRUE | 94 | 0 | |||||||||
RETURN_FALSE | 95 | 0 | |||||||||
VAR_INT[*][*] | 96 | n | VI | VIO | |||||||
VAR_FLOAT[*][*] | 97 | n | VF | VFO | |||||||
LVAR_INT[*][*] | 98 | n | LI | LIO | |||||||
LVAR_FLOAT[*][*] | 99 | n | LF | LFO | |||||||
VAR_TEXT_LABEL[*][*] | 100 | n | VT | VTO | |||||||
LVAR_TEXT_LABEL[*][*] | 101 | n | LT | LTO | |||||||
{[*][*] | 102 | 0 | |||||||||
}[*][*] | 103 | 0 | |||||||||
IF[*][*] | 104 | 1 | I | ||||||||
IFNOT[*][*] | 105 | 1 | I | ||||||||
ELSE[*][*] | 106 | 0 | |||||||||
ENDIF[*][*] | 107 | 0 | |||||||||
WHILE[*][*] | 108 | 1 | I | ||||||||
WHILENOT[*][*] | 109 | 1 | I | ||||||||
ENDWHILE[*][*] | 110 | 0 | |||||||||
REPEAT[*][*] | 111 | 2 | I | VLI | |||||||
ENDREPEAT[*][*] | 112 | 0 | |||||||||
SWITCH[*][*] | 113 | 1 | VLI | ||||||||
ENDSWITCH[*][*] | 114 | 0 | |||||||||
CASE[*][*] | 115 | 1 | I | ||||||||
DEFAULT[*][*] | 116 | 0 | |||||||||
BREAK[*][*] | 117 | 0 | |||||||||
ANDOR[*] | 120 | 1 | I | ||||||||
LAUNCH_MISSION | 121 | 1 | R | ||||||||
SAVE_VAR_INT[*] | 297 | l | VI | VIO | |||||||
SAVE_VAR_FLOAT[*] | 298 | l | VF | VFO | |||||||
SAVE_VAR_TEXT_LABEL[*] | 299 | l | VT | VTO | |||||||
GOSUB_FILE | 442 | 2 | R | R | |||||||
PLAYER_MADE_PROGRESS | 479 | 1 | I | ||||||||
SET_PROGRESS_TOTAL | 480 | 1 | I | ||||||||
REGISTER_MISSION_GIVEN | 490 | 0 | |||||||||
REGISTER_MISSION_PASSED | 491 | 1 | T | ||||||||
SCRIPT_NAME | 568 | 1 | T | ||||||||
LOAD_AND_LAUNCH_MISSION | 648 | 1 | R | ||||||||
LOAD_AND_LAUNCH_MISSION_INTERNAL | 649 | 1 | I | ||||||||
SET_TOTAL_NUMBER_OF_MISSIONS | 662 | 1 | I | ||||||||
REGISTER_ODDJOB_MISSION_PASSED | 874 | 0 | |||||||||
CALL[*][*][*] | 890 | 1+i+o | R | AM | |||||||
VLM | |||||||||||
CALLNOT[*][*][*] | 891 | 1+i+o | R | AM | |||||||
VLM | |||||||||||
SET_COLLECTABLE2_TOTAL | 1242 | 1 | I |
- Notes
- ^ A special mission directive which is never compiled;
- ^ It is used to build the various control flows internally;
- ^ It is used to declare one or more variables;
- ^ It embeds a variable scope;
- ^ It has an undefined amount of arguments;
- ^ It is a likely definition of the standard command;
- ^ It should exist but its position is purely guessed;
- ^ Arguments amount varies when compiling.
This section is incomplete. You can help by fixing and expanding it.
Uncommon values
Arguments of some commands keep uncommon values which look familiar after encoding:
Command | Arg. ID |
Value | Encoded | |||
---|---|---|---|---|---|---|
Command | Arg. ID |
Value | Type | |||
GOTO | 1 | Generic label | GOTO | 1 | Positive/Negative offset | INT |
GOTO_IF_FALSE | 1 | Generic label | GOTO_IF_FALSE | 1 | Positive/Negative offset | INT |
GOSUB | 1 | Gosub label | GOSUB | 1 | Positive/Negative offset | INT |
GOSUB_FILE | 1 | Gosub label | GOSUB_FILE | 1 | Zero-based offset | INT |
2 | Foreign gosub file | 2 | ||||
1 | Gosub label | 1 | Positive/Negative offset | |||
2 | Foreign gosub label | 2 | ||||
START_NEW_SCRIPT | 1 | Script label | START_NEW_SCRIPT | 1 | Positive/Negative offset | INT |
l | Passed locals | l | Passed locals | ANY_MULTI | ||
LAUNCH_MISSION | 1 | Subscript file | LAUNCH_MISSION | 1 | Zero-based offset | INT |
Subscript label | Positive/Negative offset | |||||
LOAD_AND_LAUNCH_MISSION | 1 | Mission script file | LOAD_AND_LAUNCH_MISSION_INTERNAL | 1 | Mission identifier | INT |
Mission script label | LOAD_AND_LAUNCH_MISSION | Positive/Negative offset | ||||
GOTO_IF_TRUE | 1 | Generic label | GOTO_IF_TRUE | 1 | Positive/Negative offset | INT |
LOAD_AND_LAUNCH_MISSION_EXCLUSIVE | 1 | Mission script file | LOAD_AND_LAUNCH_MISSION_INTERNAL | 1 | Negative mission identifier | INT |
Mission script label | LOAD_AND_LAUNCH_MISSION_EXCLUSIVE | Positive/Negative offset | ||||
CALL CALLNOT |
1 | Function label | CALL CALLNOT |
1 | # of input arguments | INT |
2 | # of output arguments | |||||
3 | # of script locals | |||||
4 | Positive/Negative offset | |||||
i | Input arguments | i | Input arguments | ANY_MULTI | ||
o | Output arguments | o | Output arguments | HOLD_MULTI |
This section is incomplete. You can help by fixing and expanding it.
Compare flag
The compare flag is an internal script-dependent flag which makes conditional GOTOs such as GOTO_IF_TRUE (unavailable in ) and GOTO_IF_FALSE deciding whether to jump otherwise. It can handle up to 8 checks per conditional statement and indicates you are verifying a single condition (0, see also Optimization) or multiple conditions with either AND (1 to 8) or OR (21 to 28) logical operators (see also ANDOR).
Control flows analysis
As an overview of the compiled source, control flows are literally nested meaning that the code is unoptimized. Furthermore, the jump of an embedded construct doesn't get merged with that of the construct itself, which consists of a benefit for the code parsing.
IF
As regards the IF control flow, if the whole check is true the consequence is performed and the code jumps to the end of the construct, otherwise it skips to the alternative (see also Compare flag):
IFNOT
Not that much to say more than the preceding construct, the IFNOT control flow is built nearly in the same way. In fact, the ELSE clause points to the alternative, whereas the GOTO jumps to its end. The substantial difference consists in the substitution of GOTO_IF_FALSE with GOTO_IF_TRUE:
WHILE
The WHILE control flow is built pretty much similarly to the previous, even though when the consequence is read the code is moved to the beginning of the construct:
WHILENOT
To say the least, WHILENOT control flow follows the same constitution of both WHILE and IFNOT constructs, by exchanging GOTO_IF_FALSE with GOTO_IF_TRUE:
REPEAT
Seemingly, the REPEAT control flow is the first construct ever optimized as a result of a possible R* compiler fault. Moreover, it sounds ambiguous as it loops at least once. This was probably the intention of R* programmers, that is iterating at least once else the construct is useless. However, there are few chance they decide to use such structure to avoid some conflict with some other constructs:
SWITCH
In , the SWITCH control flow is more complex and efficient because the game uses internally a binary search algorithm to jump at the label that matches with the value of a particular case. This method requires a known amount of cases which is up to 75. When a case is true, a consequence is executed and the code jumps to the end of the construct, otherwise the alternative may be performed. As the code is unoptimized, the GOTO of the last case is still compiled even though its label points right after the command mentioned earlier:
In and , such control flow is a set of nested IF constructs which causes a very slight loss of performance by considering that ANDOR isn't compiled:
Optimization
In and , whenever a single condition is checked ANDOR doesn't get compiled cause no logical operator (AND, OR) is used and so they become really useless. Its lack increases the script efficiency a lot. However, the jump of the ELSE clause of an IF statement which points to the end of the construct is still compiled after a GOTO. Furthermore, Stories Games come with an improved data type managing which causes a considerable decrease of the compiled file size.
Tools
- SCRambl - an open-source tool provided by Deji
- Mission Scripting Tools
See also
External links
- DMA's official GTA2script Compiler V9.6, DMA's official GTA2script information - found into the game copy
- GTA III 10th Anniversary source - found into the iOS game copy
- GTA III unofficial main.sc - Reconstruction of main.sc, missing from the iOS source code, by Link2012
- GTA III/VC unavailable script commands (possibly NOP'd) - some sort of information by Wesser as a result of the reverse-engineering applied to the Rockstar Games' official GTA3 Script Compiler V413
- Official GTA3 Script Compiler V413 - found into the GTA VC 10th Anniversary copy
- GTA3 Script Compiler V413 Compilation Strings - a list by Wesser of all the error messages the compiler can throw and more
- GTA SA PC/PS2/XBOX script commands, GTA SA Mobile script commands - taken directly from the Android application and rearranged by Wesser
- GTAForums: Reconstructing the III .sc language – a post by NTAuthority