Skip to content
Raphaël Zumer edited this page Feb 15, 2019 · 22 revisions

Accessing Memory

  • byte(address) - the 8-bit value at the specified address
  • word(address) - the 16-bit value at the specified address
  • dword(address) - the 32-bit value at the specified address
  • bit0(address) - the least significant bit of the specified address
  • bit1(address) - the second least significant bit of the specified address
  • bit2(address) - the third least significant bit of the specified address
  • bit3(address) - the fourth least significant bit of the specified address
  • bit4(address) - the fifth least significant bit of the specified address
  • bit5(address) - the sixth least significant bit of the specified address
  • bit6(address) - the seventh least significant bit of the specified address
  • bit7(address) - the most significant bit of the specified address
  • low4(address) - the four least significant bits of the specified address
  • high4(address) - the four most significant bits of the specified address
  • prev(accessor(address)) - the value of the specified address from the previous frame (accessor is one of the above - i.e. byte)

Arithmetic Operations

  • left + right - adds numerical values or concatenates string values
  • left - right - subtracts the second value from the first value
  • left * right - multiplies numerical values
  • left / right - divides the first value by the second value, the remainder is discarded
  • left % right - calculates the remainder when the first value is divided by the second value

Comparisons

  • left == right - the two values are equal
  • left != right - the two values are not equal
  • left > right - the first value is greater than the second value
  • left >= right - the first value is greater than or equal to the second value
  • left < right - the first value is less than the second value
  • left <= right - the first value is less than or equal to the second value

left must be a memory address or a function that evaluates to a memory address

right may be a memory address, a constant, or a function that evaluates to a memory address or constant

Logical Comparisons

  • left && right - both conditions must be true
  • left || right - either condition must be true
  • !left - the condition must be false

left and right must be comparisons or functions that evaluate to comparisons

Order of Operations

