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:

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:

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.