Basic syntax
Back when ReachVariantTool was made, only Bungie and 343 Industries knew what Megalo truly looked like. However, community reverse-engineers figured out Megalo's bytecode, and this made it possible for members of the public to design their own script languages that compile to the same bytecode. That is what was done for ReachVariantTool. Since then, 343 Industries released MegaloEdit, an official editor for Megalo which uses the same language that Bungie used internally.
The RVT dialect of Megalo is loosely inspired by Lua syntax, though there are quite a few differences such as the use of zero-indexed collections. It is not compatible with the "true" Megalo language used in MegaloEdit.
Literals
Numbers
Numbers can be defined with any of the following syntaxes:
- 12345
- Defines the decimal value 12345.
- 0x3039
- Defines the hexadecimal value 0x3039, which is equal to decimal 12345.
- 0b11000000111001
- Defines a binary value which is equal to decimal 12345.
Strings
Megalo scripts can contain strings. A string is any value that starts and ends with
"
, '
, or `
. The value must start and end with the
same symbol.
Inside of a string, backslashes have special meaning: they can be used to indicate special characters, or to "escape" certain characters from processing.
-
\xHH
- Treats HH as a hexadecimal character code. The entire escape sequence will be replaced with the Unicode character that has that code. You must always provide a two-digit hexadecimal number.
-
\uHHHH
-
Treats HHHH as a hexadecimal character code. The entire escape sequence will be replaced with the Unicode character that has that code. You must always provide a four-digit hexadecimal number.
There are special codes that can be used to show Halo-specific icons and symbols.
-
\"
-
Inserts a double-quote mark into the string. You can use this to include double-quotes
in the text of a string that is itself wrapped in double-quotes, e.g.
"And then he said, \"Hello!\""
-
\'
-
Inserts a single-quote mark into the string. You can use this to include single-quotes
in the text of a string that is itself wrapped in single-quotes, e.g.
'But that\'s not very nice.'
-
\`
-
Inserts a backtick into the string. You can use this to include backticks in the text
of a string that is itself wrapped in backticks, e.g.
`Backticks are often used instead of apostrophes, but I s\`pose that's a bit odd.`
-
\[any other character]
- Just inserts the given character.
-
\\
- Inserts a real backslash into the string.
Statements
Statements in a Megalo script can be divided into three categories: conditions, actions,
and blocks. There is no statement separator character; statements are separated with
whitespace, and the allowed syntax is limited in order to enable this. Expressions are
not supported; for example, the following statement is interpreted as an assignment
(a = b
) followed by an unexpected operator:
a = b + c -- The above is interpreted as two statements: "a = b" and "+ c". -- The supported syntax is: -- a = b -- a += c
Blocks
The following block types are available:
- do ... end
- A generic block.
- enum ... end
- An user-defined enum definition.
- for ... do ... end
- A piece of code which should be executed multiple times in a row, with specific information made available each time. More details here.
- function ... end
- A user-defined function which can be "called," or triggered to run, from different parts of the script. More details here.
- if ... altif ... alt ... end
- A piece of code which should execute if specific conditions are met. More details here.
Top-level blocks (that is, blocks not nested inside of other blocks) can be prefixed with an event name.
Actions
An action is any statement that doesn't appear in an if-block's condition list. Actions can begin with a keyword, or they can take one of the following forms:
function(...)
variable = function(...)
variable = value
You can call user-defined functions or hardcoded "action" functions. You cannot call hardcoded "condition" functions.
Conditions
Conditions are terms in an if-block's condition list. They must take one of the following two forms:
function(...)
value == other_value
Conditions can be prefixed with the word not to invert them. Conditions must be separated from each other with the word or or the word and.
You can call hardcoded "condition" functions here, but not user-defined functions or any hardcoded "action" functions.
Variables
Megalo supports the following variable types:
Variables are either global or nested. There is one "global" set of variables that is accessible at all times, and every object, player, and team is also capable of holding its own set of variables. Variables can be referred to by no more than two levels of depth:
global.player[0] -- valid global.player[0].player[1] -- valid global.player[0].player[1].player[2] -- invalid
In this particular Megalo dialect, you cannot define your own variables; there are a fixed number available. However, you can give names to variables, and variable declarations can be used to set a variable's networking priority and initial value.
Properties
A property is a member of a variable. Properties can appear in any place where a variable of the same type can be used. You cannot access variables or other properties through a property.
current_player.team -- valid current_player.biped.spawn_sequence -- invalid (var.property.property) current_player.team.number[0] -- invalid (var.property.var)
Most properties cannot be accessed through a nested variable. The biped property on player variables is an exception.
current_player.team -- valid (var.property) current_player.player[0].team -- invalid (var.var.property) current_player.biped -- valid (var.biped) current_player.player[0].biped -- valid (var.var.biped)
Most properties do not have indices. There is one exception: the script_stat property on players and teams. There, you must use an index to specify the target stat:
current_player.script_stat[0]
Accessors
An accessor uses similar syntax to a property, but can only appear in assignment statements. Internally, these exist as "getter" and "setter" functions. As such, you can use accessors on variables and properties.
current_object.shields += 50 -- valid; access through variable current_player.biped.shields += 50 -- valid; access through property
Most accessors have both getters and setters. Some do not, which means that they can only appear on the appropriate side of an assignment statement.
current_object.max_shields += 50 -- valid; there is a setter for this global.number[0] = current_object.max_shields -- invalid; there is no getter for this
From a language design standpoint, accessors were preferred over getter/setter
functions in cases where the internal setter takes the same range of assignment
operators as an assignment statement. Although the parser as currently designed could
accept an assignment operator as an argument (e.g. foo.set_bar(+=, 5)
),
this was avoided for consistency with almost every other programming language ever
made.