# RoomleScript Overview

# What is RoomleScript?

RoomleScript is our proprietary language, which is most similar to JavaScript. In the Component object, which you define with the Json component files, the values of the objects' attributes are either constants or RoomleScripts.

To tell, which attribute value is available in RoomleScript, please refer to Roomle Configuration Catalog Structure Format, chapter "JSON Objects". Wherever there is Script<type>, you can use either a constant of that type, or a RoomleScript, which returns the value of that type.

# Roomle Script Basic overview

# Internal Variables

Variables do not have a keyword to declare them. Once a variable is first assigned, it is initialized and declared as well. The data type is assigned automatically, just like in JavaScript. There are following data types available:

  • Boolean
  • Integer
  • Float
  • String (enclosed in 'single quotes')
  • Array of Float

Example:

booleanVariable = true; /* or false, also 1 and 0 work implicitly */
anInteger = 64;
aDecimalOrFloatValue = 3.141;
stringText = 'hello';
anArray = [131.05, 314, -16.64];

It is the responsibility of the scripter, that all used variables are initialized by the time they are used. This is especially important to test during reloading docked configurations, because variables can be initialized from other components as well. In order to be sure that all variables are intialized, we've defined as best practice to use following construct at the beginning of the onUpdate script:

if (ifnull(inited, false) == false) {
    inited = true;
    variablesOrConstants = 564;
    you = false;
    wantTo = 33423.5423;
    initialize = 'empty';
}

The ifnull function returns the false fallback value if the inited variable is not initialized (is null), which it is at the beginning of the component instance live. Setting inited = true makes sure that the inside of the block won't run more than once.

# Comments

There 3 ways commenting inside RoomleScripts:

// single line comment
# equal single line comment
/* multi
line
comment
*/

Attention: using single line comments breaks "Load into Kernel" functionality, therefore it is recommended to use the /* multi line */ style comments. Even the Roomle Component Formatter will convert // style comments to:

/* single line comment */ 

# Operators and Expressions

operator use note
+ addition
- subtraction
* multiplication
/ division* modulo: see function fmod(float):float
| string concatenation
&& logical AND
|| logical OR
== equation / is equal
!= is not equal
= assignment
! negation use extensive brackets (!always)

To write an expression inside RoomleScript:

totalHeight = ((hasPlinth) * plinthHeight) + countOfShelves * 400 + (countOfShelves - 1) * woodThickness;

Example of expressions in the Component definition:

{
    ...
    "parameters": [
        ...
        {
            "key": "width",
            "type": "Decimal",
            "defaultValue": 800,
            "valueObjects": [
                {
                    "value": "800",
                    "condition": "depth < 600"
                },
                {
                    "value": "1200",
                    "condition": true
                },
                {
                    "value": "1600",
                    "condition": "condition = depth == 800;"
                }
            ],
            "enabled": true,
            "visible": "height > 400",
            "visibleInPartList": "height > 400"
        },
        ...
    ],
    "articleNr": "'500.' | widthCode | '.' | materialCode",
    ...
}

# Branching

Currently, RoomleScript has only if and if-else blocks. Braces are mandatory. At the moment, no elsif is possible and must be replaced with another nested if-block. Example follows:

if (booleanExpression) {
    ...
} else {
    if (anotherBooleanExpression) {
        ...
    } else {
        ...
    }
}

if (someValue == anotherValue && (!negatedValue)) {
    ...
}

# Cycle

For-cycle is available within RoomleScript, syntax is identical with JavaScript:

for (i = 0; i < loopsCount; i++) {
    ...
}

Break keyword is not available. For a workaround you can do the following:

continue = true;
for (i = 0; i < loopsCount && continue; i = i + 1) { 
    ...
    /* eventually: */
    continue = false;
    ...
}

# Context

Default context of any internal variable is the whole current component. There are a few contexts you can utilize in certain situations:

prefix scope note
none current component
_. local in current script variable resets to null after script finishes
parameter. parameter.onValueChange script; provides userTriggeredChange
other. assignmentScripts; other component in the connection access to all parameters and internal vars of the component on the other side
self. assignmentScripts; current component in the connection actually is redundant, but helps to understand the code unambigously and should be used whenever you also use other.
connection. assignmentScripts; provides index, position, isPreview There are two connections in one docking/sibling point dock pair, one on each side. You can store variables relevant to the connection. You can not access other component's connection.

# Available functions

For reference on available functions, see Roomle Configuration Catalog Structure Format, chapter RoomleScript.

# Internal values

Some scripts have internal values defined. See the table and their purpose:

identifier context purpose
articleNr articleNr script sets the current component's article number
number subComponent.numberInPartList sets the subcomponent's count of entries in the partlist
connection.isPreview docking condition read-only, returns true if in docking preview state
connection.index docking range returns index of the docking point in the range array
connection.position docking range, line returns Vector3f position of the child relative to the parent's coordinate system
condition all conditions sets the result of the condition

# Custom functions

At the moment, you can not define functions. However, for repetitive code between components, in some cases you can use a subcomponent with assignment and superseding as a workaround.

# Arrays

RoomleScript supports arrays of floats. To initialize the array:

array = [0, 0, 0, 0];
emptyArray = [0];
for (_.i = 1; _.i < arrayLength; _.i++) {
    pushback(emptyArray, 0);
}

To access values in the array, get(array, index) and set(array, index, value) functions are used (VS Code snippets: getValueFromArray, setValueInArray)

value = get(array, 3);
set(array, 3, 32.146);

Note: RoomleSript does not support accessing values using index in square brackets, like array[5].