Operators are evaluated in this order (within a level, operators are evaluated left to right as they're read from the expression):

  • Parentheses
  • Multiplication, division, and modulus
  • Addition and subtraction
  • Comparisons
  • Not
  • And
  • Or

The following statement: N + M * A < B && C > X || D / G * H > Y + Z

Would be interpreted as: (((N + (M * A)) < B) && (C > X)) || (((D / G) * H) > (Y + Z))

It is particularly important to understand how this affects the creation of alt groups:

A == N && B == X || B == Y

Because AND has higher precedence than OR, the interpreter sees:

(A == N && B == X) || (B == Y)

Which is two alt groups and no core group. An achievement must have a core group, so this will generate a parsing error. You can use parentheses to force a different order. What was intended was:

A == N && (B == X || B == Y)

Here, A == N becomes the core group and there are two alt groups: B == X and B == Y.

Variables

  • name = value

name must start with a letter or underscore and may contain additional letters, numbers or underscores.

value may be a numerical constant, string constant, or expression. The expression will not be evaluated until the variable is used, which allows variables to be assigned to function calls, memory accessors, or logical comparisons to be evaluated later.

Variables allow storage of values for for later use. They may be assigned constant or calculated values. Variables within a function (including function parameters) are only accessible within the function. Variables outside a function may be used inside or outside of functions. Variables may not be referenced before they are defined.

Arrays

  • name = [ value1, value2 ]

An array is a variable that stores multiple values. Arrays are accessed using square brackets. Indices are zero-based, so to access the first item of the array, use name[0].

Dictionaries

  • name = { key: value, key: value }

A dictionary is a special type of variable that maps integer values to other values - typically string constants. key may be a numerical constant or a variable or function call that evaluates to a numerical constant.

Dictionaries can be accessed or modified using square brackets: value = name[key]. name[key] = value

Functions

  • function name(parameters) { code }

A function is a reusable piece of code that may have slightly different behavior controlled by parameters that are passed to the function.

A function may return anything that can be assigned to a variable (numerical or string constants, or an expression to be evaluated later)

This shorthand defines a function that just returns the specified expression.

  • function name(parameters) => expression

Functions are called by using the function name in an expression. Parameters may be passed by index or key.

  • variable = name(parameter1, parameter2)
  • variable = name(p1 = parameter1, p2 = parameter2)

Loops

  • for key in dictionary { code }

Executes the specified code for each key in the provided dictionary.

  • for item in array { code }

Executes the specified code for each item in the provided array.

  • for index in range(start, stop, [step]) { code }

Executes the specified code for each integer in the inclusive range between start and stop. If step is not specified, it's value is 1. range(1, 5) is equivalent to [1,2,3,4,5] and range(1, 5, 2) is equivalent to [1,3,5].

If/Else

  • if (condition) { code }
  • if (condition) { code } else { code }

Allows arbitrary execution of code. Typically used inside functions or loops to change behavior based on parameter or loop index values.

Comments

  • // This is a comment

Anytime the interpreter finds a double slash, the rest of the current line is ignored.

Special functions

  • always_true()

Defines the clause "1==1". Typically only used to move a PauseIf/ResetIf to an alt group:

byte(0x1234) == 8 && (always_true() || (never(byte(0x2345) == 12) && unless(byte(0x3456) == 6)))

This allows the achievement (core group) to trigger while the never is paused by the unless.

  • always_false()

Defines the clause "0==1". Typically used for constructing alt chains:

trigger = always_false()
for test in tests
    trigger = trigger || test
achievement(..., trigger = trigger)

If more than two alt groups exist, the always_false group will be removed from the final achievement code, but you can't OR conditions together without a starting condition.

Defining Achievements

  • achievement(title, description, points, trigger)

Defines a new achievement with the specified title (string), description (string), points (integer), and trigger. trigger is an expression with one or more comparisons.

function current_board() => byte(0x0088)
function current_player() => byte(0x008C)
function trigger_win_game() => current_board() == 0x8B && current_player() == 0
achievement(
    title = "Score!", description = "Win a game with at least 600 points", points = 25,
    trigger = trigger_win_game() && byte(0x0573) >= 0xF6 // 0x0573 is hundreds place of score
)

First, a couple of helper functions are defined to make the code easier to read. Because all functions are inlined and evaluated on use, this expands to:

trigger = byte(0x0088) == 0x8B && byte(0x008C) == 0 && byte(0x0573) >= 0xF6

Achievement triggers support four special helper functions:

  • repeated(count, comparison) - adds a HitCount to the condition - the specified comparison must be true for count frames to trigger the achievement
  • once(comparison) - shorthand for repeated(1, comparison) - the specified comparison must have been true at one point, but is not required to currently be true to trigger the achievement.
  • never(comparison) - this becomes a ResetIf - if the comparison is ever true, any HitCounts in the achievement are reset to 0.
  • unless(comparison) - this becomes a PauseIf - the achievement is not processed while this condition is true.

comparison does not support logical operators, except in the case of OR with repeated and once, for which the result is a chain of AddHits conditions with the specified HitCount appearing on the final condition.

Defining Rich Presence

  • rich_presence_display(format_string, parameters...)

Defines the Rich Presence script. Only one string may be defined per script - if called multiple times, the last one will win.

format_string is a string with zero or more placeholders that will be evaluated by the emulator at runtime. For each placeholder a parameter must be defined using the rich_presence_ helper functions:

  • rich_presence_value(name, expression, format)
  • rich_presence_lookup(name, expression, dictionary)

name is the name to associate to the placeholder.

expression is a memory accessor, arithmetic expression, or a function that evaluates to a memory access or arithmetic expression.

dictionary is the key to value map used to convert the result of expression into a string.

format is one of the following:

  • VALUE - number (default)
  • SECS - the value is a number of seconds that should be formatted as MM:SS
  • FRAMES - the value is a number of frames that should be converted to seconds and displayed as MM:SS
  • POINTS - the value should be displayed as a six digit score value followed by the word 'POINTS'
  • MILLISECS - the value is a number of hundredths of a second and should be displays as MM:SS.FFF
function lives() => byte(0x05D4) + 1
function stage() => byte(0x003A)
stages = { 1: "Downtown", 2: "Sewers" }
rich_presence_display("{0}, {1} lives",
    rich_presence_lookup("Stage", stage(), stages),
    rich_presence_value("Lives", lives())
)

Conditional Rich Presence

  • rich_presence_conditional_display(condition, format_string, parameters...)

Defines a conditional Rich Presence display string. When executing the Rich Presence script, each condition is examined in order. If the condition is matched, that display string will be used. If no conditions are matched, the default display string will be used. You must still provide a default display string by calling rich_presence_display.

Has the same structure as rich_presence_display with the additional condition parameter. condition must evaluate to one or more conditions.

rich_presence_conditional_display(is_title_screen(), "Title Screen")
rich_presence_display("Playing Battle {0} in {1}", 
    rich_presence_value("Battle", current_level()),
    rich_presence_lookup("Landscape", current_landscape(), landscapes)
)

Defining Leaderboards

  • leaderboard(title, description, start, cancel, submit, value, format)

Defines a Leaderboard script. title and description must be strings.

start, cancel, and submit are trigger expressions similar to the achievement's trigger parameter, except OR conditions (||) are not supported.

value is a memory accessor, arithmetic expression, or a function that evaluates to a memory access or arithmetic expression.

format is one of the following:

  • VALUE - number (default)
  • SECS - the value is a number of seconds that should be formatted as MM:SS
  • FRAMES - the value is a number of frames that should be converted to seconds and displayed as MM:SS
  • POINTS - the value should be displayed as a six digit score value followed by the word 'POINTS'
  • MILLISECS - the value is a number of hundredths of a second and should be displays as MM:SS.FFF